f4b446646e81a551814e935c1d0abe373734daac
[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, session=keystone_utils.keystone_session(os_creds))
43
44
45 def get_image(glance, image_name=None):
46     """
47     Returns an OpenStack image object for a given name
48     :param glance: the Glance client
49     :param image_name: the image name to lookup
50     :return: the image object or None
51     """
52     images = glance.images.list()
53     for image in images:
54         if glance.version == VERSION_1:
55             if image.name == image_name:
56                 image = glance.images.get(image.id)
57                 return Image(name=image.name, image_id=image.id, size=image.size, properties=image.properties)
58         elif glance.version == VERSION_2:
59             if image['name'] == image_name:
60                 return Image(name=image['name'], image_id=image['id'], size=image['size'],
61                              properties=image.get('properties'))
62     return None
63
64
65 def get_image_status(glance, image):
66     """
67     Returns a new OpenStack Image object for a given OpenStack image object
68     :param glance: the Glance client
69     :param image: the domain Image object
70     :return: the OpenStack Image object
71     """
72     if glance.version == VERSION_1:
73         os_image = glance.images.get(image.id)
74         return os_image.status
75     elif glance.version == VERSION_2:
76         os_image = glance.images.get(image.id)
77         return os_image['status']
78     else:
79         raise Exception('Unsupported glance client version')
80
81
82 def create_image(glance, image_settings):
83     """
84     Creates and returns OpenStack image object with an external URL
85     :param glance: the glance client
86     :param image_settings: the image settings object
87     :return: the OpenStack image object
88     :raise Exception if using a file and it cannot be found
89     """
90     if glance.version == VERSION_1:
91         return __create_image_v1(glance, image_settings)
92     elif glance.version == VERSION_2:
93         return __create_image_v2(glance, image_settings)
94     else:
95         raise Exception('Unsupported glance client version')
96
97
98 def __create_image_v1(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     created_image = None
107
108     # TODO/REFACTOR - replace each call with one including kwargs
109     if image_settings.url:
110         if image_settings.extra_properties:
111             created_image = glance.images.create(
112                 name=image_settings.name, disk_format=image_settings.format, container_format="bare",
113                 location=image_settings.url, properties=image_settings.extra_properties,
114                 is_public=image_settings.public)
115         else:
116             created_image = glance.images.create(name=image_settings.name, disk_format=image_settings.format,
117                                                  container_format="bare", location=image_settings.url,
118                                                  is_public=image_settings.public)
119     elif image_settings.image_file:
120         image_file = open(image_settings.image_file, 'rb')
121         if image_settings.extra_properties:
122             created_image = glance.images.create(
123                 name=image_settings.name, disk_format=image_settings.format, container_format="bare", data=image_file,
124                 properties=image_settings.extra_properties, is_public=image_settings.public)
125         else:
126             created_image = glance.images.create(
127                 name=image_settings.name, disk_format=image_settings.format, container_format="bare", data=image_file,
128                 is_public=image_settings.public)
129
130     return Image(name=image_settings.name, image_id=created_image.id, size=created_image.size,
131                  properties=created_image.properties)
132
133
134 def __create_image_v2(glance, image_settings):
135     """
136     Creates and returns OpenStack image object with an external URL
137     :param glance: the glance client v2
138     :param image_settings: the image settings object
139     :return: the OpenStack image object
140     :raise Exception if using a file and it cannot be found
141     """
142     cleanup_temp_file = False
143     image_file = None
144     if image_settings.image_file:
145         image_filename = image_settings.image_file
146     elif image_settings.url:
147         file_name = str(uuid.uuid4())
148         try:
149             image_file = file_utils.download(image_settings.url, './tmp', file_name)
150             image_filename = image_file.name
151         except:
152             os.remove('./tmp/' + file_name)
153             raise
154
155         cleanup_temp_file = True
156     else:
157         raise Exception('Filename or URL of image not configured')
158
159     created_image = None
160     try:
161         kwargs = dict()
162         kwargs['name'] = image_settings.name
163         kwargs['disk_format'] = image_settings.format
164         kwargs['container_format'] = 'bare'
165
166         if image_settings.public:
167             kwargs['visibility'] = 'public'
168
169         if image_settings.extra_properties:
170             kwargs.update(image_settings.extra_properties)
171
172         created_image = glance.images.create(**kwargs)
173         image_file = open(image_filename, 'rb')
174         glance.images.upload(created_image['id'], image_file)
175     except:
176         logger.error('Unexpected exception creating image. Rolling back')
177         if created_image:
178             delete_image(glance, created_image)
179         raise
180     finally:
181         if image_file:
182             image_file.close()
183         if cleanup_temp_file:
184             os.remove(image_filename)
185
186     updated_image = glance.images.get(created_image['id'])
187     return Image(name=updated_image['name'], image_id=updated_image['id'], size=updated_image['size'],
188                  properties=updated_image.get('properties'))
189
190
191 def delete_image(glance, image):
192     """
193     Deletes an image from OpenStack
194     :param glance: the glance client
195     :param image: the image to delete
196     """
197     if glance.version == VERSION_1:
198         glance.images.delete(image)
199     elif glance.version == VERSION_2:
200         glance.images.delete(image.id)
201     else:
202         raise Exception('Unsupported glance client version')