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 cinderclient.exceptions import NotFound
21 from snaps.config.volume import VolumeConfig
22 from snaps.openstack.openstack_creator import OpenStackVolumeObject
23 from snaps.openstack.utils import cinder_utils
25 __author__ = 'spisarski'
27 logger = logging.getLogger('create_volume')
29 VOLUME_ACTIVE_TIMEOUT = 300
30 VOLUME_DELETE_TIMEOUT = 60
32 STATUS_ACTIVE = 'available'
33 STATUS_IN_USE = 'in-use'
34 STATUS_FAILED = 'error'
35 STATUS_DELETED = 'deleted'
38 class OpenStackVolume(OpenStackVolumeObject):
40 Class responsible for managing an volume in OpenStack
43 def __init__(self, os_creds, volume_settings):
46 :param os_creds: The OpenStack connection credentials
47 :param volume_settings: The volume settings
50 super(self.__class__, self).__init__(os_creds)
52 self.volume_settings = volume_settings
57 Loads the existing Volume
58 :return: The Volume domain object or None
60 super(self.__class__, self).initialize()
62 self.__volume = cinder_utils.get_volume(
63 self._cinder, volume_settings=self.volume_settings)
66 def create(self, block=False):
68 Creates the volume in OpenStack if it does not already exist and
69 returns the domain Volume object
70 :return: The Volume domain object or None
75 self.__volume = cinder_utils.create_volume(
76 self._cinder, self.volume_settings)
79 'Created volume with name - %s', self.volume_settings.name)
82 if self.volume_active(block=True):
83 logger.info('Volume is now active with name - %s',
84 self.volume_settings.name)
87 raise VolumeCreationError(
88 'Volume was not created or activated in the '
89 'alloted amount of time')
91 logger.info('Did not create volume due to cleanup mode')
97 Cleanse environment of all artifacts
102 if self.volume_active():
103 cinder_utils.delete_volume(self._cinder, self.__volume)
105 logger.warn('Timeout waiting to delete volume %s',
111 if self.volume_deleted(block=True):
113 'Volume has been properly deleted with name - %s',
114 self.volume_settings.name)
118 'Volume not deleted within the timeout period of %s '
119 'seconds', VOLUME_DELETE_TIMEOUT)
120 except Exception as e:
122 'Unexpected error while checking VM instance status - %s',
127 def get_volume(self):
129 Returns the domain Volume object as it was populated when create() was
135 def volume_active(self, block=False, timeout=VOLUME_ACTIVE_TIMEOUT,
136 poll_interval=POLL_INTERVAL):
138 Returns true when the volume status returns the value of
140 :param block: When true, thread will block until active or timeout
141 value in seconds has been exceeded (False)
142 :param timeout: The timeout value
143 :param poll_interval: The polling interval in seconds
146 return self._volume_status_check(STATUS_ACTIVE, block, timeout,
149 def volume_in_use(self):
151 Returns true when the volume status returns the value of
155 return self._volume_status_check(STATUS_IN_USE, False, 0, 0)
157 def volume_deleted(self, block=False, poll_interval=POLL_INTERVAL):
159 Returns true when the VM status returns the value of
160 expected_status_code or instance retrieval throws a NotFound exception.
161 :param block: When true, thread will block until active or timeout
162 value in seconds has been exceeded (False)
163 :param poll_interval: The polling interval in seconds
167 return self._volume_status_check(
168 STATUS_DELETED, block, VOLUME_DELETE_TIMEOUT, poll_interval)
169 except NotFound as e:
171 "Volume not found when querying status for %s with message "
172 "%s", STATUS_DELETED, e)
175 def _volume_status_check(self, expected_status_code, block, timeout,
178 Returns true when the volume status returns the value of
180 :param expected_status_code: instance status evaluated with this string
182 :param block: When true, thread will block until active or timeout
183 value in seconds has been exceeded (False)
184 :param timeout: The timeout value
185 :param poll_interval: The polling interval in seconds
188 # sleep and wait for volume status change
192 start = time.time() - timeout + 1
194 while timeout > time.time() - start:
195 status = self._status(expected_status_code)
197 logger.debug('Volume is active with name - %s',
198 self.volume_settings.name)
201 logger.debug('Retry querying volume status in %s seconds',
203 time.sleep(poll_interval)
204 logger.debug('Volume status query timeout in %s',
205 str(timeout - (time.time() - start)))
208 'Timeout checking for volume status for ' + expected_status_code)
211 def _status(self, expected_status_code):
213 Returns True when active else False
214 :param expected_status_code: instance status evaluated with this string
218 status = cinder_utils.get_volume_status(self._cinder, self.__volume)
221 'Cannot volume status for volume with ID - %s',
225 if status == 'ERROR':
226 raise VolumeCreationError(
227 'Instance had an error during deployment')
228 logger.debug('Instance status is - ' + status)
229 return status == expected_status_code
232 class VolumeSettings(VolumeConfig):
234 Class to hold the configuration settings required for creating OpenStack
235 Volume Type Encryption objects
239 def __init__(self, **kwargs):
240 from warnings import warn
241 warn('Use snaps.config.volume.VolumeConfig instead',
243 super(self.__class__, self).__init__(**kwargs)
246 class VolumeCreationError(Exception):
248 Exception to be thrown when an volume cannot be created
251 def __init__(self, message):
252 Exception.__init__(self, message)