1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 # and others. All rights reserved.
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:
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
19 from snaps import file_utils
20 from glanceclient.client import Client
22 from snaps.domain.image import Image
23 from snaps.openstack.utils import keystone_utils
25 __author__ = 'spisarski'
27 logger = logging.getLogger('glance_utils')
33 Utilities for basic neutron API calls
37 def glance_client(os_creds, session=None):
39 Creates and returns a glance client object
40 :param os_creds: the credentials for connecting to the OpenStack remote API
41 :param session: the keystone session object (optional)
42 :return: the glance client
45 session = keystone_utils.keystone_session(os_creds)
47 return Client(version=os_creds.image_api_version,
49 region_name=os_creds.region_name)
52 def get_image(glance, image_name=None, image_settings=None):
54 Returns an OpenStack image object for a given name
55 :param glance: the Glance client
56 :param image_name: the image name to lookup
57 :param image_settings: the image settings used for lookups
58 :return: the image object or None
62 if image_settings.exists:
63 img_filter = {'name': image_settings.name}
65 img_filter = {'name': image_settings.name,
66 'disk_format': image_settings.format}
68 img_filter = {'name': image_name}
70 images = glance.images.list(**{'filters': img_filter})
72 if glance.version == VERSION_1:
73 image = glance.images.get(image.id)
74 return Image(name=image.name, image_id=image.id,
75 size=image.size, properties=image.properties)
76 elif glance.version == VERSION_2:
78 name=image['name'], image_id=image['id'],
79 size=image['size'], properties=image.get('properties'))
82 def get_image_by_id(glance, image_id):
84 Returns an OpenStack image object for a given name
85 :param glance: the Glance client
86 :param image_id: the image ID to lookup
87 :return: the SNAPS-OO Domain Image object or None
89 image = glance.images.get(image_id)
91 name=image['name'], image_id=image['id'],
92 size=image['size'], properties=image.get('properties'))
95 def get_image_status(glance, image):
97 Returns a new OpenStack Image object for a given OpenStack image object
98 :param glance: the Glance client
99 :param image: the domain Image object
100 :return: the OpenStack Image object
102 if glance.version == VERSION_1:
103 os_image = glance.images.get(image.id)
104 return os_image.status
105 elif glance.version == VERSION_2:
106 os_image = glance.images.get(image.id)
107 return os_image['status']
109 raise GlanceException('Unsupported glance client version')
112 def create_image(glance, image_settings):
114 Creates and returns OpenStack image object with an external URL
115 :param glance: the glance client
116 :param image_settings: the image settings object
117 :return: the OpenStack image object
118 :raise Exception if using a file and it cannot be found
120 if glance.version == VERSION_1:
121 return __create_image_v1(glance, image_settings)
122 elif glance.version == VERSION_2:
123 return __create_image_v2(glance, image_settings)
125 raise GlanceException('Unsupported glance client version')
128 def __create_image_v1(glance, image_settings):
130 Creates and returns OpenStack image object with an external URL
131 :param glance: the glance client
132 :param image_settings: the image settings object
133 :return: the OpenStack image object
134 :raise exceptions from the Glance client or IOError when opening a file
137 'name': image_settings.name, 'disk_format': image_settings.format,
138 'container_format': 'bare', 'is_public': image_settings.public}
143 if image_settings.extra_properties:
144 kwargs['properties'] = image_settings.extra_properties
146 if image_settings.url:
147 kwargs['location'] = image_settings.url
148 elif image_settings.image_file:
149 image_file = open(image_settings.image_file, 'rb')
150 kwargs['data'] = image_file
153 'Unable to create image with name - %s. No file or URL',
157 created_image = glance.images.create(**kwargs)
158 return Image(name=image_settings.name, image_id=created_image.id,
159 size=created_image.size,
160 properties=created_image.properties)
166 def __create_image_v2(glance, image_settings):
168 Creates and returns OpenStack image object with an external URL
169 :param glance: the glance client v2
170 :param image_settings: the image settings object
171 :return: the OpenStack image object
172 :raise GlanceException or IOException or URLError
174 cleanup_temp_file = False
176 if image_settings.image_file is not None:
177 image_filename = image_settings.image_file
178 elif image_settings.url:
179 file_name = str(uuid.uuid4())
181 image_file = file_utils.download(
182 image_settings.url, './tmp', file_name)
183 image_filename = image_file.name
186 os.remove(image_file.name)
189 cleanup_temp_file = True
191 raise GlanceException('Filename or URL of image not configured')
196 kwargs['name'] = image_settings.name
197 kwargs['disk_format'] = image_settings.format
198 kwargs['container_format'] = 'bare'
200 if image_settings.public:
201 kwargs['visibility'] = 'public'
203 if image_settings.extra_properties:
204 kwargs.update(image_settings.extra_properties)
206 os_image = glance.images.create(**kwargs)
207 image_file = open(os.path.expanduser(image_filename), 'rb')
208 glance.images.upload(os_image['id'], image_file)
210 logger.error('Unexpected exception creating image. Rolling back')
212 delete_image(glance, Image(
213 name=os_image['name'], image_id=os_image['id'],
214 size=os_image['size'], properties=os_image.get('properties')))
218 logger.debug('Closing file %s', image_file.name)
220 if cleanup_temp_file:
221 logger.info('Removing file %s', image_file.name)
222 os.remove(image_filename)
224 return get_image_by_id(glance, os_image['id'])
227 def delete_image(glance, image):
229 Deletes an image from OpenStack
230 :param glance: the glance client
231 :param image: the image to delete
233 logger.info('Deleting image named - %s', image.name)
234 glance.images.delete(image.id)
237 class GlanceException(Exception):
239 Exception when calls to the Glance client cannot be served properly