Auto Generated INFO.yaml file
[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                   region_name=os_creds.region_name)
45
46
47 def get_image(glance, image_name=None, image_settings=None):
48     """
49     Returns an OpenStack image object for a given name
50     :param glance: the Glance client
51     :param image_name: the image name to lookup
52     :param image_settings: the image settings used for lookups
53     :return: the image object or None
54     """
55     img_filter = dict()
56     if image_settings:
57         if image_settings.exists:
58             img_filter = {'name': image_settings.name}
59         else:
60             img_filter = {'name': image_settings.name,
61                           'disk_format': image_settings.format}
62     elif image_name:
63         img_filter = {'name': image_name}
64
65     images = glance.images.list(**{'filters': img_filter})
66     for image in images:
67         if glance.version == VERSION_1:
68             image = glance.images.get(image.id)
69             return Image(name=image.name, image_id=image.id,
70                          size=image.size, properties=image.properties)
71         elif glance.version == VERSION_2:
72             return Image(
73                 name=image['name'], image_id=image['id'],
74                 size=image['size'], properties=image.get('properties'))
75
76
77 def get_image_by_id(glance, image_id):
78     """
79     Returns an OpenStack image object for a given name
80     :param glance: the Glance client
81     :param image_id: the image ID to lookup
82     :return: the SNAPS-OO Domain Image object or None
83     """
84     image = glance.images.get(image_id)
85     return Image(
86         name=image['name'], image_id=image['id'],
87         size=image['size'], properties=image.get('properties'))
88
89
90 def get_image_status(glance, image):
91     """
92     Returns a new OpenStack Image object for a given OpenStack image object
93     :param glance: the Glance client
94     :param image: the domain Image object
95     :return: the OpenStack Image object
96     """
97     if glance.version == VERSION_1:
98         os_image = glance.images.get(image.id)
99         return os_image.status
100     elif glance.version == VERSION_2:
101         os_image = glance.images.get(image.id)
102         return os_image['status']
103     else:
104         raise GlanceException('Unsupported glance client version')
105
106
107 def create_image(glance, image_settings):
108     """
109     Creates and returns OpenStack image object with an external URL
110     :param glance: the glance client
111     :param image_settings: the image settings object
112     :return: the OpenStack image object
113     :raise Exception if using a file and it cannot be found
114     """
115     if glance.version == VERSION_1:
116         return __create_image_v1(glance, image_settings)
117     elif glance.version == VERSION_2:
118         return __create_image_v2(glance, image_settings)
119     else:
120         raise GlanceException('Unsupported glance client version')
121
122
123 def __create_image_v1(glance, image_settings):
124     """
125     Creates and returns OpenStack image object with an external URL
126     :param glance: the glance client
127     :param image_settings: the image settings object
128     :return: the OpenStack image object
129     :raise exceptions from the Glance client or IOError when opening a file
130     """
131     kwargs = {
132         'name': image_settings.name, 'disk_format': image_settings.format,
133         'container_format': 'bare', 'is_public': image_settings.public}
134
135     image_file = None
136
137     try:
138         if image_settings.extra_properties:
139             kwargs['properties'] = image_settings.extra_properties
140
141         if image_settings.url:
142             kwargs['location'] = image_settings.url
143         elif image_settings.image_file:
144             image_file = open(image_settings.image_file, 'rb')
145             kwargs['data'] = image_file
146         else:
147             logger.warn(
148                 'Unable to create image with name - %s. No file or URL',
149                 image_settings.name)
150             return None
151
152         created_image = glance.images.create(**kwargs)
153         return Image(name=image_settings.name, image_id=created_image.id,
154                      size=created_image.size,
155                      properties=created_image.properties)
156     finally:
157         if image_file:
158             image_file.close()
159
160
161 def __create_image_v2(glance, image_settings):
162     """
163     Creates and returns OpenStack image object with an external URL
164     :param glance: the glance client v2
165     :param image_settings: the image settings object
166     :return: the OpenStack image object
167     :raise GlanceException or IOException or URLError
168     """
169     cleanup_temp_file = False
170     image_file = None
171     if image_settings.image_file is not None:
172         image_filename = image_settings.image_file
173     elif image_settings.url:
174         file_name = str(uuid.uuid4())
175         try:
176             image_file = file_utils.download(
177                 image_settings.url, './tmp', file_name)
178             image_filename = image_file.name
179         except:
180             if image_file:
181                 os.remove(image_file.name)
182             raise
183
184         cleanup_temp_file = True
185     else:
186         raise GlanceException('Filename or URL of image not configured')
187
188     os_image = None
189     try:
190         kwargs = dict()
191         kwargs['name'] = image_settings.name
192         kwargs['disk_format'] = image_settings.format
193         kwargs['container_format'] = 'bare'
194
195         if image_settings.public:
196             kwargs['visibility'] = 'public'
197
198         if image_settings.extra_properties:
199             kwargs.update(image_settings.extra_properties)
200
201         os_image = glance.images.create(**kwargs)
202         image_file = open(os.path.expanduser(image_filename), 'rb')
203         glance.images.upload(os_image['id'], image_file)
204     except:
205         logger.error('Unexpected exception creating image. Rolling back')
206         if os_image:
207             delete_image(glance, Image(
208                 name=os_image['name'], image_id=os_image['id'],
209                 size=os_image['size'], properties=os_image.get('properties')))
210         raise
211     finally:
212         if image_file:
213             logger.debug('Closing file %s', image_file.name)
214             image_file.close()
215         if cleanup_temp_file:
216             logger.info('Removing file %s', image_file.name)
217             os.remove(image_filename)
218
219     return get_image_by_id(glance, os_image['id'])
220
221
222 def delete_image(glance, image):
223     """
224     Deletes an image from OpenStack
225     :param glance: the glance client
226     :param image: the image to delete
227     """
228     logger.info('Deleting image named - %s', image.name)
229     glance.images.delete(image.id)
230
231
232 class GlanceException(Exception):
233     """
234     Exception when calls to the Glance client cannot be served properly
235     """