~ubuntu-cloud-archive/ubuntu/precise/nova/trunk

« back to all changes in this revision

Viewing changes to nova/volume/driver.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Chuck Short, Soren Hansen
  • Date: 2012-09-07 17:49:53 UTC
  • mfrom: (1.1.61)
  • Revision ID: package-import@ubuntu.com-20120907174953-oapuvix1jxm830he
Tags: 2012.2~rc1~20120907.15996-0ubuntu1
[ Chuck Short ]
* New upstream release.
* debian/nova-common.postinst: Drop nova_sudoers permission changing
  since we do it in the debian/rules. (LP: #995285)

[ Soren Hansen ]
* Update debian/watch to account for symbolically named tarballs and
  use newer URL.
* Fix Launchpad URLs in debian/watch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
 
21
21
"""
22
22
 
 
23
import os
23
24
import time
24
25
 
25
26
from nova import exception
103
104
                                run_as_root=True)
104
105
        volume_groups = out.split()
105
106
        if not FLAGS.volume_group in volume_groups:
106
 
            raise exception.NovaException(_("volume group %s doesn't exist")
 
107
            exception_message = (_("volume group %s doesn't exist")
107
108
                                  % FLAGS.volume_group)
 
109
            raise exception.VolumeBackendAPIException(data=exception_message)
108
110
 
109
111
    def _create_volume(self, volume_name, sizestr):
110
112
        self._try_execute('lvcreate', '-L', sizestr, '-n',
111
113
                          volume_name, FLAGS.volume_group, run_as_root=True)
112
114
 
113
115
    def _copy_volume(self, srcstr, deststr, size_in_g):
 
116
        # Use O_DIRECT to avoid thrashing the system buffer cache
 
117
        direct_flags = ('iflag=direct', 'oflag=direct')
 
118
 
 
119
        # Check whether O_DIRECT is supported
 
120
        try:
 
121
            self._execute('dd', 'count=0', 'if=%s' % srcstr, 'of=%s' % deststr,
 
122
                          *direct_flags, run_as_root=True)
 
123
        except exception.ProcessExecutionError:
 
124
            direct_flags = ()
 
125
 
 
126
        # Perform the copy
114
127
        self._execute('dd', 'if=%s' % srcstr, 'of=%s' % deststr,
115
128
                      'count=%d' % (size_in_g * 1024), 'bs=1M',
116
 
                      run_as_root=True)
 
129
                      *direct_flags, run_as_root=True)
117
130
 
118
131
    def _volume_not_present(self, volume_name):
119
132
        path_name = '%s/%s' % (FLAGS.volume_group, volume_name)
227
240
        """Disallow connection from connector"""
228
241
        raise NotImplementedError()
229
242
 
 
243
    def attach_volume(self, context, volume_id, instance_uuid, mountpoint):
 
244
        """ Callback for volume attached to instance."""
 
245
        pass
 
246
 
 
247
    def detach_volume(self, context, volume_id):
 
248
        """ Callback for volume detached."""
 
249
        pass
 
250
 
230
251
    def get_volume_stats(self, refresh=False):
231
252
        """Return the current state of the volume service. If 'refresh' is
232
253
           True, run the update first."""
263
284
 
264
285
    def ensure_export(self, context, volume):
265
286
        """Synchronously recreates an export for a logical volume."""
266
 
        try:
267
 
            iscsi_target = self.db.volume_get_iscsi_target_num(context,
268
 
                                                           volume['id'])
269
 
        except exception.NotFound:
270
 
            LOG.info(_("Skipping ensure_export. No iscsi_target "
271
 
                       "provisioned for volume: %s"), volume['id'])
272
 
            return
 
287
        # NOTE(jdg): tgtadm doesn't use the iscsi_targets table
 
288
        # TODO(jdg): In the future move all of the dependent stuff into the
 
289
        # cooresponding target admin class
 
290
        if not isinstance(self.tgtadm, iscsi.TgtAdm):
 
291
            try:
 
292
                iscsi_target = self.db.volume_get_iscsi_target_num(context,
 
293
                                                               volume['id'])
 
294
            except exception.NotFound:
 
295
                LOG.info(_("Skipping ensure_export. No iscsi_target "
 
296
                           "provisioned for volume: %s"), volume['id'])
 
297
                return
 
298
        else:
 
299
            iscsi_target = 1  # dummy value when using TgtAdm
273
300
 
274
301
        iscsi_name = "%s%s" % (FLAGS.iscsi_target_prefix, volume['name'])
275
302
        volume_path = "/dev/%s/%s" % (FLAGS.volume_group, volume['name'])
276
303
 
 
304
        # NOTE(jdg): For TgtAdm case iscsi_name is the ONLY param we need
 
305
        # should clean this all up at some point in the future
277
306
        self.tgtadm.create_iscsi_target(iscsi_name, iscsi_target,
278
 
                0, volume_path, check_exit_code=False)
 
307
                                        0, volume_path,
 
308
                                        check_exit_code=False)
279
309
 
280
310
    def _ensure_iscsi_targets(self, context, host):
281
311
        """Ensure that target ids have been created in datastore."""
282
 
        host_iscsi_targets = self.db.iscsi_target_count_by_host(context, host)
283
 
        if host_iscsi_targets >= FLAGS.iscsi_num_targets:
284
 
            return
285
 
        # NOTE(vish): Target ids start at 1, not 0.
286
 
        for target_num in xrange(1, FLAGS.iscsi_num_targets + 1):
287
 
            target = {'host': host, 'target_num': target_num}
288
 
            self.db.iscsi_target_create_safe(context, target)
 
312
        # NOTE(jdg): tgtadm doesn't use the iscsi_targets table
 
313
        # TODO(jdg): In the future move all of the dependent stuff into the
 
314
        # cooresponding target admin class
 
315
        if not isinstance(self.tgtadm, iscsi.TgtAdm):
 
316
            host_iscsi_targets = self.db.iscsi_target_count_by_host(context,
 
317
                                                                    host)
 
318
            if host_iscsi_targets >= FLAGS.iscsi_num_targets:
 
319
                return
 
320
 
 
321
            # NOTE(vish): Target ids start at 1, not 0.
 
322
            for target_num in xrange(1, FLAGS.iscsi_num_targets + 1):
 
323
                target = {'host': host, 'target_num': target_num}
 
324
                self.db.iscsi_target_create_safe(context, target)
289
325
 
290
326
    def create_export(self, context, volume):
291
327
        """Creates an export for a logical volume."""
292
 
        self._ensure_iscsi_targets(context, volume['host'])
293
 
        iscsi_target = self.db.volume_allocate_iscsi_target(context,
294
 
                                                      volume['id'],
295
 
                                                      volume['host'])
 
328
        #BOOKMARK(jdg)
 
329
 
296
330
        iscsi_name = "%s%s" % (FLAGS.iscsi_target_prefix, volume['name'])
297
331
        volume_path = "/dev/%s/%s" % (FLAGS.volume_group, volume['name'])
298
332
 
299
 
        self.tgtadm.create_iscsi_target(iscsi_name, iscsi_target,
300
 
                0, volume_path)
301
 
 
302
333
        model_update = {}
303
 
        if FLAGS.iscsi_helper == 'tgtadm':
304
 
            lun = 1
305
 
        else:
 
334
 
 
335
        # TODO(jdg): In the future move all of the dependent stuff into the
 
336
        # cooresponding target admin class
 
337
        if not isinstance(self.tgtadm, iscsi.TgtAdm):
306
338
            lun = 0
 
339
            self._ensure_iscsi_targets(context, volume['host'])
 
340
            iscsi_target = self.db.volume_allocate_iscsi_target(context,
 
341
                                                                volume['id'],
 
342
                                                                volume['host'])
 
343
        else:
 
344
            lun = 1  # For tgtadm the controller is lun 0, dev starts at lun 1
 
345
            iscsi_target = 0  # NOTE(jdg): Not used by tgtadm
 
346
 
 
347
        # NOTE(jdg): For TgtAdm case iscsi_name is the ONLY param we need
 
348
        # should clean this all up at some point in the future
 
349
        tid = self.tgtadm.create_iscsi_target(iscsi_name,
 
350
                                              iscsi_target,
 
351
                                              0,
 
352
                                              volume_path)
307
353
        model_update['provider_location'] = _iscsi_location(
308
 
            FLAGS.iscsi_ip_address, iscsi_target, iscsi_name, lun)
 
354
            FLAGS.iscsi_ip_address, tid, iscsi_name, lun)
309
355
        return model_update
310
356
 
311
357
    def remove_export(self, context, volume):
312
358
        """Removes an export for a logical volume."""
313
 
        try:
314
 
            iscsi_target = self.db.volume_get_iscsi_target_num(context,
315
 
                                                           volume['id'])
316
 
        except exception.NotFound:
317
 
            LOG.info(_("Skipping remove_export. No iscsi_target "
318
 
                       "provisioned for volume: %s"), volume['id'])
319
 
            return
 
359
        #BOOKMARK jdg
 
360
        location = volume['provider_location'].split(' ')
 
361
        iqn = location[1]
 
362
        if 'iqn' not in iqn:
 
363
            LOG.warning(_("Jacked... didn't get an iqn"))
 
364
            return
 
365
 
 
366
        # NOTE(jdg): tgtadm doesn't use the iscsi_targets table
 
367
        # TODO(jdg): In the future move all of the dependent stuff into the
 
368
        # cooresponding target admin class
 
369
        if not isinstance(self.tgtadm, iscsi.TgtAdm):
 
370
            try:
 
371
                iscsi_target = self.db.volume_get_iscsi_target_num(context,
 
372
                                                               volume['id'])
 
373
            except exception.NotFound:
 
374
                LOG.info(_("Skipping remove_export. No iscsi_target "
 
375
                           "provisioned for volume: %s"), volume['id'])
 
376
            return
 
377
        else:
 
378
            iscsi_target = 0
320
379
 
321
380
        try:
322
381
            # ietadm show will exit with an error
323
382
            # this export has already been removed
324
 
            self.tgtadm.show_target(iscsi_target)
 
383
            self.tgtadm.show_target(iscsi_target, iqn=iqn)
325
384
        except Exception as e:
326
385
            LOG.info(_("Skipping remove_export. No iscsi_target "
327
386
                       "is presently exported for volume: %s"), volume['id'])
379
438
            location = self._do_iscsi_discovery(volume)
380
439
 
381
440
            if not location:
382
 
                raise exception.NovaException(_("Could not find iSCSI export "
383
 
                                        " for volume %s") %
384
 
                                      (volume['name']))
 
441
                raise exception.InvalidVolume(_("Could not find iSCSI export "
 
442
                                                " for volume %s") %
 
443
                                              (volume['name']))
385
444
 
386
445
            LOG.debug(_("ISCSI Discovery: Found %s") % (location))
387
446
            properties['target_discovered'] = True
453
512
 
454
513
    def check_for_export(self, context, volume_id):
455
514
        """Make sure volume is exported."""
456
 
 
457
 
        tid = self.db.volume_get_iscsi_target_num(context, volume_id)
 
515
        vol_uuid_file = 'volume-%s' % volume_id
 
516
        volume_path = os.path.join(FLAGS.volumes_dir, vol_uuid_file)
 
517
        if os.path.isfile(volume_path):
 
518
            iqn = '%s%s' % (FLAGS.iscsi_target_prefix,
 
519
                            vol_uuid_file)
 
520
        else:
 
521
            raise exception.PersistentVolumeFileNotFound(volume_id=volume_id)
 
522
 
 
523
        # TODO(jdg): In the future move all of the dependent stuff into the
 
524
        # cooresponding target admin class
 
525
        if not isinstance(self.tgtadm, iscsi.TgtAdm):
 
526
            tid = self.db.volume_get_iscsi_target_num(context, volume_id)
 
527
        else:
 
528
            tid = 0
 
529
 
458
530
        try:
459
 
            self.tgtadm.show_target(tid)
 
531
            self.tgtadm.show_target(tid, iqn=iqn)
460
532
        except exception.ProcessExecutionError, e:
461
533
            # Instances remount read-only in this case.
462
534
            # /etc/init.d/iscsitarget restart and rebooting nova-volume
500
572
        (stdout, stderr) = self._execute('rados', 'lspools')
501
573
        pools = stdout.split("\n")
502
574
        if not FLAGS.rbd_pool in pools:
503
 
            raise exception.NovaException(_("rbd has no pool %s") %
504
 
                                  FLAGS.rbd_pool)
 
575
            exception_message = (_("rbd has no pool %s") %
 
576
                                    FLAGS.rbd_pool)
 
577
            raise exception.VolumeBackendAPIException(data=exception_message)
505
578
 
506
579
    def create_volume(self, volume):
507
580
        """Creates a logical volume."""
578
651
            #  use it and just check if 'running' is in the output.
579
652
            (out, err) = self._execute('collie', 'cluster', 'info')
580
653
            if not 'running' in out.split():
581
 
                msg = _("Sheepdog is not working: %s") % out
582
 
                raise exception.NovaException(msg)
 
654
                exception_message = _("Sheepdog is not working: %s") % out
 
655
                raise exception.VolumeBackendAPIException(
 
656
                                                data=exception_message)
 
657
 
583
658
        except exception.ProcessExecutionError:
584
 
            raise exception.NovaException(_("Sheepdog is not working"))
 
659
            exception_message = _("Sheepdog is not working")
 
660
            raise exception.NovaException(data=exception_message)
585
661
 
586
662
    def create_volume(self, volume):
587
663
        """Creates a sheepdog volume"""