~0x44/nova/bug838466

« back to all changes in this revision

Viewing changes to nova/volume/manager.py

  • Committer: Vishvananda Ishaya
  • Date: 2010-10-25 07:45:33 UTC
  • mto: This revision was merged to the branch mainline in revision 391.
  • Revision ID: vishvananda@yahoo.com-20101025074533-yrb480c9o189zv9r
ISCSI Volume support

* Rewrite of Volume code to make VolumeManager more generic
* AoE vs. iscsi moved to driver layer
* Added db support for target ids
* Added code to re-export volumes on restart of VolumeManager
* Includes a script to create /dev/iscsi volumes on remote hosts

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
 
27
27
from twisted.internet import defer
28
28
 
 
29
from nova import context
29
30
from nova import exception
30
31
from nova import flags
31
32
from nova import manager
36
37
flags.DEFINE_string('storage_availability_zone',
37
38
                    'nova',
38
39
                    'availability zone of this service')
39
 
flags.DEFINE_string('volume_driver', 'nova.volume.driver.AOEDriver',
 
40
flags.DEFINE_string('volume_driver', 'nova.volume.driver.ISCSIDriver',
40
41
                    'Driver to use for volume creation')
41
 
flags.DEFINE_integer('num_shelves',
42
 
                    100,
43
 
                    'Number of vblade shelves')
44
 
flags.DEFINE_integer('blades_per_shelf',
45
 
                    16,
46
 
                    'Number of vblade blades per shelf')
47
 
 
48
 
 
49
 
class AOEManager(manager.Manager):
50
 
    """Manages Ata-Over_Ethernet volumes"""
 
42
 
 
43
 
 
44
class VolumeManager(manager.Manager):
 
45
    """Manages attachable block storage devices"""
51
46
    def __init__(self, volume_driver=None, *args, **kwargs):
52
47
        if not volume_driver:
53
48
            volume_driver = FLAGS.volume_driver
54
49
        self.driver = utils.import_object(volume_driver)
55
 
        super(AOEManager, self).__init__(*args, **kwargs)
 
50
        super(VolumeManager, self).__init__(*args, **kwargs)
 
51
        # NOTE(vish): Implementation specific db handling is done
 
52
        #             by the driver.
 
53
        self.driver.db = self.db
56
54
 
57
 
    def _ensure_blades(self, context):
58
 
        """Ensure that blades have been created in datastore"""
59
 
        total_blades = FLAGS.num_shelves * FLAGS.blades_per_shelf
60
 
        if self.db.export_device_count(context) >= total_blades:
61
 
            return
62
 
        for shelf_id in xrange(FLAGS.num_shelves):
63
 
            for blade_id in xrange(FLAGS.blades_per_shelf):
64
 
                dev = {'shelf_id': shelf_id, 'blade_id': blade_id}
65
 
                self.db.export_device_create_safe(context, dev)
 
55
    def init_host(self):
 
56
        """Do any initialization that needs to be run if this is a
 
57
           standalone service.
 
58
        """
 
59
        self.driver.check_for_setup_error()
 
60
        ctxt = context.get_admin_context()
 
61
        volumes = self.db.volume_get_all_by_host(ctxt, self.host)
 
62
        logging.debug("Re-exporting %s volumes", len(volumes))
 
63
        for volume in volumes:
 
64
            self.driver.ensure_export(context, volume)
66
65
 
67
66
    @defer.inlineCallbacks
68
67
    def create_volume(self, context, volume_id):
69
68
        """Creates and exports the volume"""
70
69
        context = context.elevated()
71
 
        logging.info("volume %s: creating", volume_id)
72
 
 
73
70
        volume_ref = self.db.volume_get(context, volume_id)
 
71
        logging.info("volume %s: creating", volume_ref['name'])
74
72
 
75
73
        self.db.volume_update(context,
76
74
                              volume_id,
77
75
                              {'host': self.host})
78
 
 
79
 
        size = volume_ref['size']
80
 
        logging.debug("volume %s: creating lv of size %sG", volume_id, size)
81
 
        yield self.driver.create_volume(volume_ref['ec2_id'], size)
82
 
 
83
 
        logging.debug("volume %s: allocating shelf & blade", volume_id)
84
 
        self._ensure_blades(context)
85
 
        rval = self.db.volume_allocate_shelf_and_blade(context, volume_id)
86
 
        (shelf_id, blade_id) = rval
87
 
 
88
 
        logging.debug("volume %s: exporting shelf %s & blade %s", volume_id,
89
 
                      shelf_id, blade_id)
90
 
 
91
 
        yield self.driver.create_export(volume_ref['ec2_id'],
92
 
                                        shelf_id,
93
 
                                        blade_id)
94
 
 
95
 
        logging.debug("volume %s: re-exporting all values", volume_id)
96
 
        yield self.driver.ensure_exports()
 
76
        # NOTE(vish): so we don't have to get volume from db again
 
77
        #             before passing it to the driver.
 
78
        volume_ref['host'] = self.host
 
79
 
 
80
        logging.debug("volume %s: creating lv of size %sG",
 
81
                      volume_ref['name'], volume_ref['size'])
 
82
        yield self.driver.create_volume(volume_ref)
 
83
 
 
84
        logging.debug("volume %s: creating export", volume_ref['name'])
 
85
        yield self.driver.create_export(context, volume_ref)
97
86
 
98
87
        now = datetime.datetime.utcnow()
99
88
        self.db.volume_update(context,
100
89
                              volume_ref['id'], {'status': 'available',
101
90
                                                 'launched_at': now})
102
 
        logging.debug("volume %s: created successfully", volume_id)
 
91
        logging.debug("volume %s: created successfully", volume_ref['name'])
103
92
        defer.returnValue(volume_id)
104
93
 
105
94
    @defer.inlineCallbacks
111
100
            raise exception.Error("Volume is still attached")
112
101
        if volume_ref['host'] != self.host:
113
102
            raise exception.Error("Volume is not local to this node")
114
 
        logging.debug("Deleting volume with id of: %s", volume_id)
115
 
        shelf_id, blade_id = self.db.volume_get_shelf_and_blade(context,
116
 
                                                                volume_id)
117
 
        yield self.driver.remove_export(volume_ref['ec2_id'],
118
 
                                        shelf_id,
119
 
                                        blade_id)
120
 
        yield self.driver.delete_volume(volume_ref['ec2_id'])
 
103
        logging.debug("volume %s: deleting", volume_ref['name'])
 
104
        yield self.driver.delete_volume(volume_ref)
121
105
        self.db.volume_destroy(context, volume_id)
 
106
        logging.debug("volume %s: deleted successfully", volume_ref['name'])
122
107
        defer.returnValue(True)
123
108
 
124
109
    @defer.inlineCallbacks
127
112
 
128
113
        Returns path to device.
129
114
        """
130
 
        context = context.elevated()
131
 
        volume_ref = self.db.volume_get(context, volume_id)
132
 
        yield self.driver.discover_volume(volume_ref['ec2_id'])
133
 
        shelf_id, blade_id = self.db.volume_get_shelf_and_blade(context,
134
 
                                                                volume_id)
135
 
        defer.returnValue("/dev/etherd/e%s.%s" % (shelf_id, blade_id))
 
115
        context = context.admin()
 
116
        volume_ref = self.db.volume_get(context, volume_id)
 
117
        if volume_ref['host'] == self.host:
 
118
            # NOTE(vish): No need to discover local volumes.
 
119
            path = yield self.driver.local_path(volume_ref)
 
120
        else:
 
121
            path = yield self.driver.discover_volume(volume_ref)
 
122
        defer.returnValue(path)
 
123
 
 
124
    @defer.inlineCallbacks
 
125
    def remove_compute_volume(self, context, volume_id):
 
126
        """Remove remote volume on compute host """
 
127
        context = context.admin()
 
128
        volume_ref = self.db.volume_get(context, volume_id)
 
129
        if volume_ref['host'] == self.host:
 
130
            # NOTE(vish): No need to undiscover local volumes.
 
131
            defer.returnValue(True)
 
132
        else:
 
133
            yield self.driver.undiscover_volume(volume_ref)
 
134