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
20 from neutronclient.common.utils import str2bool
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_type')
30 class OpenStackVolumeType(OpenStackVolumeObject):
32 Class responsible for managing an volume in OpenStack
35 def __init__(self, os_creds, volume_type_settings):
38 :param os_creds: The OpenStack connection credentials
39 :param volume_type_settings: The volume type settings
42 super(self.__class__, self).__init__(os_creds)
44 self.volume_type_settings = volume_type_settings
45 self.__volume_type = None
49 Loads the existing Volume
50 :return: The Volume domain object or None
52 super(self.__class__, self).initialize()
54 self.__volume_type = cinder_utils.get_volume_type(
55 self._cinder, volume_type_settings=self.volume_type_settings)
57 return self.__volume_type
61 Creates the volume in OpenStack if it does not already exist and
62 returns the domain Volume object
63 :return: The Volume domain object or None
67 if not self.__volume_type:
68 self.__volume_type = cinder_utils.create_volume_type(
69 self._cinder, self.volume_type_settings)
71 'Created volume type with name - %s',
72 self.volume_type_settings.name)
74 return self.__volume_type
78 Cleanse environment of all artifacts
81 if self.__volume_type:
83 cinder_utils.delete_volume_type(self._cinder,
88 self.__volume_type = None
90 def get_volume_type(self):
92 Returns the domain Volume object as it was populated when create() was
96 return self.__volume_type
99 class VolumeTypeSettings:
100 def __init__(self, **kwargs):
103 :param name: the volume's name (required)
104 :param description: the volume's name (optional)
105 :param encryption: VolumeTypeEncryptionSettings (optional)
106 :param qos_spec_name: name of the QoS Spec to associate (optional)
107 :param public: volume visibility where True denotes global
110 TODO - Implement project_access parameter that will associate this
111 VolumeType to a list of project names
114 self.name = kwargs.get('name')
115 self.description = kwargs.get('description')
116 self.qos_spec_name = kwargs.get('qos_spec_name')
118 if 'encryption' in kwargs:
119 if isinstance(kwargs['encryption'], dict):
120 self.encryption = VolumeTypeEncryptionSettings(
121 **kwargs['encryption'])
122 elif isinstance(kwargs['encryption'],
123 VolumeTypeEncryptionSettings):
124 self.encryption = kwargs['encryption']
126 self.encryption = None
128 if 'public' in kwargs:
129 if isinstance(kwargs['public'], str):
130 self.public = str2bool(kwargs['public'])
132 self.public = kwargs['public']
137 raise VolumeTypeSettingsError("The attribute name is required")
139 def __eq__(self, other):
140 return (self.name == other.name
141 and self.description == other.description
142 and self.qos_spec_name == other.qos_spec_name
143 and self.encryption == other.encryption
144 and self.public == other.public)
147 class ControlLocation(enum.Enum):
149 QoS Specification consumer types
151 front_end = 'front-end'
152 back_end = 'back-end'
155 class VolumeTypeEncryptionSettings:
156 def __init__(self, **kwargs):
159 :param name: the volume's name (required)
160 :param provider_class: the volume's provider class (e.g. LuksEncryptor)
161 :param control_location: the notional service where encryption is
162 performed (e.g., front-end=Nova). The default
163 value is 'front-end.'
164 :param cipher: the encryption algorithm/mode to use
165 (e.g., aes-xts-plain64). If the field is left empty,
166 the provider default will be used
167 :param key_size: the size of the encryption key, in bits
168 (e.g., 128, 256). If the field is left empty, the
169 provider default will be used
172 self.name = kwargs.get('name')
173 self.provider_class = kwargs.get('provider_class')
174 self.control_location = kwargs.get('control_location')
175 if kwargs.get('control_location'):
176 self.control_location = map_control_location(
177 kwargs['control_location'])
179 self.control_location = None
181 self.cipher = kwargs.get('cipher')
182 self.key_size = kwargs.get('key_size')
184 if (not self.name or not self.provider_class
185 or not self.control_location):
186 raise VolumeTypeSettingsError(
187 'The attributes name, provider_class, and control_location '
190 def __eq__(self, other):
191 return (self.name == other.name
192 and self.provider_class == other.provider_class
193 and self.control_location == other.control_location
194 and self.cipher == other.cipher
195 and self.key_size == other.key_size)
198 def map_control_location(control_location):
200 Takes a the protocol value maps it to the Consumer enum. When None return
202 :param control_location: the value to map to the Enum
203 :return: a ControlLocation enum object
204 :raise: Exception if control_location parameter is invalid
206 if not control_location:
208 elif isinstance(control_location, ControlLocation):
209 return control_location
211 proto_str = str(control_location)
212 if proto_str == 'front-end':
213 return ControlLocation.front_end
214 elif proto_str == 'back-end':
215 return ControlLocation.back_end
217 raise VolumeTypeSettingsError('Invalid Consumer - ' + proto_str)
220 class VolumeTypeSettingsError(Exception):
222 Exception to be thrown when an volume settings are incorrect
225 def __init__(self, message):
226 Exception.__init__(self, message)
229 class VolumeTypeCreationError(Exception):
231 Exception to be thrown when an volume cannot be created
234 def __init__(self, message):
235 Exception.__init__(self, message)