Merge "Created new class AnsibleException."
[snaps.git] / snaps / openstack / utils / glance_utils.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain 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,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 import logging
16 import os
17 import uuid
18
19 from snaps import file_utils
20 from glanceclient.client import Client
21
22 from snaps.domain.image import Image
23 from snaps.openstack.utils import keystone_utils
24
25 __author__ = 'spisarski'
26
27 logger = logging.getLogger('glance_utils')
28
29 VERSION_1 = 1.0
30 VERSION_2 = 2.0
31
32 """
33 Utilities for basic neutron API calls
34 """
35
36
37 def glance_client(os_creds):
38     """
39     Creates and returns a glance client object
40     :return: the glance client
41     """
42     return Client(version=os_creds.image_api_version,
43                   session=keystone_utils.keystone_session(os_creds))
44
45
46 def get_image(glance, image_name=None):
47     """
48     Returns an OpenStack image object for a given name
49     :param glance: the Glance client
50     :param image_name: the image name to lookup
51     :return: the image object or None
52     """
53     images = glance.images.list()
54     for image in images:
55         if glance.version == VERSION_1:
56             if image.name == image_name:
57                 image = glance.images.get(image.id)
58                 return Image(name=image.name, image_id=image.id,
59                              size=image.size, properties=image.properties)
60         elif glance.version == VERSION_2:
61             if image['name'] == image_name:
62                 return Image(
63                     name=image['name'], image_id=image['id'],
64                     size=image['size'], properties=image.get('properties'))
65     return None
66
67
68 def get_image_status(glance, image):
69     """
70     Returns a new OpenStack Image object for a given OpenStack image object
71     :param glance: the Glance client
72     :param image: the domain Image object
73     :return: the OpenStack Image object
74     """
75     if glance.version == VERSION_1:
76         os_image = glance.images.get(image.id)
77         return os_image.status
78     elif glance.version == VERSION_2:
79         os_image = glance.images.get(image.id)
80         return os_image['status']
81     else:
82         raise GlanceException('Unsupported glance client version')
83
84
85 def create_image(glance, image_settings):
86     """
87     Creates and returns OpenStack image object with an external URL
88     :param glance: the glance client
89     :param image_settings: the image settings object
90     :return: the OpenStack image object
91     :raise Exception if using a file and it cannot be found
92     """
93     if glance.version == VERSION_1:
94         return __create_image_v1(glance, image_settings)
95     elif glance.version == VERSION_2:
96         return __create_image_v2(glance, image_settings)
97     else:
98         raise GlanceException('Unsupported glance client version')
99
100
101 def __create_image_v1(glance, image_settings):
102     """
103     Creates and returns OpenStack image object with an external URL
104     :param glance: the glance client
105     :param image_settings: the image settings object
106     :return: the OpenStack image object
107     :raise exceptions from the Glance client or IOError when opening a file
108     """
109     created_image = None
110
111     # TODO/REFACTOR - replace each call with one including kwargs
112     if image_settings.url:
113         if image_settings.extra_properties:
114             created_image = glance.images.create(
115                 name=image_settings.name, disk_format=image_settings.format,
116                 container_format="bare", location=image_settings.url,
117                 properties=image_settings.extra_properties,
118                 is_public=image_settings.public)
119         else:
120             created_image = glance.images.create(
121                 name=image_settings.name, disk_format=image_settings.format,
122                 container_format="bare", location=image_settings.url,
123                 is_public=image_settings.public)
124     elif image_settings.image_file:
125         image_file = open(image_settings.image_file, 'rb')
126         if image_settings.extra_properties:
127             created_image = glance.images.create(
128                 name=image_settings.name, disk_format=image_settings.format,
129                 container_format="bare", data=image_file,
130                 properties=image_settings.extra_properties,
131                 is_public=image_settings.public)
132         else:
133             created_image = glance.images.create(
134                 name=image_settings.name, disk_format=image_settings.format,
135                 container_format="bare", data=image_file,
136                 is_public=image_settings.public)
137
138     return Image(name=image_settings.name, image_id=created_image.id,
139                  size=created_image.size, properties=created_image.properties)
140
141
142 def __create_image_v2(glance, image_settings):
143     """
144     Creates and returns OpenStack image object with an external URL
145     :param glance: the glance client v2
146     :param image_settings: the image settings object
147     :return: the OpenStack image object
148     :raise GlanceException or IOException or URLError
149     """
150     cleanup_temp_file = False
151     image_file = None
152     if image_settings.image_file:
153         image_filename = image_settings.image_file
154     elif image_settings.url:
155         file_name = str(uuid.uuid4())
156         try:
157             image_file = file_utils.download(
158                 image_settings.url, './tmp', file_name)
159             image_filename = image_file.name
160         except:
161             os.remove('./tmp/' + file_name)
162             raise
163
164         cleanup_temp_file = True
165     else:
166         raise GlanceException('Filename or URL of image not configured')
167
168     created_image = None
169     try:
170         kwargs = dict()
171         kwargs['name'] = image_settings.name
172         kwargs['disk_format'] = image_settings.format
173         kwargs['container_format'] = 'bare'
174
175         if image_settings.public:
176             kwargs['visibility'] = 'public'
177
178         if image_settings.extra_properties:
179             kwargs.update(image_settings.extra_properties)
180
181         created_image = glance.images.create(**kwargs)
182         image_file = open(image_filename, 'rb')
183         glance.images.upload(created_image['id'], image_file)
184     except:
185         logger.error('Unexpected exception creating image. Rolling back')
186         if created_image:
187             delete_image(glance, created_image)
188         raise
189     finally:
190         if image_file:
191             image_file.close()
192         if cleanup_temp_file:
193             os.remove(image_filename)
194
195     updated_image = glance.images.get(created_image['id'])
196     return Image(
197         name=updated_image['name'], image_id=updated_image['id'],
198         size=updated_image['size'], properties=updated_image.get('properties'))
199
200
201 def delete_image(glance, image):
202     """
203     Deletes an image from OpenStack
204     :param glance: the glance client
205     :param image: the image to delete
206     """
207     if glance.version == VERSION_1:
208         glance.images.delete(image)
209     elif glance.version == VERSION_2:
210         glance.images.delete(image.id)
211     else:
212         raise GlanceException('Unsupported glance client version')
213
214
215 class GlanceException(Exception):
216     """
217     Exception when calls to the Glance client cannot be served properly
218     """