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