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):
38 Creates and returns a cinder client object
39 :return: the cinder client
41 return Client(version=os_creds.volume_api_version,
42 session=keystone_utils.keystone_session(os_creds),
43 region_name=os_creds.region_name)
46 def get_volume(cinder, keystone=None, volume_name=None, volume_settings=None,
49 Returns an OpenStack volume object for a given name
50 :param cinder: the Cinder client
51 :param keystone: the Keystone client (required if project_name or
52 volume_settings.project_name is not None
53 :param volume_name: the volume name to lookup
54 :param volume_settings: the volume settings used for lookups
55 :param project_name: the name of the project associated with the volume
56 :return: the volume object or None
59 volume_name = volume_settings.name
61 volumes = cinder.volumes.list()
62 for os_volume in volumes:
63 if os_volume.name == volume_name:
65 if hasattr(os_volume, 'os-vol-tenant-attr:tenant_id'):
67 os_volume, 'os-vol-tenant-attr:tenant_id')
69 if volume_settings and volume_settings.project_name:
70 project_name = volume_settings.project_name
73 project = keystone_utils.get_project_by_id(
76 if project and project.name == project_name:
77 return __map_os_volume_to_domain(os_volume)
79 return __map_os_volume_to_domain(os_volume)
82 def __get_os_volume_by_id(cinder, volume_id):
84 Returns an OpenStack volume object for a given name
85 :param cinder: the Cinder client
86 :param volume_id: the volume ID to lookup
87 :return: the SNAPS-OO Domain Volume object or None
89 return cinder.volumes.get(volume_id)
92 def get_volume_by_id(cinder, volume_id):
94 Returns an OpenStack volume object for a given name
95 :param cinder: the Cinder client
96 :param volume_id: the volume ID to lookup
97 :return: the SNAPS-OO Domain Volume object or None
99 os_volume = __get_os_volume_by_id(cinder, volume_id)
100 return __map_os_volume_to_domain(os_volume)
103 def __map_os_volume_to_domain(os_volume):
105 Returns a SNAPS-OO domain Volume object that is created by an OpenStack
107 :param os_volume: the OpenStack volume object
108 :return: Volume domain object
111 if hasattr(os_volume, 'os-vol-tenant-attr:tenant_id'):
112 project_id = getattr(
113 os_volume, 'os-vol-tenant-attr:tenant_id')
116 name=os_volume.name, volume_id=os_volume.id,
117 project_id=project_id, description=os_volume.description,
118 size=os_volume.size, vol_type=os_volume.volume_type,
119 availability_zone=os_volume.availability_zone,
120 multi_attach=os_volume.multiattach,
121 attachments=os_volume.attachments)
124 def get_volume_status(cinder, volume):
126 Returns a new OpenStack Volume object for a given OpenStack volume object
127 :param cinder: the Cinder client
128 :param volume: the domain Volume object
129 :return: the OpenStack Volume object
131 os_volume = cinder.volumes.get(volume.id)
132 return os_volume.status
135 def create_volume(cinder, keystone, volume_settings):
137 Creates and returns OpenStack volume object with an external URL
138 :param cinder: the cinder client
139 :param keystone: the keystone client
140 :param volume_settings: the volume settings object
141 :return: the OpenStack volume object
142 :raise Exception if using a file and it cannot be found
145 if volume_settings.project_name:
146 project = keystone_utils.get_project(
147 keystone, project_name=volume_settings.project_name)
149 project_id = project.id
151 raise KeystoneUtilsException(
152 'Project cannot be found with name - '
153 + volume_settings.project_name)
154 os_volume = cinder.volumes.create(
155 name=volume_settings.name,
156 project_id=project_id,
157 description=volume_settings.description,
158 size=volume_settings.size,
159 imageRef=volume_settings.image_name,
160 volume_type=volume_settings.type_name,
161 availability_zone=volume_settings.availability_zone,
162 multiattach=volume_settings.multi_attach)
164 return __map_os_volume_to_domain(os_volume)
167 def delete_volume(cinder, volume):
169 Deletes an volume from OpenStack
170 :param cinder: the cinder client
171 :param volume: the volume to delete
173 logger.info('Deleting volume named - %s', volume.name)
174 return cinder.volumes.delete(volume.id)
177 def get_volume_type(cinder, volume_type_name=None, volume_type_settings=None):
179 Returns an OpenStack volume type object for a given name
180 :param cinder: the Cinder client
181 :param volume_type_name: the volume type name to lookup
182 :param volume_type_settings: the volume type settings used for lookups
183 :return: the volume type object or None
185 if not volume_type_name and not volume_type_settings:
188 if volume_type_settings:
189 volume_type_name = volume_type_settings.name
191 volume_types = cinder.volume_types.list()
192 for vol_type in volume_types:
193 if vol_type.name == volume_type_name:
194 encryption = get_volume_encryption_by_type(cinder, vol_type)
195 return VolumeType(vol_type.name, vol_type.id, vol_type.is_public,
199 def __get_os_volume_type_by_id(cinder, volume_type_id):
201 Returns an OpenStack volume type object for a given name
202 :param cinder: the Cinder client
203 :param volume_type_id: the volume_type ID to lookup
204 :return: the SNAPS-OO Domain Volume object or None
207 return cinder.volume_types.get(volume_type_id)
209 logger.info('Volume with ID [%s] does not exist',
213 def get_volume_type_by_id(cinder, volume_type_id):
215 Returns an OpenStack volume type object for a given name
216 :param cinder: the Cinder client
217 :param volume_type_id: the volume_type ID to lookup
218 :return: the SNAPS-OO Domain Volume object or None
220 os_vol_type = __get_os_volume_type_by_id(cinder, volume_type_id)
222 temp_vol_type = VolumeType(os_vol_type.name, os_vol_type.id,
223 os_vol_type.is_public, None, None)
224 encryption = get_volume_encryption_by_type(cinder, temp_vol_type)
227 if os_vol_type.qos_specs_id:
228 qos_spec = get_qos_by_id(cinder, os_vol_type.qos_specs_id)
230 return VolumeType(os_vol_type.name, os_vol_type.id,
231 os_vol_type.is_public, encryption, qos_spec)
234 def create_volume_type(cinder, type_settings):
236 Creates and returns OpenStack volume type object with an external URL
237 :param cinder: the cinder client
238 :param type_settings: the volume type settings object
239 :return: the volume type domain object
240 :raise Exception if using a file and it cannot be found
242 vol_type = cinder.volume_types.create(
243 type_settings.name, type_settings.description,
244 type_settings.public)
246 vol_encryption = None
247 if type_settings.encryption:
249 vol_encryption = create_volume_encryption(
250 cinder, vol_type, type_settings.encryption)
251 except Exception as e:
252 logger.warn('Error creating volume encryption - %s', e)
255 if type_settings.qos_spec_name:
257 qos_spec = get_qos(cinder, qos_name=type_settings.qos_spec_name)
258 cinder.qos_specs.associate(qos_spec, vol_type.id)
259 except NotFound as e:
260 logger.warn('Unable to locate qos_spec named %s - %s',
261 type_settings.qos_spec_name, e)
263 return VolumeType(vol_type.name, vol_type.id, vol_type.is_public,
264 vol_encryption, qos_spec)
267 def delete_volume_type(cinder, vol_type):
269 Deletes an volume from OpenStack
270 :param cinder: the cinder client
271 :param vol_type: the VolumeType domain object
273 logger.info('Deleting volume named - %s', vol_type.name)
274 cinder.volume_types.delete(vol_type.id)
277 def get_volume_encryption_by_type(cinder, volume_type):
279 Returns an OpenStack volume type object for a given name
280 :param cinder: the Cinder client
281 :param volume_type: the VolumeType domain object
282 :return: the VolumeEncryption domain object or None
284 os_vol_type = __get_os_volume_type_by_id(cinder, volume_type.id)
285 encryption = cinder.volume_encryption_types.get(os_vol_type)
286 if hasattr(encryption, 'encryption_id'):
288 if hasattr(encryption, 'cipher'):
289 cipher = encryption.cipher
291 if hasattr(encryption, 'key_size'):
292 key_size = encryption.key_size
293 return VolumeTypeEncryption(
294 encryption.encryption_id, encryption.volume_type_id,
295 encryption.control_location, encryption.provider, cipher, key_size)
298 def create_volume_encryption(cinder, volume_type, encryption_settings):
300 Creates and returns OpenStack volume type object with an external URL
301 :param cinder: the cinder client
302 :param volume_type: the VolumeType object to associate the encryption
303 :param encryption_settings: the volume type encryption settings object
304 :return: the VolumeTypeEncryption domain object
306 specs = {'name': encryption_settings.name,
307 'provider': encryption_settings.provider_class}
308 if encryption_settings.key_size:
309 specs['key_size'] = encryption_settings.key_size
310 if encryption_settings.provider_class:
311 specs['provider_class'] = encryption_settings.provider_class
312 if encryption_settings.control_location:
313 specs['control_location'] = encryption_settings.control_location.value
314 if encryption_settings.cipher:
315 specs['cipher'] = encryption_settings.cipher
317 encryption = cinder.volume_encryption_types.create(volume_type.id, specs)
320 if hasattr(encryption, 'cipher'):
321 cipher = encryption.cipher
323 if hasattr(encryption, 'key_size'):
324 key_size = encryption.key_size
325 return VolumeTypeEncryption(
326 encryption.encryption_id, encryption.volume_type_id,
327 encryption.control_location, encryption.provider, cipher, key_size)
330 def delete_volume_type_encryption(cinder, vol_type):
332 Deletes an volume from OpenStack
333 :param cinder: the cinder client
334 :param vol_type: the associated VolumeType domain object
336 logger.info('Deleting volume encryption for volume type - %s',
338 os_vol_type = __get_os_volume_type_by_id(cinder, vol_type.id)
339 cinder.volume_encryption_types.delete(os_vol_type)
342 def __get_os_qos(cinder, qos_name=None, qos_settings=None):
344 Returns an OpenStack QoS object for a given name
345 :param cinder: the Cinder client
346 :param qos_name: the qos name to lookup
347 :param qos_settings: the qos settings used for lookups
348 :return: the qos object or None
350 if not qos_name and not qos_settings:
354 qos_name = qos_settings.name
356 qoss = cinder.qos_specs.list()
358 if qos.name == qos_name:
362 def get_qos(cinder, qos_name=None, qos_settings=None):
364 Returns an OpenStack QoS object for a given name
365 :param cinder: the Cinder client
366 :param qos_name: the qos name to lookup
367 :param qos_settings: the qos settings used for lookups
368 :return: the qos object or None
370 os_qos = __get_os_qos(cinder, qos_name, qos_settings)
372 return QoSSpec(name=os_qos.name, spec_id=os_qos.id,
373 consumer=os_qos.consumer)
376 def get_qos_by_id(cinder, qos_id):
378 Returns an OpenStack qos object for a given name
379 :param cinder: the Cinder client
380 :param qos_id: the qos ID to lookup
381 :return: the SNAPS-OO Domain Volume object or None
383 qos = cinder.qos_specs.get(qos_id)
384 return QoSSpec(name=qos.name, spec_id=qos.id, consumer=qos.consumer)
387 def create_qos(cinder, qos_settings):
389 Creates and returns OpenStack qos object with an external URL
390 :param cinder: the cinder client
391 :param qos_settings: the qos settings object
392 :return: the qos domain object
393 :raise Exception if using a file and it cannot be found
395 specs = qos_settings.specs
396 specs['consumer'] = qos_settings.consumer.value
397 qos = cinder.qos_specs.create(qos_settings.name, qos_settings.specs)
398 return QoSSpec(name=qos.name, spec_id=qos.id, consumer=qos.consumer)
401 def delete_qos(cinder, qos):
403 Deletes an QoS from OpenStack
404 :param cinder: the cinder client
405 :param qos: the qos domain object to delete
407 logger.info('Deleting QoS named - %s', qos.name)
408 cinder.qos_specs.delete(qos.id)
411 class KeystoneUtilsException(Exception):
413 Exception when calls to the Keystone client cannot be served properly