summaryrefslogtreecommitdiffstats
path: root/snaps/openstack/create_image.py
diff options
context:
space:
mode:
authorspisarski <s.pisarski@cablelabs.com>2017-02-15 09:13:54 -0700
committerspisarski <s.pisarski@cablelabs.com>2017-02-15 09:15:34 -0700
commit57777f3df521553a06cd01a3861b415d2905ceca (patch)
treef3b3be457baec7b5231309989aa3ffa9658cd25d /snaps/openstack/create_image.py
parent73ef791a1cde68e0d8d69cddf63534fbb90f3e2d (diff)
Initial patch with all code from CableLabs repository.
Change-Id: I70a2778718c5e7f21fd14e4ad28c9269d3761cc7 Signed-off-by: spisarski <s.pisarski@cablelabs.com>
Diffstat (limited to 'snaps/openstack/create_image.py')
-rw-r--r--snaps/openstack/create_image.py188
1 files changed, 188 insertions, 0 deletions
diff --git a/snaps/openstack/create_image.py b/snaps/openstack/create_image.py
new file mode 100644
index 0000000..e1b8d94
--- /dev/null
+++ b/snaps/openstack/create_image.py
@@ -0,0 +1,188 @@
+# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
+# and others. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import logging
+import time
+
+from glanceclient.exc import HTTPNotFound
+
+from snaps.openstack.utils import glance_utils, nova_utils
+
+__author__ = 'spisarski'
+
+logger = logging.getLogger('create_image')
+
+IMAGE_ACTIVE_TIMEOUT = 600
+POLL_INTERVAL = 3
+STATUS_ACTIVE = 'active'
+
+
+class OpenStackImage:
+ """
+ Class responsible for creating an image in OpenStack
+ """
+
+ def __init__(self, os_creds, image_settings):
+ """
+ Constructor
+ :param os_creds: The OpenStack connection credentials
+ :param image_settings: The image settings
+ :return:
+ """
+ self.__os_creds = os_creds
+ self.image_settings = image_settings
+ self.__image = None
+ self.__glance = glance_utils.glance_client(os_creds)
+
+ def create(self, cleanup=False):
+ """
+ Creates the image in OpenStack if it does not already exist
+ :param cleanup: Denotes whether or not this is being called for cleanup or not
+ :return: The OpenStack Image object
+ """
+ from snaps.openstack.utils import nova_utils
+ nova = nova_utils.nova_client(self.__os_creds)
+
+ self.__image = glance_utils.get_image(nova, self.__glance, self.image_settings.name)
+ if self.__image:
+ logger.info('Found image with name - ' + self.image_settings.name)
+ return self.__image
+ elif not cleanup:
+ self.__image = glance_utils.create_image(self.__glance, self.image_settings)
+ logger.info('Creating image')
+ if self.image_active(block=True):
+ logger.info('Image is now active with name - ' + self.image_settings.name)
+ return self.__image
+ else:
+ raise Exception('Image did not activate in the alloted amount of time')
+ else:
+ logger.info('Did not create image due to cleanup mode')
+
+ return self.__image
+
+ def clean(self):
+ """
+ Cleanse environment of all artifacts
+ :return: void
+ """
+ if self.__image:
+ try:
+ glance_utils.delete_image(self.__glance, self.__image)
+ except HTTPNotFound:
+ pass
+ self.__image = None
+
+ def get_image(self):
+ """
+ Returns the OpenStack image object as it was populated when create() was called
+ :return: the object
+ """
+ return self.__image
+
+ def image_active(self, block=False, timeout=IMAGE_ACTIVE_TIMEOUT, poll_interval=POLL_INTERVAL):
+ """
+ Returns true when the image status returns the value of expected_status_code
+ :param block: When true, thread will block until active or timeout value in seconds has been exceeded (False)
+ :param timeout: The timeout value
+ :param poll_interval: The polling interval in seconds
+ :return: T/F
+ """
+ return self._image_status_check(STATUS_ACTIVE, block, timeout, poll_interval)
+
+ def _image_status_check(self, expected_status_code, block, timeout, poll_interval):
+ """
+ Returns true when the image status returns the value of expected_status_code
+ :param expected_status_code: instance status evaluated with this string value
+ :param block: When true, thread will block until active or timeout value in seconds has been exceeded (False)
+ :param timeout: The timeout value
+ :param poll_interval: The polling interval in seconds
+ :return: T/F
+ """
+ # sleep and wait for image status change
+ if block:
+ start = time.time()
+ else:
+ start = time.time() - timeout
+
+ while timeout > time.time() - start:
+ status = self._status(expected_status_code)
+ if status:
+ logger.info('Image is active with name - ' + self.image_settings.name)
+ return True
+
+ logger.debug('Retry querying image status in ' + str(poll_interval) + ' seconds')
+ time.sleep(poll_interval)
+ logger.debug('Image status query timeout in ' + str(timeout - (time.time() - start)))
+
+ logger.error('Timeout checking for image status for ' + expected_status_code)
+ return False
+
+ def _status(self, expected_status_code):
+ """
+ Returns True when active else False
+ :param expected_status_code: instance status evaluated with this string value
+ :return: T/F
+ """
+ # TODO - Place this API call into glance_utils.
+ nova = nova_utils.nova_client(self.__os_creds)
+ instance = glance_utils.get_image(nova, self.__glance, self.image_settings.name)
+ # instance = self.__glance.images.get(self.__image)
+ if not instance:
+ logger.warn('Cannot find instance with id - ' + self.__image.id)
+ return False
+
+ if instance.status == 'ERROR':
+ raise Exception('Instance had an error during deployment')
+ logger.debug('Instance status is - ' + instance.status)
+ return instance.status == expected_status_code
+
+
+class ImageSettings:
+ def __init__(self, config=None, name=None, image_user=None, img_format=None, url=None, image_file=None,
+ nic_config_pb_loc=None):
+ """
+
+ :param config: dict() object containing the configuration settings using the attribute names below as each
+ member's the key and overrides any of the other parameters.
+ :param name: the image's name (required)
+ :param image_user: the image's default sudo user (required)
+ :param img_format: the image type (required)
+ :param url: the image download location (requires url or img_file)
+ :param image_file: the image file location (requires url or img_file)
+ :param nic_config_pb_loc: the file location to the Ansible Playbook that can configure multiple NICs
+ """
+
+ if config:
+ self.name = config.get('name')
+ self.image_user = config.get('image_user')
+ self.format = config.get('format')
+ self.url = config.get('download_url')
+ self.image_file = config.get('image_file')
+ self.nic_config_pb_loc = config.get('nic_config_pb_loc')
+ else:
+ self.name = name
+ self.image_user = image_user
+ self.format = img_format
+ self.url = url
+ self.image_file = image_file
+ self.nic_config_pb_loc = nic_config_pb_loc
+
+ if not self.name or not self.image_user or not self.format:
+ raise Exception("The attributes name, image_user, format, and url are required for ImageSettings")
+
+ if not self.url and not self.image_file:
+ raise Exception('URL or image file must be set')
+
+ if self.url and self.image_file:
+ raise Exception('Please set either URL or image file, not both')