47
43
'Name for the VG that will contain exported volumes')
48
44
flags.DEFINE_string('aoe_eth_dev', 'eth0',
49
45
'Which device to export the volumes on')
50
flags.DEFINE_string('storage_name',
52
'name of this service')
53
46
flags.DEFINE_integer('first_shelf_id',
54
47
utils.last_octet(utils.get_my_ip()) * 10,
55
48
'AoE starting shelf_id for this service')
59
52
flags.DEFINE_string('aoe_export_dir',
60
53
'/var/lib/vblade-persist/vblades',
61
54
'AoE directory where exports are created')
62
flags.DEFINE_integer('slots_per_shelf',
55
flags.DEFINE_integer('blades_per_shelf',
64
'Number of AoE slots per shelf')
57
'Number of AoE blades per shelf')
65
58
flags.DEFINE_string('storage_availability_zone',
67
60
'availability zone of this service')
91
85
super(VolumeService, self).__init__()
92
86
self.volume_class = Volume
93
87
if FLAGS.fake_storage:
94
FLAGS.aoe_export_dir = tempfile.mkdtemp()
95
88
self.volume_class = FakeVolume
96
89
self._init_volume_group()
99
# TODO(josh): Get rid of this destructor, volumes destroy themselves
100
if FLAGS.fake_storage:
102
shutil.rmtree(FLAGS.aoe_export_dir)
103
except Exception, err:
106
91
@defer.inlineCallbacks
107
92
@validate.rangetest(size=(0, 1000))
108
93
def create_volume(self, size, user_id, project_id):
114
99
logging.debug("Creating volume of size: %s" % (size))
115
100
vol = yield self.volume_class.create(size, user_id, project_id)
116
datastore.Redis.instance().sadd('volumes', vol['volume_id'])
117
datastore.Redis.instance().sadd('volumes:%s' % (FLAGS.storage_name), vol['volume_id'])
118
101
logging.debug("restarting exports")
119
102
yield self._restart_exports()
120
103
defer.returnValue(vol['volume_id'])
134
117
def delete_volume(self, volume_id):
135
118
logging.debug("Deleting volume with id of: %s" % (volume_id))
136
119
vol = get_volume(volume_id)
137
if vol['status'] == "attached":
120
if vol['attach_status'] == "attached":
138
121
raise exception.Error("Volume is still attached")
139
if vol['node_name'] != FLAGS.storage_name:
122
if vol['node_name'] != FLAGS.node_name:
140
123
raise exception.Error("Volume is not local to this node")
141
124
yield vol.destroy()
142
datastore.Redis.instance().srem('volumes', vol['volume_id'])
143
datastore.Redis.instance().srem('volumes:%s' % (FLAGS.storage_name), vol['volume_id'])
144
125
defer.returnValue(True)
146
127
@defer.inlineCallbacks
147
128
def _restart_exports(self):
148
129
if FLAGS.fake_storage:
150
yield process.simple_execute("sudo vblade-persist auto all")
151
# NOTE(vish): this command sometimes sends output to stderr for warnings
131
# NOTE(vish): these commands sometimes sends output to stderr for warnings
132
yield process.simple_execute("sudo vblade-persist auto all", error_ok=1)
152
133
yield process.simple_execute("sudo vblade-persist start all", error_ok=1)
154
135
@defer.inlineCallbacks
172
153
return self.volume_id
174
155
def default_state(self):
175
return {"volume_id": self.volume_id}
156
return {"volume_id": self.volume_id,
157
"node_name": "unassigned"}
178
160
@defer.inlineCallbacks
179
161
def create(cls, size, user_id, project_id):
180
162
volume_id = utils.generate_uid('vol')
181
163
vol = cls(volume_id)
182
vol['node_name'] = FLAGS.storage_name
164
vol['node_name'] = FLAGS.node_name
183
165
vol['size'] = size
184
166
vol['user_id'] = user_id
185
167
vol['project_id'] = project_id
225
207
self['attach_status'] = "detached"
211
is_new = self.is_new_record()
212
super(Volume, self).save()
214
redis = datastore.Redis.instance()
215
key = self.__devices_key
216
# TODO(vish): these should be added by admin commands
217
more = redis.scard(self._redis_association_name("node",
219
if (not redis.exists(key) and not more):
220
for shelf_id in range(FLAGS.first_shelf_id,
221
FLAGS.last_shelf_id + 1):
222
for blade_id in range(FLAGS.blades_per_shelf):
223
redis.sadd(key, "%s.%s" % (shelf_id, blade_id))
224
self.associate_with("node", self['node_name'])
228
226
@defer.inlineCallbacks
229
227
def destroy(self):
231
yield self._remove_export()
232
except Exception as ex:
233
logging.debug("Ingnoring failure to remove export %s" % ex)
228
yield self._remove_export()
235
229
yield self._delete_lv()
230
self.unassociate_with("node", self['node_name'])
231
if self.get('shelf_id', None) and self.get('blade_id', None):
232
redis = datastore.Redis.instance()
233
key = self.__devices_key
234
redis.sadd(key, "%s.%s" % (self['shelf_id'], self['blade_id']))
236
235
super(Volume, self).destroy()
238
237
@defer.inlineCallbacks
244
243
yield process.simple_execute(
245
244
"sudo lvcreate -L %s -n %s %s" % (sizestr,
246
245
self['volume_id'],
249
249
@defer.inlineCallbacks
250
250
def _delete_lv(self):
251
251
yield process.simple_execute(
252
252
"sudo lvremove -f %s/%s" % (FLAGS.volume_group,
253
self['volume_id']), error_ok=1)
256
def __devices_key(self):
257
return 'volume_devices:%s' % FLAGS.node_name
255
259
@defer.inlineCallbacks
256
260
def _setup_export(self):
257
(shelf_id, blade_id) = get_next_aoe_numbers()
261
redis = datastore.Redis.instance()
262
key = self.__devices_key
263
device = redis.spop(key)
266
(shelf_id, blade_id) = device.split('.')
258
267
self['aoe_device'] = "e%s.%s" % (shelf_id, blade_id)
259
268
self['shelf_id'] = shelf_id
260
269
self['blade_id'] = blade_id
262
yield self._exec_export()
271
yield self._exec_setup_export()
264
273
@defer.inlineCallbacks
265
def _exec_export(self):
274
def _exec_setup_export(self):
266
275
yield process.simple_execute(
267
276
"sudo vblade-persist setup %s %s %s /dev/%s/%s" %
268
277
(self['shelf_id'],
269
278
self['blade_id'],
270
279
FLAGS.aoe_eth_dev,
271
280
FLAGS.volume_group,
281
self['volume_id']), error_ok=1)
274
283
@defer.inlineCallbacks
275
284
def _remove_export(self):
285
if not self.get('shelf_id', None) or not self.get('blade_id', None):
286
defer.returnValue(False)
287
yield self._exec_remove_export()
288
defer.returnValue(True)
290
@defer.inlineCallbacks
291
def _exec_remove_export(self):
276
292
yield process.simple_execute(
277
293
"sudo vblade-persist stop %s %s" % (self['shelf_id'],
294
self['blade_id']), error_ok=1)
279
295
yield process.simple_execute(
280
296
"sudo vblade-persist destroy %s %s" % (self['shelf_id'],
297
self['blade_id']), error_ok=1)
284
301
class FakeVolume(Volume):
285
302
def _create_lv(self):
288
def _exec_export(self):
305
def _exec_setup_export(self):
289
306
fname = os.path.join(FLAGS.aoe_export_dir, self['aoe_device'])
290
307
f = file(fname, "w")
293
def _remove_export(self):
310
def _exec_remove_export(self):
311
os.unlink(os.path.join(FLAGS.aoe_export_dir, self['aoe_device']))
296
313
def _delete_lv(self):
299
def get_next_aoe_numbers():
300
for shelf_id in xrange(FLAGS.first_shelf_id, FLAGS.last_shelf_id + 1):
301
aoes = glob.glob("%s/e%s.*" % (FLAGS.aoe_export_dir, shelf_id))
305
blade_id = int(max([int(a.rpartition('.')[2]) for a in aoes])) + 1
306
if blade_id < FLAGS.slots_per_shelf:
307
logging.debug("Next shelf.blade is %s.%s", shelf_id, blade_id)
308
return (shelf_id, blade_id)
309
raise NoMoreVolumes()