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.
16 from glanceclient.exc import HTTPNotFound
20 from snaps.openstack.openstack_creator import OpenStackCloudObject
21 from snaps.openstack.utils import glance_utils
22 from snaps.config import image
24 __author__ = 'spisarski'
26 logger = logging.getLogger('create_image')
28 IMAGE_ACTIVE_TIMEOUT = 600
30 STATUS_ACTIVE = 'active'
33 class OpenStackImage(OpenStackCloudObject):
35 Class responsible for managing an image in OpenStack
38 def __init__(self, os_creds, image_settings):
41 :param os_creds: The OpenStack connection credentials
42 :param image_settings: An snaps.config.image.ImageConfig object
44 super(self.__class__, self).__init__(os_creds)
46 self.image_settings = image_settings
48 self.__kernel_image = None
49 self.__ramdisk_image = None
54 Loads the existing Image
55 :return: The Image domain object or None
57 self.__glance = glance_utils.glance_client(self._os_creds)
58 self.__image = glance_utils.get_image(
59 self.__glance, image_settings=self.image_settings)
62 logger.info('Found image with name - ' + self.image_settings.name)
64 elif (self.image_settings.exists and not self.image_settings.url
65 and not self.image_settings.image_file):
66 raise ImageCreationError(
67 'Image with does not exist with name - ' +
68 self.image_settings.name)
70 if self.image_settings.kernel_image_settings:
71 self.__kernel_image = glance_utils.get_image(
73 image_settings=self.image_settings.kernel_image_settings)
75 if self.image_settings.ramdisk_image_settings:
76 self.__ramdisk_image = glance_utils.get_image(
78 image_settings=self.image_settings.ramdisk_image_settings)
84 Creates the image in OpenStack if it does not already exist and returns
85 the domain Image object
86 :return: The Image domain object or None
91 extra_properties = self.image_settings.extra_properties or dict()
93 if self.image_settings.kernel_image_settings:
94 if not self.__kernel_image:
96 'Creating associated kernel image with name - %s',
97 self.image_settings.kernel_image_settings.name)
98 self.__kernel_image = glance_utils.create_image(
100 self.image_settings.kernel_image_settings)
101 extra_properties['kernel_id'] = self.__kernel_image.id
103 if self.image_settings.ramdisk_image_settings:
104 if not self.__ramdisk_image:
106 'Creating associated ramdisk image with name - %s',
107 self.image_settings.ramdisk_image_settings.name)
108 self.__ramdisk_image = glance_utils.create_image(
110 self.image_settings.ramdisk_image_settings)
111 extra_properties['ramdisk_id'] = self.__ramdisk_image.id
113 self.image_settings.extra_properties = extra_properties
114 self.__image = glance_utils.create_image(self.__glance,
118 'Created image with name - %s', self.image_settings.name)
120 if self.__image and self.image_active(block=True):
122 'Image is now active with name - %s',
123 self.image_settings.name)
126 raise ImageCreationError(
127 'Image was not created or activated in the alloted amount'
134 Cleanse environment of all artifacts
137 for img in [self.__image, self.__kernel_image, self.__ramdisk_image]:
140 glance_utils.delete_image(self.__glance, img)
145 self.__kernel_image = None
146 self.__ramdisk_image = None
150 Returns the domain Image object as it was populated when create() was
156 def get_kernel_image(self):
158 Returns the OpenStack kernel image object as it was populated when
162 return self.__kernel_image
164 def get_ramdisk_image(self):
166 Returns the OpenStack ramdisk image object as it was populated when
170 return self.__ramdisk_image
172 def image_active(self, block=False, timeout=IMAGE_ACTIVE_TIMEOUT,
173 poll_interval=POLL_INTERVAL):
175 Returns true when the image status returns the value of
177 :param block: When true, thread will block until active or timeout
178 value in seconds has been exceeded (False)
179 :param timeout: The timeout value
180 :param poll_interval: The polling interval in seconds
183 return self._image_status_check(STATUS_ACTIVE, block, timeout,
186 def _image_status_check(self, expected_status_code, block, timeout,
189 Returns true when the image status returns the value of
191 :param expected_status_code: instance status evaluated with this string
193 :param block: When true, thread will block until active or timeout
194 value in seconds has been exceeded (False)
195 :param timeout: The timeout value
196 :param poll_interval: The polling interval in seconds
199 # sleep and wait for image status change
203 start = time.time() - timeout
205 while timeout > time.time() - start:
206 status = self._status(expected_status_code)
209 'Image is active with name - ' + self.image_settings.name)
212 logger.debug('Retry querying image status in ' + str(
213 poll_interval) + ' seconds')
214 time.sleep(poll_interval)
215 logger.debug('Image status query timeout in ' + str(
216 timeout - (time.time() - start)))
219 'Timeout checking for image status for ' + expected_status_code)
222 def _status(self, expected_status_code):
224 Returns True when active else False
225 :param expected_status_code: instance status evaluated with this string
229 status = glance_utils.get_image_status(self.__glance, self.__image)
232 'Cannot image status for image with ID - ' + self.__image.id)
235 if status == 'ERROR':
236 raise ImageCreationError('Instance had an error during deployment')
237 logger.debug('Instance status is - ' + status)
238 return status == expected_status_code
241 class ImageSettings(image.ImageConfig):
243 Deprecated, use snaps.config.image.ImageSettings instead
245 def __init__(self, **kwargs):
246 from warnings import warn
247 warn('Use snaps.config.image.ImageConfig instead', DeprecationWarning)
248 super(ImageSettings, self).__init__(**kwargs)
251 class ImageCreationError(Exception):
253 Exception to be thrown when an image cannot be created
256 def __init__(self, message):
257 Exception.__init__(self, message)