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.
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.
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
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
38
:volume_group: Name of the group that will contain exported volumes (default:
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)
36
58
flags.DEFINE_string('storage_availability_zone',
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',
43
'Number of vblade shelves')
44
flags.DEFINE_integer('blades_per_shelf',
46
'Number of vblade blades per shelf')
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')
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
77
self.driver.db = self.db
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:
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)
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)
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)
73
93
volume_ref = self.db.volume_get(context, volume_id)
94
logging.info("volume %s: creating", volume_ref['name'])
75
96
self.db.volume_update(context,
77
98
{'host': self.host})
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)
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
88
logging.debug("volume %s: exporting shelf %s & blade %s", volume_id,
91
yield self.driver.create_export(volume_ref['ec2_id'],
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
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)
107
logging.debug("volume %s: creating export", volume_ref['name'])
108
yield self.driver.create_export(context, volume_ref)
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)
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,
117
yield self.driver.remove_export(volume_ref['ec2_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)
124
134
@defer.inlineCallbacks
125
135
def setup_compute_volume(self, context, volume_id):
126
"""Setup remote volume on compute host
128
Returns path to device.
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,
135
defer.returnValue("/dev/etherd/e%s.%s" % (shelf_id, blade_id))
136
"""Setup remote volume on compute host.
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)
144
path = yield self.driver.discover_volume(volume_ref)
145
defer.returnValue(path)
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)
155
yield self.driver.undiscover_volume(volume_ref)