CAL refactoring
[osm/SO.git] / rwcal / plugins / vala / rwcal_openstack / rift / rwcal / openstack / glance / glance_drv.py
1 #!/usr/bin/python
2
3 #
4 # Copyright 2017 RIFT.IO Inc
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain 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,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18 import logging
19 from glanceclient import client as glclient
20 import glanceclient.exc as GlanceException
21 import time
22
23
24
25 class GlanceAPIVersionException(Exception):
26 def __init__(self, errors):
27 self.errors = errors
28 super(GlanceAPIVersionException, self).__init__("Multiple Exception Received")
29
30 def __str__(self):
31 return self.__repr__()
32
33 def __repr__(self):
34 msg = "{} : Following Exception(s) have occured during Neutron API discovery".format(self.__class__)
35 for n,e in enumerate(self.errors):
36 msg += "\n"
37 msg += " {}: {}".format(n, str(e))
38 return msg
39
40 class GlanceDriver(object):
41 """
42 GlanceDriver Class for image management
43 """
44 ### List of supported API versions in prioritized order
45 supported_versions = ["2"]
46
47 def __init__(self,
48 sess_handle,
49 region_name = 'RegionOne',
50 service_type = 'image',
51 logger = None):
52 """
53 Constructor for GlanceDriver class
54 Arguments:
55 sess_handle (instance of class SessionDriver)
56 region_name (string ): Region name
57 service_type(string) : Service type name
58 logger (instance of logging.Logger)
59 """
60 self._sess_handle = sess_handle
61
62 if logger is None:
63 self.log = logging.getLogger('rwcal.openstack.glance')
64 self.log.setLevel(logging.DEBUG)
65 else:
66 self.log = logger
67
68
69 #### Attempt to use API versions in prioritized order defined in
70 #### GlanceDriver.supported_versions
71 def select_version(version):
72 try:
73 self.log.info("Attempting to use Glance v%s APIs", version)
74 gldrv = glclient.Client(version = version,
75 region_name = region_name,
76 service_type = service_type,
77 session=self._sess_handle.session)
78 except Exception as e:
79 self.log.info(str(e))
80 raise
81 else:
82 self.log.info("Glance API v%s selected", version)
83 return (version, gldrv)
84
85 errors = []
86 for v in GlanceDriver.supported_versions:
87 try:
88 (self._version, self._gl_drv) = select_version(v)
89 except Exception as e:
90 errors.append(e)
91 else:
92 break
93 else:
94 raise GlanceAPIVersionException(errors)
95
96 @property
97 def glance_endpoint(self):
98 return self._gl_drv.http_client.get_endpoint()
99
100 @property
101 def project_id(self):
102 return self._sess_handle.project_id
103
104 def _get_glance_connection(self):
105 """
106 Returns instance of object glanceclient.client.Client
107 Use for DEBUG ONLY
108 """
109 return self._gl_drv
110
111 def image_list(self):
112 """
113 Returns list of dictionaries. Each dictionary contains attributes associated with
114 image
115
116 Arguments: None
117
118 Returns: List of dictionaries.
119 """
120 images = []
121 try:
122 image_info = self._gl_drv.images.list()
123 except Exception as e:
124 self.log.exception("List Image operation failed. Exception: %s", str(e))
125 raise
126 images = [ img for img in image_info ]
127 return images
128
129 def image_create(self, **kwargs):
130 """
131 Creates an image
132 Arguments:
133 A dictionary of kwargs with following keys
134 {
135 'name'(string) : Name of the image
136 'location'(string) : URL (http://....) where image is located
137 'disk_format'(string) : Disk format
138 Possible values are 'ami', 'ari', 'aki', 'vhd', 'vmdk', 'raw', 'qcow2', 'vdi', 'iso'
139 'container_format'(string): Container format
140 Possible values are 'ami', 'ari', 'aki', 'bare', 'ovf'
141 'tags' : A list of user tags
142 'checksum' : The image md5 checksum
143 }
144 Returns:
145 image_id (string) : UUID of the image
146
147 """
148 try:
149 image = self._gl_drv.images.create(**kwargs)
150 except Exception as e:
151 self.log.exception("Create Image operation failed. Exception: %s", str(e))
152 raise
153
154 return image.id
155
156 def image_upload(self, image_id, fd):
157 """
158 Upload the image
159
160 Arguments:
161 image_id: UUID of the image
162 fd : File descriptor for the image file
163 Returns: None
164 """
165 try:
166 self._gl_drv.images.upload(image_id, fd)
167 except Exception as e:
168 self.log.exception("Image upload operation failed. Exception: %s",str(e))
169 raise
170
171 def image_add_location(self, image_id, location, metadata):
172 """
173 Add image URL location
174
175 Arguments:
176 image_id : UUID of the image
177 location : http URL for the image
178
179 Returns: None
180 """
181 try:
182 self._gl_drv.images.add_location(image_id, location, metadata)
183 except Exception as e:
184 self.log.exception("Image location add operation failed. Exception: %s",str(e))
185 raise
186
187 def image_update(self, image_id, remove_props = None, **kwargs):
188 """
189 Update an image
190
191 Arguments:
192 image_id: UUID of the image
193 remove_props: list of property names to remove
194 [
195 'my_custom_property1',
196 'my_custom_property2'
197 ]
198 kwargs: A dctionary of kwargs with the image attributes and their new values
199 {
200 'my_custom_property'(name of property) : Value of the custom property
201 }
202
203 If remove_props is not None, it is assumed that the function is called to
204 remove the specified property from the image, and kwargs is None.
205 Otherwise, the image properties are updated with kwargs. Its either-or.
206 """
207 assert image_id == self._image_get(image_id)['id']
208 try:
209 if remove_props is not None:
210 self._gl_drv.images.update(image_id, remove_props=remove_props)
211 else:
212 self._gl_drv.images.update(image_id, **kwargs)
213 except Exception as e:
214 self.log.exception("Update Image operation failed for image_id : %s. Exception: %s",image_id, str(e))
215 raise
216
217 def image_delete(self, image_id):
218 """
219 Delete an image
220
221 Arguments:
222 image_id: UUID of the image
223
224 Returns: None
225
226 """
227 assert image_id == self._image_get(image_id)['id']
228 try:
229 self._gl_drv.images.delete(image_id)
230 except Exception as e:
231 self.log.exception("Delete Image operation failed for image_id : %s. Exception: %s",image_id, str(e))
232 raise
233
234
235 def _image_get(self, image_id):
236 """
237 Returns a dictionary object of VM image attributes
238
239 Arguments:
240 image_id (string): UUID of the image
241
242 Returns:
243 A dictionary of the image attributes
244 """
245 max_retry = 5
246 try:
247 image = self._gl_drv.images.get(image_id)
248 except GlanceException.HTTPBadRequest as e:
249 # RIFT-14241: The get image request occasionally returns the below message. Retry in case of bad request exception.
250 # Error code 400.: Message: Bad request syntax ('0').: Error code explanation: 400 = Bad request syntax or unsupported method. (HTTP 400)
251 self.log.warning("Got bad request response during get_image request. Retrying.")
252 if max_retry > 0:
253 max_retry -= 1
254 time.sleep(2)
255 image = self._gl_drv.images.get(image_id)
256 else:
257 self.log.exception("Get Image operation failed for image_id : %s. Exception: %s", image_id, str(e))
258 raise
259 except Exception as e:
260 self.log.exception("Get Image operation failed for image_id : %s. Exception: %s", image_id, str(e))
261 raise
262
263 return image
264
265 def image_get(self, image_id):
266 """
267 Returns a dictionary object of VM image attributes
268
269 Arguments:
270 image_id (string): UUID of the image
271
272 Returns:
273 A dictionary of the image attributes
274 """
275 return self._image_get(image_id)
276
277 def image_verify(self, image_id):
278 """
279 Verifies if image with image-id exists and is in active state
280
281 Arguments:
282 image_id(string): UUID of the image
283
284 Returns:
285 None
286 Raises except if image not found or not in active state
287 """
288 img = self.image_get(image_id)
289 if img['status'] != 'active':
290 raise GlanceException.NotFound("Image with image_id: %s not found in active state. Current State: %s"
291 %(img['id'], img['status']))
292