Third patch for volume support.
[snaps.git] / snaps / openstack / utils / cinder_utils.py
1 # Copyright (c) 2017 Cable Television Laboratories, Inc. ("CableLabs")
2 #                    and others.  All rights reserved.
3 #
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:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15 import logging
16
17 from cinderclient.client import Client
18 from cinderclient.exceptions import NotFound
19
20 from snaps.domain.volume import (
21     QoSSpec, VolumeType, VolumeTypeEncryption, Volume)
22 from snaps.openstack.utils import keystone_utils
23
24 __author__ = 'spisarski'
25
26 logger = logging.getLogger('cinder_utils')
27
28 VERSION_2 = 2
29 VERSION_3 = 3
30
31 """
32 Utilities for basic neutron API calls
33 """
34
35
36 def cinder_client(os_creds):
37     """
38     Creates and returns a cinder client object
39     :return: the cinder client
40     """
41     return Client(version=os_creds.volume_api_version,
42                   session=keystone_utils.keystone_session(os_creds),
43                   region_name=os_creds.region_name)
44
45
46 def get_volume(cinder, volume_name=None, volume_settings=None):
47     """
48     Returns an OpenStack volume object for a given name
49     :param cinder: the Cinder client
50     :param volume_name: the volume name to lookup
51     :param volume_settings: the volume settings used for lookups
52     :return: the volume object or None
53     """
54     if volume_settings:
55         volume_name = volume_settings.name
56
57     volumes = cinder.volumes.list()
58     for volume in volumes:
59         if volume.name == volume_name:
60             return Volume(
61                 name=volume.name, volume_id=volume.id,
62                 description=volume.description, size=volume.size,
63                 vol_type=volume.volume_type,
64                 availability_zone=volume.availability_zone,
65                 multi_attach=volume.multiattach)
66
67
68 def get_volume_by_id(cinder, volume_id):
69     """
70     Returns an OpenStack volume object for a given name
71     :param cinder: the Cinder client
72     :param volume_id: the volume ID to lookup
73     :return: the SNAPS-OO Domain Volume object or None
74     """
75     volume = cinder.volumes.get(volume_id)
76     return Volume(
77         name=volume.name, volume_id=volume.id, description=volume.description,
78         size=volume.size, vol_type=volume.volume_type,
79         availability_zone=volume.availability_zone,
80         multi_attach=volume.multiattach)
81
82
83 def get_volume_status(cinder, volume):
84     """
85     Returns a new OpenStack Volume object for a given OpenStack volume object
86     :param cinder: the Cinder client
87     :param volume: the domain Volume object
88     :return: the OpenStack Volume object
89     """
90     os_volume = cinder.volumes.get(volume.id)
91     return os_volume.status
92
93
94 def create_volume(cinder, volume_settings):
95     """
96     Creates and returns OpenStack volume object with an external URL
97     :param cinder: the cinder client
98     :param volume_settings: the volume settings object
99     :return: the OpenStack volume object
100     :raise Exception if using a file and it cannot be found
101     """
102     created_volume = cinder.volumes.create(
103         name=volume_settings.name, description=volume_settings.description,
104         size=volume_settings.size, imageRef=volume_settings.image_name,
105         volume_type=volume_settings.type_name,
106         availability_zone=volume_settings.availability_zone,
107         multiattach=volume_settings.multi_attach)
108
109     return Volume(
110         name=created_volume.name, volume_id=created_volume.id,
111         description=created_volume.description,
112         size=created_volume.size, vol_type=created_volume.volume_type,
113         availability_zone=created_volume.availability_zone,
114         multi_attach=created_volume.multiattach)
115
116
117 def delete_volume(cinder, volume):
118     """
119     Deletes an volume from OpenStack
120     :param cinder: the cinder client
121     :param volume: the volume to delete
122     """
123     logger.info('Deleting volume named - %s', volume.name)
124     cinder.volumes.delete(volume.id)
125
126
127 def get_volume_type(cinder, volume_type_name=None, volume_type_settings=None):
128     """
129     Returns an OpenStack volume type object for a given name
130     :param cinder: the Cinder client
131     :param volume_type_name: the volume type name to lookup
132     :param volume_type_settings: the volume type settings used for lookups
133     :return: the volume type object or None
134     """
135     if not volume_type_name and not volume_type_settings:
136         return None
137
138     if volume_type_settings:
139         volume_type_name = volume_type_settings.name
140
141     volume_types = cinder.volume_types.list()
142     for vol_type in volume_types:
143         if vol_type.name == volume_type_name:
144             encryption = get_volume_encryption_by_type(cinder, vol_type)
145             return VolumeType(vol_type.name, vol_type.id, vol_type.is_public,
146                               encryption, None)
147
148
149 def __get_os_volume_type_by_id(cinder, volume_type_id):
150     """
151     Returns an OpenStack volume type object for a given name
152     :param cinder: the Cinder client
153     :param volume_type_id: the volume_type ID to lookup
154     :return: the SNAPS-OO Domain Volume object or None
155     """
156     try:
157         return cinder.volume_types.get(volume_type_id)
158     except NotFound:
159         logger.info('Volume with ID [%s] does not exist',
160                     volume_type_id)
161
162
163 def get_volume_type_by_id(cinder, volume_type_id):
164     """
165     Returns an OpenStack volume type object for a given name
166     :param cinder: the Cinder client
167     :param volume_type_id: the volume_type ID to lookup
168     :return: the SNAPS-OO Domain Volume object or None
169     """
170     os_vol_type = __get_os_volume_type_by_id(cinder, volume_type_id)
171     if os_vol_type:
172         temp_vol_type = VolumeType(os_vol_type.name, os_vol_type.id,
173                                    os_vol_type.is_public, None, None)
174         encryption = get_volume_encryption_by_type(cinder, temp_vol_type)
175
176         qos_spec = None
177         if os_vol_type.qos_specs_id:
178             qos_spec = get_qos_by_id(cinder, os_vol_type.qos_specs_id)
179
180         return VolumeType(os_vol_type.name, os_vol_type.id,
181                           os_vol_type.is_public, encryption, qos_spec)
182
183
184 def create_volume_type(cinder, type_settings):
185     """
186     Creates and returns OpenStack volume type object with an external URL
187     :param cinder: the cinder client
188     :param type_settings: the volume type settings object
189     :return: the volume type domain object
190     :raise Exception if using a file and it cannot be found
191     """
192     vol_type = cinder.volume_types.create(
193         type_settings.name, type_settings.description,
194         type_settings.public)
195
196     vol_encryption = None
197     if type_settings.encryption:
198         try:
199             vol_encryption = create_volume_encryption(
200                 cinder, vol_type, type_settings.encryption)
201         except Exception as e:
202             logger.warn('Error creating volume encryption - %s', e)
203
204     qos_spec = None
205     if type_settings.qos_spec_name:
206         try:
207             qos_spec = get_qos(cinder, qos_name=type_settings.qos_spec_name)
208             cinder.qos_specs.associate(qos_spec, vol_type.id)
209         except NotFound as e:
210             logger.warn('Unable to locate qos_spec named %s - %s',
211                         type_settings.qos_spec_name, e)
212
213     return VolumeType(vol_type.name, vol_type.id, vol_type.is_public,
214                       vol_encryption, qos_spec)
215
216
217 def delete_volume_type(cinder, vol_type):
218     """
219     Deletes an volume from OpenStack
220     :param cinder: the cinder client
221     :param vol_type: the VolumeType domain object
222     """
223     logger.info('Deleting volume named - %s', vol_type.name)
224     cinder.volume_types.delete(vol_type.id)
225
226
227 def get_volume_encryption_by_type(cinder, volume_type):
228     """
229     Returns an OpenStack volume type object for a given name
230     :param cinder: the Cinder client
231     :param volume_type: the VolumeType domain object
232     :return: the VolumeEncryption domain object or None
233     """
234     os_vol_type = __get_os_volume_type_by_id(cinder, volume_type.id)
235     encryption = cinder.volume_encryption_types.get(os_vol_type)
236     if hasattr(encryption, 'encryption_id'):
237         cipher = None
238         if hasattr(encryption, 'cipher'):
239             cipher = encryption.cipher
240         key_size = None
241         if hasattr(encryption, 'key_size'):
242             key_size = encryption.key_size
243         return VolumeTypeEncryption(
244             encryption.encryption_id, encryption.volume_type_id,
245             encryption.control_location, encryption.provider, cipher, key_size)
246
247
248 def create_volume_encryption(cinder, volume_type, encryption_settings):
249     """
250     Creates and returns OpenStack volume type object with an external URL
251     :param cinder: the cinder client
252     :param volume_type: the VolumeType object to associate the encryption
253     :param encryption_settings: the volume type encryption settings object
254     :return: the VolumeTypeEncryption domain object
255     """
256     specs = {'name': encryption_settings.name,
257              'provider': encryption_settings.provider_class}
258     if encryption_settings.key_size:
259         specs['key_size'] = encryption_settings.key_size
260     if encryption_settings.provider_class:
261         specs['provider_class'] = encryption_settings.provider_class
262     if encryption_settings.control_location:
263         specs['control_location'] = encryption_settings.control_location.value
264     if encryption_settings.cipher:
265         specs['cipher'] = encryption_settings.cipher
266
267     encryption = cinder.volume_encryption_types.create(volume_type.id, specs)
268
269     cipher = None
270     if hasattr(encryption, 'cipher'):
271         cipher = encryption.cipher
272     key_size = None
273     if hasattr(encryption, 'key_size'):
274         key_size = encryption.key_size
275     return VolumeTypeEncryption(
276         encryption.encryption_id, encryption.volume_type_id,
277         encryption.control_location, encryption.provider, cipher, key_size)
278
279
280 def delete_volume_type_encryption(cinder, vol_type):
281     """
282     Deletes an volume from OpenStack
283     :param cinder: the cinder client
284     :param vol_type: the associated VolumeType domain object
285     """
286     logger.info('Deleting volume encryption for volume type - %s',
287                 vol_type.name)
288     os_vol_type = __get_os_volume_type_by_id(cinder, vol_type.id)
289     cinder.volume_encryption_types.delete(os_vol_type)
290
291
292 def __get_os_qos(cinder, qos_name=None, qos_settings=None):
293     """
294     Returns an OpenStack QoS object for a given name
295     :param cinder: the Cinder client
296     :param qos_name: the qos name to lookup
297     :param qos_settings: the qos settings used for lookups
298     :return: the qos object or None
299     """
300     if not qos_name and not qos_settings:
301         return None
302
303     if qos_settings:
304         qos_name = qos_settings.name
305
306     qoss = cinder.qos_specs.list()
307     for qos in qoss:
308         if qos.name == qos_name:
309             return qos
310
311
312 def get_qos(cinder, qos_name=None, qos_settings=None):
313     """
314     Returns an OpenStack QoS object for a given name
315     :param cinder: the Cinder client
316     :param qos_name: the qos name to lookup
317     :param qos_settings: the qos settings used for lookups
318     :return: the qos object or None
319     """
320     os_qos = __get_os_qos(cinder, qos_name, qos_settings)
321     if os_qos:
322         return QoSSpec(name=os_qos.name, spec_id=os_qos.id,
323                        consumer=os_qos.consumer)
324
325
326 def get_qos_by_id(cinder, qos_id):
327     """
328     Returns an OpenStack qos object for a given name
329     :param cinder: the Cinder client
330     :param qos_id: the qos ID to lookup
331     :return: the SNAPS-OO Domain Volume object or None
332     """
333     qos = cinder.qos_specs.get(qos_id)
334     return QoSSpec(name=qos.name, spec_id=qos.id, consumer=qos.consumer)
335
336
337 def create_qos(cinder, qos_settings):
338     """
339     Creates and returns OpenStack qos object with an external URL
340     :param cinder: the cinder client
341     :param qos_settings: the qos settings object
342     :return: the qos domain object
343     :raise Exception if using a file and it cannot be found
344     """
345     specs = qos_settings.specs
346     specs['consumer'] = qos_settings.consumer.value
347     qos = cinder.qos_specs.create(qos_settings.name, qos_settings.specs)
348     return QoSSpec(name=qos.name, spec_id=qos.id, consumer=qos.consumer)
349
350
351 def delete_qos(cinder, qos):
352     """
353     Deletes an QoS from OpenStack
354     :param cinder: the cinder client
355     :param qos: the qos domain object to delete
356     """
357     logger.info('Deleting QoS named - %s', qos.name)
358     cinder.qos_specs.delete(qos.id)