~rlane/nova/ldapimprovements

« back to all changes in this revision

Viewing changes to nova/volume/manager.py

  • Committer: Ryan Lane
  • Date: 2010-11-24 15:46:32 UTC
  • mfrom: (382.48.1 trunk)
  • Revision ID: laner@controller-20101124154632-zh7kwjuyyd02a2lh
MergeĀ fromĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
16
#    License for the specific language governing permissions and limitations
17
17
#    under the License.
18
 
 
19
18
"""
20
 
Volume manager manages creating, attaching, detaching, and
21
 
destroying persistent storage volumes, ala EBS.
 
19
Volume manager manages creating, attaching, detaching, and persistent storage.
 
20
 
 
21
Persistant storage volumes keep their state independent of instances.  You can
 
22
attach to an instance, terminate the instance, spawn a new instance (even
 
23
one from a different image) and re-attach the volume with the same data
 
24
intact.
 
25
 
 
26
**Related Flags**
 
27
 
 
28
:volume_topic:  What :mod:`rpc` topic to listen to (default: `volume`).
 
29
:volume_manager:  The module name of a class derived from
 
30
                  :class:`manager.Manager` (default:
 
31
                  :class:`nova.volume.manager.AOEManager`).
 
32
:storage_availability_zone:  Defaults to `nova`.
 
33
:volume_driver:  Used by :class:`AOEManager`.  Defaults to
 
34
                 :class:`nova.volume.driver.AOEDriver`.
 
35
:num_shelves:  Number of shelves for AoE (default: 100).
 
36
:num_blades:  Number of vblades per shelf to allocate AoE storage from
 
37
              (default: 16).
 
38
:volume_group:  Name of the group that will contain exported volumes (default:
 
39
                `nova-volumes`)
 
40
:aoe_eth_dev:  Device name the volumes will be exported on (default: `eth0`).
 
41
:num_shell_tries:  Number of times to attempt to run AoE commands (default: 3)
 
42
 
22
43
"""
23
44
 
24
45
import logging
26
47
 
27
48
from twisted.internet import defer
28
49
 
 
50
from nova import context
29
51
from nova import exception
30
52
from nova import flags
31
53
from nova import manager
36
58
flags.DEFINE_string('storage_availability_zone',
37
59
                    'nova',
38
60
                    'availability zone of this service')
39
 
flags.DEFINE_string('volume_driver', 'nova.volume.driver.AOEDriver',
 
61
flags.DEFINE_string('volume_driver', 'nova.volume.driver.ISCSIDriver',
40
62
                    '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"""
 
63
flags.DEFINE_boolean('use_local_volumes', True,
 
64
                     'if True, will not discover local volumes')
 
65
 
 
66
 
 
67
class VolumeManager(manager.Manager):
 
68
    """Manages attachable block storage devices."""
51
69
    def __init__(self, volume_driver=None, *args, **kwargs):
 
70
        """Load the driver from the one specified in args, or from flags."""
52
71
        if not volume_driver:
53
72
            volume_driver = FLAGS.volume_driver
54
73
        self.driver = utils.import_object(volume_driver)
55
 
        super(AOEManager, self).__init__(*args, **kwargs)
 
74
        super(VolumeManager, self).__init__(*args, **kwargs)
 
75
        # NOTE(vish): Implementation specific db handling is done
 
76
        #             by the driver.
 
77
        self.driver.db = self.db
56
78
 
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)
 
79
    def init_host(self):
 
80
        """Do any initialization that needs to be run if this is a
 
81
           standalone service."""
 
82
        self.driver.check_for_setup_error()
 
83
        ctxt = context.get_admin_context()
 
84
        volumes = self.db.volume_get_all_by_host(ctxt, self.host)
 
85
        logging.debug("Re-exporting %s volumes", len(volumes))
 
86
        for volume in volumes:
 
87
            self.driver.ensure_export(ctxt, volume)
66
88
 
67
89
    @defer.inlineCallbacks
68
90
    def create_volume(self, context, volume_id):
69
 
        """Creates and exports the volume"""
 
91
        """Creates and exports the volume."""
70
92
        context = context.elevated()
71
 
        logging.info("volume %s: creating", volume_id)
72
 
 
73
93
        volume_ref = self.db.volume_get(context, volume_id)
 
94
        logging.info("volume %s: creating", volume_ref['name'])
74
95
 
75
96
        self.db.volume_update(context,
76
97
                              volume_id,
77
98
                              {'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()
 
99
        # NOTE(vish): so we don't have to get volume from db again
 
100
        #             before passing it to the driver.
 
101
        volume_ref['host'] = self.host
 
102
 
 
103
        logging.debug("volume %s: creating lv of size %sG",
 
104
                      volume_ref['name'], volume_ref['size'])
 
105
        yield self.driver.create_volume(volume_ref)
 
106
 
 
107
        logging.debug("volume %s: creating export", volume_ref['name'])
 
108
        yield self.driver.create_export(context, volume_ref)
97
109
 
98
110
        now = datetime.datetime.utcnow()
99
111
        self.db.volume_update(context,
100
112
                              volume_ref['id'], {'status': 'available',
101
113
                                                 'launched_at': now})
102
 
        logging.debug("volume %s: created successfully", volume_id)
 
114
        logging.debug("volume %s: created successfully", volume_ref['name'])
103
115
        defer.returnValue(volume_id)
104
116
 
105
117
    @defer.inlineCallbacks
106
118
    def delete_volume(self, context, volume_id):
107
 
        """Deletes and unexports volume"""
 
119
        """Deletes and unexports volume."""
108
120
        context = context.elevated()
109
121
        volume_ref = self.db.volume_get(context, volume_id)
110
122
        if volume_ref['attach_status'] == "attached":
111
123
            raise exception.Error("Volume is still attached")
112
124
        if volume_ref['host'] != self.host:
113
125
            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'])
 
126
        logging.debug("volume %s: removing export", volume_ref['name'])
 
127
        yield self.driver.remove_export(context, volume_ref)
 
128
        logging.debug("volume %s: deleting", volume_ref['name'])
 
129
        yield self.driver.delete_volume(volume_ref)
121
130
        self.db.volume_destroy(context, volume_id)
 
131
        logging.debug("volume %s: deleted successfully", volume_ref['name'])
122
132
        defer.returnValue(True)
123
133
 
124
134
    @defer.inlineCallbacks
125
135
    def setup_compute_volume(self, context, volume_id):
126
 
        """Setup remote volume on compute host
127
 
 
128
 
        Returns path to device.
129
 
        """
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))
 
136
        """Setup remote volume on compute host.
 
137
 
 
138
        Returns path to device."""
 
139
        context = context.elevated()
 
140
        volume_ref = self.db.volume_get(context, volume_id)
 
141
        if volume_ref['host'] == self.host and FLAGS.use_local_volumes:
 
142
            path = yield self.driver.local_path(volume_ref)
 
143
        else:
 
144
            path = yield self.driver.discover_volume(volume_ref)
 
145
        defer.returnValue(path)
 
146
 
 
147
    @defer.inlineCallbacks
 
148
    def remove_compute_volume(self, context, volume_id):
 
149
        """Remove remote volume on compute host."""
 
150
        context = context.elevated()
 
151
        volume_ref = self.db.volume_get(context, volume_id)
 
152
        if volume_ref['host'] == self.host and FLAGS.use_local_volumes:
 
153
            defer.returnValue(True)
 
154
        else:
 
155
            yield self.driver.undiscover_volume(volume_ref)