Added ability to add a floating IP to an existing instance
[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                 attachments=volume.attachments)
67
68
69 def __get_os_volume_by_id(cinder, volume_id):
70     """
71     Returns an OpenStack volume object for a given name
72     :param cinder: the Cinder client
73     :param volume_id: the volume ID to lookup
74     :return: the SNAPS-OO Domain Volume object or None
75     """
76     return cinder.volumes.get(volume_id)
77
78
79 def get_volume_by_id(cinder, volume_id):
80     """
81     Returns an OpenStack volume object for a given name
82     :param cinder: the Cinder client
83     :param volume_id: the volume ID to lookup
84     :return: the SNAPS-OO Domain Volume object or None
85     """
86     volume = __get_os_volume_by_id(cinder, volume_id)
87     return Volume(
88         name=volume.name, volume_id=volume.id, description=volume.description,
89         size=volume.size, vol_type=volume.volume_type,
90         availability_zone=volume.availability_zone,
91         multi_attach=volume.multiattach, attachments=volume.attachments)
92
93
94 def get_volume_status(cinder, volume):
95     """
96     Returns a new OpenStack Volume object for a given OpenStack volume object
97     :param cinder: the Cinder client
98     :param volume: the domain Volume object
99     :return: the OpenStack Volume object
100     """
101     os_volume = cinder.volumes.get(volume.id)
102     return os_volume.status
103
104
105 def create_volume(cinder, volume_settings):
106     """
107     Creates and returns OpenStack volume object with an external URL
108     :param cinder: the cinder client
109     :param volume_settings: the volume settings object
110     :return: the OpenStack volume object
111     :raise Exception if using a file and it cannot be found
112     """
113     volume = cinder.volumes.create(
114         name=volume_settings.name, description=volume_settings.description,
115         size=volume_settings.size, imageRef=volume_settings.image_name,
116         volume_type=volume_settings.type_name,
117         availability_zone=volume_settings.availability_zone,
118         multiattach=volume_settings.multi_attach)
119
120     return Volume(
121         name=volume.name, volume_id=volume.id,
122         description=volume.description,
123         size=volume.size, vol_type=volume.volume_type,
124         availability_zone=volume.availability_zone,
125         multi_attach=volume.multiattach, attachments=volume.attachments)
126
127
128 def delete_volume(cinder, volume):
129     """
130     Deletes an volume from OpenStack
131     :param cinder: the cinder client
132     :param volume: the volume to delete
133     """
134     logger.info('Deleting volume named - %s', volume.name)
135     return cinder.volumes.delete(volume.id)
136
137
138 def get_volume_type(cinder, volume_type_name=None, volume_type_settings=None):
139     """
140     Returns an OpenStack volume type object for a given name
141     :param cinder: the Cinder client
142     :param volume_type_name: the volume type name to lookup
143     :param volume_type_settings: the volume type settings used for lookups
144     :return: the volume type object or None
145     """
146     if not volume_type_name and not volume_type_settings:
147         return None
148
149     if volume_type_settings:
150         volume_type_name = volume_type_settings.name
151
152     volume_types = cinder.volume_types.list()
153     for vol_type in volume_types:
154         if vol_type.name == volume_type_name:
155             encryption = get_volume_encryption_by_type(cinder, vol_type)
156             return VolumeType(vol_type.name, vol_type.id, vol_type.is_public,
157                               encryption, None)
158
159
160 def __get_os_volume_type_by_id(cinder, volume_type_id):
161     """
162     Returns an OpenStack volume type object for a given name
163     :param cinder: the Cinder client
164     :param volume_type_id: the volume_type ID to lookup
165     :return: the SNAPS-OO Domain Volume object or None
166     """
167     try:
168         return cinder.volume_types.get(volume_type_id)
169     except NotFound:
170         logger.info('Volume with ID [%s] does not exist',
171                     volume_type_id)
172
173
174 def get_volume_type_by_id(cinder, volume_type_id):
175     """
176     Returns an OpenStack volume type object for a given name
177     :param cinder: the Cinder client
178     :param volume_type_id: the volume_type ID to lookup
179     :return: the SNAPS-OO Domain Volume object or None
180     """
181     os_vol_type = __get_os_volume_type_by_id(cinder, volume_type_id)
182     if os_vol_type:
183         temp_vol_type = VolumeType(os_vol_type.name, os_vol_type.id,
184                                    os_vol_type.is_public, None, None)
185         encryption = get_volume_encryption_by_type(cinder, temp_vol_type)
186
187         qos_spec = None
188         if os_vol_type.qos_specs_id:
189             qos_spec = get_qos_by_id(cinder, os_vol_type.qos_specs_id)
190
191         return VolumeType(os_vol_type.name, os_vol_type.id,
192                           os_vol_type.is_public, encryption, qos_spec)
193
194
195 def create_volume_type(cinder, type_settings):
196     """
197     Creates and returns OpenStack volume type object with an external URL
198     :param cinder: the cinder client
199     :param type_settings: the volume type settings object
200     :return: the volume type domain object
201     :raise Exception if using a file and it cannot be found
202     """
203     vol_type = cinder.volume_types.create(
204         type_settings.name, type_settings.description,
205         type_settings.public)
206
207     vol_encryption = None
208     if type_settings.encryption:
209         try:
210             vol_encryption = create_volume_encryption(
211                 cinder, vol_type, type_settings.encryption)
212         except Exception as e:
213             logger.warn('Error creating volume encryption - %s', e)
214
215     qos_spec = None
216     if type_settings.qos_spec_name:
217         try:
218             qos_spec = get_qos(cinder, qos_name=type_settings.qos_spec_name)
219             cinder.qos_specs.associate(qos_spec, vol_type.id)
220         except NotFound as e:
221             logger.warn('Unable to locate qos_spec named %s - %s',
222                         type_settings.qos_spec_name, e)
223
224     return VolumeType(vol_type.name, vol_type.id, vol_type.is_public,
225                       vol_encryption, qos_spec)
226
227
228 def delete_volume_type(cinder, vol_type):
229     """
230     Deletes an volume from OpenStack
231     :param cinder: the cinder client
232     :param vol_type: the VolumeType domain object
233     """
234     logger.info('Deleting volume named - %s', vol_type.name)
235     cinder.volume_types.delete(vol_type.id)
236
237
238 def get_volume_encryption_by_type(cinder, volume_type):
239     """
240     Returns an OpenStack volume type object for a given name
241     :param cinder: the Cinder client
242     :param volume_type: the VolumeType domain object
243     :return: the VolumeEncryption domain object or None
244     """
245     os_vol_type = __get_os_volume_type_by_id(cinder, volume_type.id)
246     encryption = cinder.volume_encryption_types.get(os_vol_type)
247     if hasattr(encryption, 'encryption_id'):
248         cipher = None
249         if hasattr(encryption, 'cipher'):
250             cipher = encryption.cipher
251         key_size = None
252         if hasattr(encryption, 'key_size'):
253             key_size = encryption.key_size
254         return VolumeTypeEncryption(
255             encryption.encryption_id, encryption.volume_type_id,
256             encryption.control_location, encryption.provider, cipher, key_size)
257
258
259 def create_volume_encryption(cinder, volume_type, encryption_settings):
260     """
261     Creates and returns OpenStack volume type object with an external URL
262     :param cinder: the cinder client
263     :param volume_type: the VolumeType object to associate the encryption
264     :param encryption_settings: the volume type encryption settings object
265     :return: the VolumeTypeEncryption domain object
266     """
267     specs = {'name': encryption_settings.name,
268              'provider': encryption_settings.provider_class}
269     if encryption_settings.key_size:
270         specs['key_size'] = encryption_settings.key_size
271     if encryption_settings.provider_class:
272         specs['provider_class'] = encryption_settings.provider_class
273     if encryption_settings.control_location:
274         specs['control_location'] = encryption_settings.control_location.value
275     if encryption_settings.cipher:
276         specs['cipher'] = encryption_settings.cipher
277
278     encryption = cinder.volume_encryption_types.create(volume_type.id, specs)
279
280     cipher = None
281     if hasattr(encryption, 'cipher'):
282         cipher = encryption.cipher
283     key_size = None
284     if hasattr(encryption, 'key_size'):
285         key_size = encryption.key_size
286     return VolumeTypeEncryption(
287         encryption.encryption_id, encryption.volume_type_id,
288         encryption.control_location, encryption.provider, cipher, key_size)
289
290
291 def delete_volume_type_encryption(cinder, vol_type):
292     """
293     Deletes an volume from OpenStack
294     :param cinder: the cinder client
295     :param vol_type: the associated VolumeType domain object
296     """
297     logger.info('Deleting volume encryption for volume type - %s',
298                 vol_type.name)
299     os_vol_type = __get_os_volume_type_by_id(cinder, vol_type.id)
300     cinder.volume_encryption_types.delete(os_vol_type)
301
302
303 def __get_os_qos(cinder, qos_name=None, qos_settings=None):
304     """
305     Returns an OpenStack QoS object for a given name
306     :param cinder: the Cinder client
307     :param qos_name: the qos name to lookup
308     :param qos_settings: the qos settings used for lookups
309     :return: the qos object or None
310     """
311     if not qos_name and not qos_settings:
312         return None
313
314     if qos_settings:
315         qos_name = qos_settings.name
316
317     qoss = cinder.qos_specs.list()
318     for qos in qoss:
319         if qos.name == qos_name:
320             return qos
321
322
323 def get_qos(cinder, qos_name=None, qos_settings=None):
324     """
325     Returns an OpenStack QoS object for a given name
326     :param cinder: the Cinder client
327     :param qos_name: the qos name to lookup
328     :param qos_settings: the qos settings used for lookups
329     :return: the qos object or None
330     """
331     os_qos = __get_os_qos(cinder, qos_name, qos_settings)
332     if os_qos:
333         return QoSSpec(name=os_qos.name, spec_id=os_qos.id,
334                        consumer=os_qos.consumer)
335
336
337 def get_qos_by_id(cinder, qos_id):
338     """
339     Returns an OpenStack qos object for a given name
340     :param cinder: the Cinder client
341     :param qos_id: the qos ID to lookup
342     :return: the SNAPS-OO Domain Volume object or None
343     """
344     qos = cinder.qos_specs.get(qos_id)
345     return QoSSpec(name=qos.name, spec_id=qos.id, consumer=qos.consumer)
346
347
348 def create_qos(cinder, qos_settings):
349     """
350     Creates and returns OpenStack qos object with an external URL
351     :param cinder: the cinder client
352     :param qos_settings: the qos settings object
353     :return: the qos domain object
354     :raise Exception if using a file and it cannot be found
355     """
356     specs = qos_settings.specs
357     specs['consumer'] = qos_settings.consumer.value
358     qos = cinder.qos_specs.create(qos_settings.name, qos_settings.specs)
359     return QoSSpec(name=qos.name, spec_id=qos.id, consumer=qos.consumer)
360
361
362 def delete_qos(cinder, qos):
363     """
364     Deletes an QoS from OpenStack
365     :param cinder: the cinder client
366     :param qos: the qos domain object to delete
367     """
368     logger.info('Deleting QoS named - %s', qos.name)
369     cinder.qos_specs.delete(qos.id)