38
39
from cinder.volume import volume_types
40
41
volume_host_opt = cfg.BoolOpt('snapshot_same_host',
42
help='Create volume from snapshot at the host where snapshot resides')
43
help='Create volume from snapshot at the host '
44
'where snapshot resides')
44
46
FLAGS = flags.FLAGS
45
47
FLAGS.register_opt(volume_host_opt)
85
87
super(API, self).__init__(db_driver)
87
89
def create(self, context, size, name, description, snapshot=None,
88
image_id=None, volume_type=None, metadata=None,
89
availability_zone=None):
90
image_id=None, volume_type=None, metadata=None,
91
availability_zone=None, source_volume=None):
93
if ((snapshot is not None) and (source_volume is not None)):
94
msg = (_("May specify either snapshot, "
95
"or src volume but not both!"))
96
raise exception.InvalidInput(reason=msg)
90
98
check_policy(context, 'create')
91
99
if snapshot is not None:
92
100
if snapshot['status'] != "available":
100
108
snapshot_id = None
110
if source_volume is not None:
111
if source_volume['status'] == "error":
112
msg = _("Unable to clone volumes that are in an error state")
113
raise exception.InvalidSourceVolume(reason=msg)
115
size = source_volume['size']
117
if size < source_volume['size']:
118
msg = _("Clones currently must be "
119
">= original volume size.")
120
raise exception.InvalidInput(reason=msg)
121
source_volid = source_volume['id']
128
except (ValueError, TypeError):
108
131
# tolerate size as stringified int
143
166
elif 'volumes' in overs:
144
167
consumed = _consumed('volumes')
145
168
LOG.warn(_("Quota exceeded for %(pid)s, tried to create "
146
"volume (%(consumed)d volumes already consumed)")
169
"volume (%(consumed)d volumes "
170
"already consumed)") % locals())
148
171
raise exception.VolumeLimitExceeded(allowed=quotas['volumes'])
150
173
if availability_zone is None:
151
174
availability_zone = FLAGS.storage_availability_zone
176
if not volume_type and not source_volume:
154
177
volume_type = volume_types.get_default_volume_type()
156
volume_type_id = volume_type.get('id')
179
if not volume_type and source_volume:
180
volume_type_id = source_volume['volume_type_id']
182
volume_type_id = volume_type.get('id')
160
'user_id': context.user_id,
161
'project_id': context.project_id,
162
'snapshot_id': snapshot_id,
163
'availability_zone': availability_zone,
164
'status': "creating",
165
'attach_status': "detached",
166
'display_name': name,
167
'display_description': description,
168
'volume_type_id': volume_type_id,
169
'metadata': metadata,
184
options = {'size': size,
185
'user_id': context.user_id,
186
'project_id': context.project_id,
187
'snapshot_id': snapshot_id,
188
'availability_zone': availability_zone,
189
'status': "creating",
190
'attach_status': "detached",
191
'display_name': name,
192
'display_description': description,
193
'volume_type_id': volume_type_id,
194
'metadata': metadata,
195
'source_volid': source_volid}
173
198
volume = self.db.volume_create(context, options)
180
205
QUOTAS.rollback(context, reservations)
183
'volume_properties': options,
184
'volume_type': volume_type,
185
'volume_id': volume['id'],
186
'snapshot_id': volume['snapshot_id'],
207
request_spec = {'volume_properties': options,
208
'volume_type': volume_type,
209
'volume_id': volume['id'],
210
'snapshot_id': volume['snapshot_id'],
211
'image_id': image_id,
212
'source_volid': volume['source_volid']}
190
214
filter_properties = {}
199
223
# If snapshot_id is set, make the call create volume directly to
200
224
# the volume host where the snapshot resides instead of passing it
201
225
# through the scheduler. So snapshot can be copy to new volume.
227
source_volid = request_spec['source_volid']
202
228
volume_id = request_spec['volume_id']
203
229
snapshot_id = request_spec['snapshot_id']
204
230
image_id = request_spec['image_id']
206
232
if snapshot_id and FLAGS.snapshot_same_host:
207
233
snapshot_ref = self.db.snapshot_get(context, snapshot_id)
208
src_volume_ref = self.db.volume_get(context,
209
snapshot_ref['volume_id'])
210
volume_ref = self.db.volume_get(context,
212
# bypass scheduler and send request directly to volume
213
self.volume_rpcapi.create_volume(context,
215
src_volume_ref['host'],
234
source_volume_ref = self.db.volume_get(context,
235
snapshot_ref['volume_id'])
236
now = timeutils.utcnow()
237
values = {'host': source_volume_ref['host'], 'scheduled_at': now}
238
volume_ref = self.db.volume_update(context, volume_id, values)
240
# bypass scheduler and send request directly to volume
241
self.volume_rpcapi.create_volume(context,
247
source_volume_ref = self.db.volume_get(context,
249
now = timeutils.utcnow()
250
values = {'host': source_volume_ref['host'], 'scheduled_at': now}
251
volume_ref = self.db.volume_update(context, volume_id, values)
253
# bypass scheduler and send request directly to volume
254
self.volume_rpcapi.create_volume(context,
219
self.scheduler_rpcapi.create_volume(context,
224
request_spec=request_spec,
225
filter_properties=filter_properties)
261
self.scheduler_rpcapi.create_volume(
267
request_spec=request_spec,
268
filter_properties=filter_properties)
227
270
@wrap_check_policy
228
271
def delete(self, context, volume, force=False):
267
310
check_policy(context, 'get', volume)
270
def get_all(self, context, search_opts=None):
313
def get_all(self, context, marker=None, limit=None, sort_key='created_at',
314
sort_dir='desc', filters={}):
271
315
check_policy(context, 'get_all')
273
if search_opts is None:
276
if (context.is_admin and 'all_tenants' in search_opts):
317
if (context.is_admin and 'all_tenants' in filters):
277
318
# Need to remove all_tenants to pass the filtering below.
278
del search_opts['all_tenants']
279
volumes = self.db.volume_get_all(context)
319
del filters['all_tenants']
320
volumes = self.db.volume_get_all(context, marker, limit, sort_key,
281
323
volumes = self.db.volume_get_all_by_project(context,
284
LOG.debug(_("Searching by: %s") % str(search_opts))
329
LOG.debug(_("Searching by: %s") % str(filters))
286
331
def _check_metadata_match(volume, searchdict):
287
332
volume_metadata = {}
319
365
rv = self.db.snapshot_get(context, snapshot_id)
320
366
return dict(rv.iteritems())
368
def get_volume(self, context, volume_id):
369
check_policy(context, 'get_volume')
370
rv = self.db.volume_get(context, volume_id)
371
return dict(rv.iteritems())
322
373
def get_all_snapshots(self, context, search_opts=None):
323
374
check_policy(context, 'get_all_snapshots')
384
435
@wrap_check_policy
385
436
def attach(self, context, volume, instance_uuid, mountpoint):
386
437
return self.volume_rpcapi.attach_volume(context,
391
442
@wrap_check_policy
392
443
def detach(self, context, volume):
395
446
@wrap_check_policy
396
447
def initialize_connection(self, context, volume, connector):
397
448
return self.volume_rpcapi.initialize_connection(context,
401
452
@wrap_check_policy
402
453
def terminate_connection(self, context, volume, connector, force=False):
403
454
self.unreserve_volume(context, volume)
404
455
return self.volume_rpcapi.terminate_connection(context,
409
460
def _create_snapshot(self, context, volume, name, description,
414
465
msg = _("must be available")
415
466
raise exception.InvalidVolume(reason=msg)
418
'volume_id': volume['id'],
419
'user_id': context.user_id,
420
'project_id': context.project_id,
421
'status': "creating",
423
'volume_size': volume['size'],
424
'display_name': name,
425
'display_description': description}
468
options = {'volume_id': volume['id'],
469
'user_id': context.user_id,
470
'project_id': context.project_id,
471
'status': "creating",
473
'volume_size': volume['size'],
474
'display_name': name,
475
'display_description': description}
427
477
snapshot = self.db.snapshot_create(context, options)
428
478
self.volume_rpcapi.create_snapshot(context, volume, snapshot)
488
538
return i['value']
542
def get_volume_image_metadata(self, context, volume):
543
db_data = self.db.volume_glance_metadata_get(context, volume['id'])
545
(meta_entry.key, meta_entry.value) for meta_entry in db_data
491
548
def _check_volume_availability(self, context, volume, force):
492
549
"""Check if the volume can be used."""
493
550
if volume['status'] not in ['available', 'in-use']:
505
562
recv_metadata = self.image_service.create(context, metadata)
506
563
self.update(context, volume, {'status': 'uploading'})
507
self.volume_rpcapi.copy_volume_to_image(context, volume,
564
self.volume_rpcapi.copy_volume_to_image(context,
510
568
response = {"id": volume['id'],
511
"updated_at": volume['updated_at'],
512
"status": 'uploading',
513
"display_description": volume['display_description'],
514
"size": volume['size'],
515
"volume_type": volume['volume_type'],
516
"image_id": recv_metadata['id'],
517
"container_format": recv_metadata['container_format'],
518
"disk_format": recv_metadata['disk_format'],
519
"image_name": recv_metadata.get('name', None)
569
"updated_at": volume['updated_at'],
570
"status": 'uploading',
571
"display_description": volume['display_description'],
572
"size": volume['size'],
573
"volume_type": volume['volume_type'],
574
"image_id": recv_metadata['id'],
575
"container_format": recv_metadata['container_format'],
576
"disk_format": recv_metadata['disk_format'],
577
"image_name": recv_metadata.get('name', None)}