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.
17 from cinderclient.client import Client
18 from cinderclient.exceptions import NotFound
20 from snaps.domain.volume import (
21 QoSSpec, VolumeType, VolumeTypeEncryption, Volume)
22 from snaps.openstack.utils import keystone_utils
24 __author__ = 'spisarski'
26 logger = logging.getLogger('cinder_utils')
32 Utilities for basic neutron API calls
36 def cinder_client(os_creds, session=None):
38 Creates and returns a cinder client object
39 :param os_creds: the credentials for connecting to the OpenStack remote API
40 :param session: the keystone session object (optional)
41 :return: the cinder client
44 session = keystone_utils.keystone_session(os_creds)
46 return Client(version=os_creds.volume_api_version,
48 region_name=os_creds.region_name)
51 def get_volume(cinder, keystone=None, volume_name=None, volume_settings=None,
54 Returns an OpenStack volume object for a given name
55 :param cinder: the Cinder client
56 :param keystone: the Keystone client (required if project_name or
57 volume_settings.project_name is not None
58 :param volume_name: the volume name to lookup
59 :param volume_settings: the volume settings used for lookups
60 :param project_name: the name of the project associated with the volume
61 :return: the volume object or None
64 volume_name = volume_settings.name
66 volumes = cinder.volumes.list()
67 for os_volume in volumes:
68 if os_volume.name == volume_name:
70 if hasattr(os_volume, 'os-vol-tenant-attr:tenant_id'):
72 os_volume, 'os-vol-tenant-attr:tenant_id')
74 if volume_settings and volume_settings.project_name:
75 project_name = volume_settings.project_name
78 project = keystone_utils.get_project_by_id(
81 if project and project.name == project_name:
82 return __map_os_volume_to_domain(os_volume)
84 return __map_os_volume_to_domain(os_volume)
87 def __get_os_volume_by_id(cinder, volume_id):
89 Returns an OpenStack volume object for a given name
90 :param cinder: the Cinder client
91 :param volume_id: the volume ID to lookup
92 :return: the SNAPS-OO Domain Volume object or None
94 return cinder.volumes.get(volume_id)
97 def get_volume_by_id(cinder, volume_id):
99 Returns an OpenStack volume object for a given name
100 :param cinder: the Cinder client
101 :param volume_id: the volume ID to lookup
102 :return: the SNAPS-OO Domain Volume object or None
104 os_volume = __get_os_volume_by_id(cinder, volume_id)
105 return __map_os_volume_to_domain(os_volume)
108 def __map_os_volume_to_domain(os_volume):
110 Returns a SNAPS-OO domain Volume object that is created by an OpenStack
112 :param os_volume: the OpenStack volume object
113 :return: Volume domain object
116 if hasattr(os_volume, 'os-vol-tenant-attr:tenant_id'):
117 project_id = getattr(
118 os_volume, 'os-vol-tenant-attr:tenant_id')
121 name=os_volume.name, volume_id=os_volume.id,
122 project_id=project_id, description=os_volume.description,
123 size=os_volume.size, vol_type=os_volume.volume_type,
124 availability_zone=os_volume.availability_zone,
125 multi_attach=os_volume.multiattach,
126 attachments=os_volume.attachments)
129 def get_volume_status(cinder, volume):
131 Returns a new OpenStack Volume object for a given OpenStack volume object
132 :param cinder: the Cinder client
133 :param volume: the domain Volume object
134 :return: the OpenStack Volume object
136 os_volume = cinder.volumes.get(volume.id)
137 return os_volume.status
140 def create_volume(cinder, keystone, volume_settings):
142 Creates and returns OpenStack volume object with an external URL
143 :param cinder: the cinder client
144 :param keystone: the keystone client
145 :param volume_settings: the volume settings object
146 :return: the OpenStack volume object
147 :raise Exception if using a file and it cannot be found
150 if volume_settings.project_name:
151 project = keystone_utils.get_project(
152 keystone, project_name=volume_settings.project_name)
154 project_id = project.id
156 raise KeystoneUtilsException(
157 'Project cannot be found with name - '
158 + volume_settings.project_name)
159 os_volume = cinder.volumes.create(
160 name=volume_settings.name,
161 project_id=project_id,
162 description=volume_settings.description,
163 size=volume_settings.size,
164 imageRef=volume_settings.image_name,
165 volume_type=volume_settings.type_name,
166 availability_zone=volume_settings.availability_zone,
167 multiattach=volume_settings.multi_attach)
169 return __map_os_volume_to_domain(os_volume)
172 def delete_volume(cinder, volume):
174 Deletes an volume from OpenStack
175 :param cinder: the cinder client
176 :param volume: the volume to delete
178 logger.info('Deleting volume named - %s', volume.name)
179 return cinder.volumes.delete(volume.id)
182 def get_volume_type(cinder, volume_type_name=None, volume_type_settings=None):
184 Returns an OpenStack volume type object for a given name
185 :param cinder: the Cinder client
186 :param volume_type_name: the volume type name to lookup
187 :param volume_type_settings: the volume type settings used for lookups
188 :return: the volume type object or None
190 if not volume_type_name and not volume_type_settings:
193 if volume_type_settings:
194 volume_type_name = volume_type_settings.name
196 volume_types = cinder.volume_types.list()
197 for vol_type in volume_types:
198 if vol_type.name == volume_type_name:
199 encryption = get_volume_encryption_by_type(cinder, vol_type)
200 return VolumeType(vol_type.name, vol_type.id, vol_type.is_public,
204 def __get_os_volume_type_by_id(cinder, volume_type_id):
206 Returns an OpenStack volume type object for a given name
207 :param cinder: the Cinder client
208 :param volume_type_id: the volume_type ID to lookup
209 :return: the SNAPS-OO Domain Volume object or None
212 return cinder.volume_types.get(volume_type_id)
214 logger.info('Volume with ID [%s] does not exist',
218 def get_volume_type_by_id(cinder, volume_type_id):
220 Returns an OpenStack volume type object for a given name
221 :param cinder: the Cinder client
222 :param volume_type_id: the volume_type ID to lookup
223 :return: the SNAPS-OO Domain Volume object or None
225 os_vol_type = __get_os_volume_type_by_id(cinder, volume_type_id)
227 temp_vol_type = VolumeType(os_vol_type.name, os_vol_type.id,
228 os_vol_type.is_public, None, None)
229 encryption = get_volume_encryption_by_type(cinder, temp_vol_type)
232 if os_vol_type.qos_specs_id:
233 qos_spec = get_qos_by_id(cinder, os_vol_type.qos_specs_id)
235 return VolumeType(os_vol_type.name, os_vol_type.id,
236 os_vol_type.is_public, encryption, qos_spec)
239 def create_volume_type(cinder, type_settings):
241 Creates and returns OpenStack volume type object with an external URL
242 :param cinder: the cinder client
243 :param type_settings: the volume type settings object
244 :return: the volume type domain object
245 :raise Exception if using a file and it cannot be found
247 vol_type = cinder.volume_types.create(
248 type_settings.name, type_settings.description,
249 type_settings.public)
251 vol_encryption = None
252 if type_settings.encryption:
254 vol_encryption = create_volume_encryption(
255 cinder, vol_type, type_settings.encryption)
256 except Exception as e:
257 logger.warn('Error creating volume encryption - %s', e)
260 if type_settings.qos_spec_name:
262 qos_spec = get_qos(cinder, qos_name=type_settings.qos_spec_name)
263 cinder.qos_specs.associate(qos_spec, vol_type.id)
264 except NotFound as e:
265 logger.warn('Unable to locate qos_spec named %s - %s',
266 type_settings.qos_spec_name, e)
268 return VolumeType(vol_type.name, vol_type.id, vol_type.is_public,
269 vol_encryption, qos_spec)
272 def delete_volume_type(cinder, vol_type):
274 Deletes an volume from OpenStack
275 :param cinder: the cinder client
276 :param vol_type: the VolumeType domain object
278 logger.info('Deleting volume named - %s', vol_type.name)
279 cinder.volume_types.delete(vol_type.id)
282 def get_volume_encryption_by_type(cinder, volume_type):
284 Returns an OpenStack volume type object for a given name
285 :param cinder: the Cinder client
286 :param volume_type: the VolumeType domain object
287 :return: the VolumeEncryption domain object or None
289 os_vol_type = __get_os_volume_type_by_id(cinder, volume_type.id)
290 encryption = cinder.volume_encryption_types.get(os_vol_type)
291 if hasattr(encryption, 'encryption_id'):
293 if hasattr(encryption, 'cipher'):
294 cipher = encryption.cipher
296 if hasattr(encryption, 'key_size'):
297 key_size = encryption.key_size
298 return VolumeTypeEncryption(
299 encryption.encryption_id, encryption.volume_type_id,
300 encryption.control_location, encryption.provider, cipher, key_size)
303 def create_volume_encryption(cinder, volume_type, encryption_settings):
305 Creates and returns OpenStack volume type object with an external URL
306 :param cinder: the cinder client
307 :param volume_type: the VolumeType object to associate the encryption
308 :param encryption_settings: the volume type encryption settings object
309 :return: the VolumeTypeEncryption domain object
311 specs = {'name': encryption_settings.name,
312 'provider': encryption_settings.provider_class}
313 if encryption_settings.key_size:
314 specs['key_size'] = encryption_settings.key_size
315 if encryption_settings.provider_class:
316 specs['provider_class'] = encryption_settings.provider_class
317 if encryption_settings.control_location:
318 specs['control_location'] = encryption_settings.control_location.value
319 if encryption_settings.cipher:
320 specs['cipher'] = encryption_settings.cipher
322 encryption = cinder.volume_encryption_types.create(volume_type.id, specs)
325 if hasattr(encryption, 'cipher'):
326 cipher = encryption.cipher
328 if hasattr(encryption, 'key_size'):
329 key_size = encryption.key_size
330 return VolumeTypeEncryption(
331 encryption.encryption_id, encryption.volume_type_id,
332 encryption.control_location, encryption.provider, cipher, key_size)
335 def delete_volume_type_encryption(cinder, vol_type):
337 Deletes an volume from OpenStack
338 :param cinder: the cinder client
339 :param vol_type: the associated VolumeType domain object
341 logger.info('Deleting volume encryption for volume type - %s',
343 os_vol_type = __get_os_volume_type_by_id(cinder, vol_type.id)
344 cinder.volume_encryption_types.delete(os_vol_type)
347 def __get_os_qos(cinder, qos_name=None, qos_settings=None):
349 Returns an OpenStack QoS object for a given name
350 :param cinder: the Cinder client
351 :param qos_name: the qos name to lookup
352 :param qos_settings: the qos settings used for lookups
353 :return: the qos object or None
355 if not qos_name and not qos_settings:
359 qos_name = qos_settings.name
361 qoss = cinder.qos_specs.list()
363 if qos.name == qos_name:
367 def get_qos(cinder, qos_name=None, qos_settings=None):
369 Returns an OpenStack QoS object for a given name
370 :param cinder: the Cinder client
371 :param qos_name: the qos name to lookup
372 :param qos_settings: the qos settings used for lookups
373 :return: the qos object or None
375 os_qos = __get_os_qos(cinder, qos_name, qos_settings)
377 return QoSSpec(name=os_qos.name, spec_id=os_qos.id,
378 consumer=os_qos.consumer)
381 def get_qos_by_id(cinder, qos_id):
383 Returns an OpenStack qos object for a given name
384 :param cinder: the Cinder client
385 :param qos_id: the qos ID to lookup
386 :return: the SNAPS-OO Domain Volume object or None
388 qos = cinder.qos_specs.get(qos_id)
389 return QoSSpec(name=qos.name, spec_id=qos.id, consumer=qos.consumer)
392 def create_qos(cinder, qos_settings):
394 Creates and returns OpenStack qos object with an external URL
395 :param cinder: the cinder client
396 :param qos_settings: the qos settings object
397 :return: the qos domain object
398 :raise Exception if using a file and it cannot be found
400 specs = qos_settings.specs
401 specs['consumer'] = qos_settings.consumer.value
402 qos = cinder.qos_specs.create(qos_settings.name, qos_settings.specs)
403 return QoSSpec(name=qos.name, spec_id=qos.id, consumer=qos.consumer)
406 def delete_qos(cinder, qos):
408 Deletes an QoS from OpenStack
409 :param cinder: the cinder client
410 :param qos: the qos domain object to delete
412 logger.info('Deleting QoS named - %s', qos.name)
413 cinder.qos_specs.delete(qos.id)
416 class KeystoneUtilsException(Exception):
418 Exception when calls to the Keystone client cannot be served properly