1 # Copyright (c) 2016 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.
18 from glanceclient.exc import HTTPNotFound
20 from snaps.openstack.utils import glance_utils, nova_utils
22 __author__ = 'spisarski'
24 logger = logging.getLogger('create_image')
26 IMAGE_ACTIVE_TIMEOUT = 600
28 STATUS_ACTIVE = 'active'
33 Class responsible for creating an image in OpenStack
36 def __init__(self, os_creds, image_settings):
39 :param os_creds: The OpenStack connection credentials
40 :param image_settings: The image settings
43 self.__os_creds = os_creds
44 self.image_settings = image_settings
46 self.__glance = glance_utils.glance_client(os_creds)
48 def create(self, cleanup=False):
50 Creates the image in OpenStack if it does not already exist
51 :param cleanup: Denotes whether or not this is being called for cleanup or not
52 :return: The OpenStack Image object
54 from snaps.openstack.utils import nova_utils
55 nova = nova_utils.nova_client(self.__os_creds)
57 self.__image = glance_utils.get_image(nova, self.__glance, self.image_settings.name)
59 logger.info('Found image with name - ' + self.image_settings.name)
62 self.__image = glance_utils.create_image(self.__glance, self.image_settings)
63 logger.info('Creating image')
64 if self.image_active(block=True):
65 logger.info('Image is now active with name - ' + self.image_settings.name)
68 raise Exception('Image did not activate in the alloted amount of time')
70 logger.info('Did not create image due to cleanup mode')
76 Cleanse environment of all artifacts
81 glance_utils.delete_image(self.__glance, self.__image)
88 Returns the OpenStack image object as it was populated when create() was called
93 def image_active(self, block=False, timeout=IMAGE_ACTIVE_TIMEOUT, poll_interval=POLL_INTERVAL):
95 Returns true when the image status returns the value of expected_status_code
96 :param block: When true, thread will block until active or timeout value in seconds has been exceeded (False)
97 :param timeout: The timeout value
98 :param poll_interval: The polling interval in seconds
101 return self._image_status_check(STATUS_ACTIVE, block, timeout, poll_interval)
103 def _image_status_check(self, expected_status_code, block, timeout, poll_interval):
105 Returns true when the image status returns the value of expected_status_code
106 :param expected_status_code: instance status evaluated with this string value
107 :param block: When true, thread will block until active or timeout value in seconds has been exceeded (False)
108 :param timeout: The timeout value
109 :param poll_interval: The polling interval in seconds
112 # sleep and wait for image status change
116 start = time.time() - timeout
118 while timeout > time.time() - start:
119 status = self._status(expected_status_code)
121 logger.info('Image is active with name - ' + self.image_settings.name)
124 logger.debug('Retry querying image status in ' + str(poll_interval) + ' seconds')
125 time.sleep(poll_interval)
126 logger.debug('Image status query timeout in ' + str(timeout - (time.time() - start)))
128 logger.error('Timeout checking for image status for ' + expected_status_code)
131 def _status(self, expected_status_code):
133 Returns True when active else False
134 :param expected_status_code: instance status evaluated with this string value
137 # TODO - Place this API call into glance_utils.
138 nova = nova_utils.nova_client(self.__os_creds)
139 instance = glance_utils.get_image(nova, self.__glance, self.image_settings.name)
140 # instance = self.__glance.images.get(self.__image)
142 logger.warn('Cannot find instance with id - ' + self.__image.id)
145 if instance.status == 'ERROR':
146 raise Exception('Instance had an error during deployment')
147 logger.debug('Instance status is - ' + instance.status)
148 return instance.status == expected_status_code
152 def __init__(self, config=None, name=None, image_user=None, img_format=None, url=None, image_file=None,
153 nic_config_pb_loc=None):
156 :param config: dict() object containing the configuration settings using the attribute names below as each
157 member's the key and overrides any of the other parameters.
158 :param name: the image's name (required)
159 :param image_user: the image's default sudo user (required)
160 :param img_format: the image type (required)
161 :param url: the image download location (requires url or img_file)
162 :param image_file: the image file location (requires url or img_file)
163 :param nic_config_pb_loc: the file location to the Ansible Playbook that can configure multiple NICs
167 self.name = config.get('name')
168 self.image_user = config.get('image_user')
169 self.format = config.get('format')
170 self.url = config.get('download_url')
171 self.image_file = config.get('image_file')
172 self.nic_config_pb_loc = config.get('nic_config_pb_loc')
175 self.image_user = image_user
176 self.format = img_format
178 self.image_file = image_file
179 self.nic_config_pb_loc = nic_config_pb_loc
181 if not self.name or not self.image_user or not self.format:
182 raise Exception("The attributes name, image_user, format, and url are required for ImageSettings")
184 if not self.url and not self.image_file:
185 raise Exception('URL or image file must be set')
187 if self.url and self.image_file:
188 raise Exception('Please set either URL or image file, not both')