3
from __future__ import print_function
20
def __init__ (self, path, name):
26
def set_size (self, size):
28
def set_range (self, rs, re):
31
self.curr_size = self.r_end - self.r_start + 1
34
value += "(%d," % self.size
36
value += "0x%x,0x%x)" % (self.r_start, self.r_end)
41
def get_bricks (host, vol):
43
t.prepend("gluster --remote-host=%s system getspec %s"%(host, vol), ".-")
44
return t.open(None, "r")
46
def generate_stanza (vf, all_xlators, cur_subvol):
48
for sv in cur_subvol.subvols:
49
generate_stanza(vf, all_xlators, sv)
50
sv_list.append(sv.name)
51
vf.write("volume %s\n" % cur_subvol.name)
52
vf.write(" type %s\n" % cur_subvol.type)
53
for kvpair in cur_subvol.opts.items():
54
vf.write(" option %s %s\n" % kvpair)
56
vf.write(" subvolumes %s\n" % ''.join(sv_list))
57
vf.write("end-volume\n\n")
60
def mount_brick (localpath, all_xlators, dht_subvol):
63
vf_name = localpath + ".vol"
64
vf = open(vf_name, "w")
65
generate_stanza(vf, all_xlators, dht_subvol)
71
subprocess.call(["glusterfs", "-f", vf_name, localpath])
84
cmd = "getfattr -e hex -n trusted.glusterfs.dht %s 2> /dev/null"
85
t.prepend(cmd%brick, ".-")
86
t.append("grep ^trusted.glusterfs.dht=", "--")
89
value = f.readline().rstrip().split('=')[1][2:]
91
print("could not get layout for %s (might be OK)" % brick)
93
v_start = int("0x"+value[16:24], 16)
94
v_end = int("0x"+value[24:32], 16)
95
return (v_start, v_end)
97
def calc_sizes (bricks, total):
101
b.good_size = (b.size << 32) / total
102
leftover -= b.good_size
109
b.good_size += leftover
113
bricks[0].good_size += leftover
117
def normalize (in_bricks):
121
while curr_hash < (1<<32):
124
if b.r_start == curr_hash:
128
curr_hash = b.r_end + 1
131
print("gap found at 0x%08x" % curr_hash)
133
return out_bricks + in_bricks, used
135
def get_score (bricks):
140
curr_hash += b.good_size
142
new_start = curr_hash
143
curr_hash += b.good_size
144
new_end = curr_hash - 1
145
if new_start > b.r_start:
146
max_start = new_start
148
max_start = b.r_start
149
if new_end < b.r_end:
153
if max_start <= min_end:
154
score += (min_end - max_start + 1)
157
if __name__ == "__main__":
159
my_usage = "%prog [options] server volume [directory]"
160
parser = optparse.OptionParser(usage=my_usage)
161
parser.add_option("-f", "--free-space", dest="free_space",
162
default=False, action="store_true",
163
help="use free space instead of total space")
164
parser.add_option("-l", "--leave-mounted", dest="leave_mounted",
165
default=False, action="store_true",
166
help="leave subvolumes mounted")
167
parser.add_option("-v", "--verbose", dest="verbose",
168
default=False, action="store_true",
169
help="verbose output")
170
options, args = parser.parse_args()
179
hostname, volname = args[:2]
182
orig_dir = os.getcwd()
183
work_dir = tempfile.mkdtemp()
185
def cleanup_workdir ():
188
print("Cleaning up %s" % work_dir)
190
subprocess.call(["umount", b.path])
191
shutil.rmtree(work_dir)
192
if not options.leave_mounted:
193
atexit.register(cleanup_workdir)
198
print("Mounting subvolumes...")
200
volfile_pipe = get_bricks(hostname, volname)
201
all_xlators, last_xlator = volfilter.load(volfile_pipe)
202
for dht_vol in all_xlators.itervalues():
203
if dht_vol.type == "cluster/distribute":
206
print("no DHT volume found")
208
for sv in dht_vol.subvols:
210
lpath = "%s/brick%s" % (work_dir, index)
212
mount_brick(lpath, all_xlators, sv)
213
bricks.append(Brick(lpath, sv.name))
220
print("Collecting information...")
223
info = os.statvfs(b.path)
227
if platform.system() == 'FreeBSD':
236
blocksper100mb = 104857600 / bsize
237
if options.free_space:
238
size = info[3] / blocksper100mb
240
size = info[2] / blocksper100mb
242
print("brick %s has invalid size %d" % (b.path, size))
249
hash_range = get_range(b.path)
250
if hash_range is not None:
253
print("%s has backwards hash range" % b.path)
255
b.set_range(hash_range[0], hash_range[1])
258
print("Calculating new layouts...")
259
calc_sizes(bricks, total)
260
bricks, used = normalize(bricks)
264
while used < len(bricks):
266
best_score = get_score(bricks)
267
for i in range(used):
268
new_bricks = bricks[:]
270
new_bricks.insert(i, bricks[used])
271
new_score = get_score(new_bricks)
272
if new_score > best_score:
274
best_score = new_score
275
if best_place != used:
278
bricks.insert(best_place, nb)
284
b.r_start = curr_hash
285
curr_hash += b.good_size
286
b.r_end = curr_hash - 1
288
print("Here are the xattr values for your size-weighted layout:")
290
print(" %s: 0x0000000200000000%08x%08x" % (
291
b.sv_name, b.r_start, b.r_end))
295
print("Fixing layout for %s" % fix_dir)
297
value = "0x0000000200000000%08x%08x" % (
299
path = "%s/%s" % (b.path, fix_dir)
300
cmd = "setfattr -n trusted.glusterfs.dht -v %s %s" % (
304
if options.leave_mounted:
305
print("The following subvolumes are still mounted:")
307
print("%s on %s" % (b.sv_name, b.path))
308
print("Don't forget to clean up when you're done.")