4549c91698b3aa5627b55230be98d3f0e4691e14
[archipelago] / xseg / archipelago
1 #!/usr/bin/env python
2 #
3 # archipelagos tool
4
5 import os, sys, subprocess, argparse, time, psutil, signal, socket
6 from subprocess import call, check_call
7
8 BIN_PATH="/usr/bin"
9 PIDFILE_PATH="/var/run/archipelago"
10
11 DEVICE_PREFIX="/dev/xsegbd"
12 XSEGBD_SYSFS="/sys/bus/xsegbd/"
13
14 CHARDEV_NAME="/dev/segdev"
15 CHARDEV_MAJOR=60
16 CHARDEV_MINOR=0
17
18 SPEC="segdev:xsegbd:512:1024:12"
19 #REQS=512
20 #PORTS=512
21
22 BPORT=""
23 MPORT=1
24 MBPORT=2
25 VTOOL=3
26 VPORT_START=204
27 VPORT_END=403
28
29 LOGS_PATH=""
30
31 BLOCKER=""
32 MAPPER=""
33 VLMC=""
34
35 NR_OPS_BLOCKERB=""
36 NR_OPS_BLOCKERM=""
37 NR_OPS_VLMC=""
38 NR_OPS_MAPPER=""
39
40 VERBOSITY_BLOCKERB=""
41 VERBOSITY_BLOCKERM=""
42 VERBOSITY_MAPPER=""
43 VERBOSITY_VLMC=""
44
45 FILED_IMAGES=""
46 FILED_MAPS=""
47
48 PITHOS=""
49 PITHOSMAPS=""
50
51 RADOS_POOL_MAPS=""
52 RADOS_POOL_BLOCKS=""
53 STORAGE="File"
54
55 try:
56     execfile(os.path.expanduser("/etc/default/archipelago"), globals())
57 except:
58     print "Cannot open defaults"
59     sys.exit(1)
60
61 HOSTNAME=socket.gethostname()
62
63 if BLOCKER == "pfiled":
64     peer_blockerb = [BLOCKER,
65             ["-p" , str(BPORT), "-g", str(SPEC), "-n", str(NR_OPS_BLOCKERB),
66              str(PITHOS), str(FILED_IMAGES), "-d",
67             "-f", os.path.join(PIDFILE_PATH, "blockerb.pid")],
68             "blockerb"]
69     peer_blockerm = [BLOCKER,
70             ["-p" , str(MBPORT), "-g", str(SPEC), "-n", str(NR_OPS_BLOCKERM),
71             str(PITHOSMAPS), str(FILED_MAPS), "-d",
72             "-f", os.path.join(PIDFILE_PATH, "blockerm.pid")],
73             "blockerm" ]
74 else:
75     peer_blockerb = [BLOCKER,
76             ["-p" , str(BPORT), "-g", str(SPEC), "-n", str(NR_OPS_BLOCKERB),
77              "--pool", str(RADOS_POOL_BLOCKS), "-v", str(VERBOSITY_BLOCKERB),
78              "-d", "--pidfile", os.path.join(PIDFILE_PATH, "blockerb.pid"),
79              "-l", os.path.join(str(LOGS_PATH), "blockerb-"+HOSTNAME+".log"),
80              "-t", "3"],
81              "blockerb"]
82     peer_blockerm = [BLOCKER,
83             ["-p" , str(MBPORT), "-g", str(SPEC), "-n", str(NR_OPS_BLOCKERM),
84              "--pool", str(RADOS_POOL_MAPS), "-v", str(VERBOSITY_BLOCKERM),
85              "-d", "--pidfile", os.path.join(PIDFILE_PATH, "blockerm.pid"),
86              "-l", os.path.join(str(LOGS_PATH), "blockerm-"+HOSTNAME+".log"),
87              "-t", "3"],
88              "blockerm"]
89
90 peer_vlmcd = [VLMC,
91          ["-t" , "1", "-sp",  str(VPORT_START), "-ep", str(VPORT_END),
92           "-g", str(SPEC), "-n", str(NR_OPS_VLMC), "-bp", str(BPORT),
93           "-mp", str(MPORT), "-d", "-v", str(VERBOSITY_VLMC),
94           "--pidfile", os.path.join(PIDFILE_PATH, "vlmcd.pid"),
95           "-l", os.path.join(str(LOGS_PATH), "vlmc-"+HOSTNAME+".log")
96           ], "vlmcd"]
97 peer_mapperd = [MAPPER,
98          ["-t" , "1", "-p",  str(MPORT), "-mbp", str(MBPORT),
99           "-g", str(SPEC), "-n", str(NR_OPS_MAPPER), "-bp", str(BPORT), "-d",
100           "--pidfile", os.path.join(PIDFILE_PATH, "mapperd.pid"),
101           "-v", str(VERBOSITY_MAPPER),
102           "-l", os.path.join(str(LOGS_PATH), "mapperd-"+HOSTNAME+".log")
103           ], "mapperd"]
104
105 peers = [peer_blockerb, peer_blockerm, peer_vlmcd, peer_mapperd]
106 modules = ["xseg", "segdev", "xseg_posix", "xseg_pthread", "xseg_segdev"]
107 xsegbd = "xsegbd"
108
109 def check_conf():
110     def isExec(file_path):
111         return os.path.isfile(file_path) and os.access(file_path, os.X_OK)
112
113     def validExec(program):
114         for path in os.environ["PATH"].split(os.pathsep):
115             exe_file = os.path.join(path, program)
116             if isExec(exe_file):
117                 return True
118         return False
119
120
121     def validPort(port, limit, name):
122         try:
123             if int(port) >= limit:
124                 print str(port) + " >= " + limit
125                 return False
126         except:
127             print "Invalid port "+name+" : " + str(port)
128             return False
129
130         return True
131
132
133     for p in [BLOCKER, MAPPER, VLMC]:
134         if not validExec(p):
135             print p + "is not a valid executable"
136             return False
137
138     if not LOGS_PATH:
139         print "LOGS_PATH is not set"
140         return False
141     if not PIDFILE_PATH:
142         print "PIDFILE_PATH is not set"
143         return False
144
145     try:
146         if not os.path.isdir(str(LOGS_PATH)):
147             print "LOGS_PATH "+str(LOGS_PATH)+" does not exist"
148             return False
149     except:
150         print "LOGS_PATH doesn't exist or is not set"
151         return False
152
153     try:
154         os.makedirs(str(PIDFILE_PATH))
155     except OSError as e:
156         if e.errno == errno.EEXIST:
157             if os.path.isdir(str(PIDFILE_PATH)):
158                 pass
159             else:
160                 print str(PIDFILE_PATH) + " is not a directory"
161                 return False
162         else:
163             print "Cannot create " + str(PIDFILE_PATH)
164             return False
165     except:
166         print "PIDFILE_PATH or is not set"
167         return False
168
169     splitted_spec = str(SPEC).split(':')
170     if len(splitted_spec) < 5:
171         "Invalid spec"
172         return False
173
174     xseg_type=splitted_spec[0]
175     xseg_name=splitted_spec[1]
176     xseg_ports=int(splitted_spec[2])
177     xseg_heapsize=int(splitted_spec[3])
178     xseg_align=int(splitted_spec[4])
179
180     if xseg_type != "segdev":
181         print "Segment type not segdev"
182         return False
183     if xseg_name != "xsegbd":
184         print "Segment name not equal xsegbd"
185         return False
186     if xseg_align != 12:
187         print "Wrong alignemt"
188         return False
189
190     for v in [VERBOSITY_BLOCKERB, VERBOSITY_BLOCKERM, VERBOSITY_MAPPER,
191                     VERBOSITY_VLMC]:
192          if v is None:
193              print "Verbosity missing"
194          try:
195              if (int(v) > 3 or int(v) < 0):
196                  print "Invalid verboisity " + str(v)
197                  return False
198          except:
199              print "Invalid verboisity " + str(v)
200              return False
201
202     for n in [NR_OPS_BLOCKERB, NR_OPS_BLOCKERM, NR_OPS_VLMC, NR_OPS_MAPPER]:
203          if n is None:
204              print "Nr ops missing"
205          try:
206              if (int(n) <= 0):
207                  print "Invalid nr_ops " + str(n)
208                  return False
209          except:
210              print "Invalid nr_ops " + str(n)
211              return False
212
213     if not validPort(VTOOL, xseg_ports, "VTOOL"):
214         return False
215     if not validPort(MPORT, xseg_ports, "MPORT"):
216         return False
217     if not validPort(BPORT, xseg_ports, "BPORT"):
218         return False
219     if not validPort(MBPORT, xseg_ports, "MBPORT"):
220         return False
221     if not validPort(VPORT_START, xseg_ports, "VPORT_START"):
222         return False
223     if not validPort(VPORT_END, xseg_ports, "VPORT_END"):
224         return False
225 #if filed
226     if STORAGE == "File":
227         if FILED_IMAGES and not os.path.isdir(str(FILED_IMAGES)):
228              print "FILED_IMAGES invalid"
229              return False
230         if FILED_MAPS and not os.path.isdir(str(FILED_MAPS)):
231              print "FILED_PATH invalid"
232              return False
233         if PITHOS and not os.path.isdir(str(PITHOS)):
234              print "PITHOS invalid " 
235              return False
236         if PITHOSMAPS and not os.path.isdir(str(PITHOSMAPS)):
237              print "PITHOSMAPS invalid"
238              return False
239
240
241     return True
242
243 if not check_conf():
244     sys.exit(1)
245
246 def exclusive(fn):
247     def exclusive_args(args):
248         file = "/tmp/vlmc_map.lock"
249         while True:
250             try:
251                 fd = os.open(file, os.O_CREAT|os.O_EXCL|os.O_WRONLY)
252                 break;
253             except Exception, reason:
254                 print >> sys.stderr, reason
255                 time.sleep(0.05)
256         try:
257             r = fn(args)
258         finally:
259             os.close(fd)
260             os.unlink(file)
261         return r
262
263     return exclusive_args
264
265 @exclusive
266 def vlmc_showmapped(args):
267     try:
268         devices = os.listdir(os.path.join(XSEGBD_SYSFS, "devices/"))
269     except:
270         return -1
271
272     if not devices:
273         return -1
274     print "id\tpool\timage\tsnap\tdevice"
275     try:
276         for f in devices:
277             d_id = open(XSEGBD_SYSFS + "devices/" + f + "/id").read().strip()
278             target = open(XSEGBD_SYSFS + "devices/"+ f + "/target").read().strip()
279
280             print "%s\t%s\t%s\t%s\t%s" % (d_id, '-', target, '-', DEVICE_PREFIX +
281             d_id)
282     except Exception, reason:
283         print >> sys.stderr, reason
284         return -2
285     return len(devices)
286
287 def loadrc(rc):
288     try:
289         if rc == None:
290             execfile(os.path.expanduser("/etc/default/archipelago"), globals())
291         else:
292             execfile(rc, globals())
293     except:
294         print "Cannot read config file"
295         sys.exit(1)
296
297     if not check_conf():
298         sys.exit(1)
299
300 def loaded_modules():
301     lines = open("/proc/modules").read().split("\n")
302     modules = [f.split(" ")[0] for f in lines]
303     return modules
304
305 def loaded_module(name):
306     return name in loaded_modules()
307
308 def load_module(name):
309     modules = loaded_modules()
310     if name in modules:
311         return 0
312     cmd = ["modprobe -v %s" % name]
313     print cmd
314     try:
315         check_call(cmd, shell=True);
316     except Exception:
317             sys.stderr.write("Module %s failed to load. \n" % name)
318             return -1
319     return 0
320
321 def unload_module(name):
322     modules = loaded_modules()
323     if name not in modules:
324         return 0
325     cmd = ["modprobe -rv %s" % name]
326     print cmd
327     try:
328         check_call(cmd, shell=True);
329     except Exception:
330             sys.stderr.write("Module %s failed to unload. \n" % name)
331             return -1
332     return 0
333
334 def create_segment():
335     #fixme blocking....
336     cmd = ["xseg", str(SPEC), "create"]
337     try:
338         check_call(cmd, shell=False);
339     except Exception:
340             sys.stderr.write("Cannot create segment. \n")
341             return -1
342     return 0
343
344 def destroy_segment():
345     #fixme blocking....
346     cmd = ["xseg", str(SPEC), "destroy"]
347     try:
348         check_call(cmd, shell=False);
349     except Exception:
350             sys.stderr.write("Cannot destroy segment. \n")
351             return 0
352     return 0
353
354 def check_running(name, pid = -1):
355     for p in psutil.process_iter():
356         if p.name == name:
357             if pid != -1:
358                 if pid == p.pid:
359                     return pid
360             else:
361                 return pid
362     return -1
363
364 def check_pidfile(name):
365     pidfile = os.path.join(PIDFILE_PATH, name + ".pid")
366     pf = None
367     try:
368         pf = open(pidfile, "r")
369         pid = int(pf.read())
370         pf.close()
371     except:
372         if pf:
373             pf.close()
374         return -1
375
376     return pid
377
378 def start_peer(peer):
379     cmd = [peer[0]] + peer[1]
380     try:
381         check_call(cmd, shell=False);
382     except Exception:
383         sys.stderr.write("Peer %s start failed.\n" % peer[0])
384         return -1
385     return 0
386
387 def stop_peer(peer):
388     pid = check_pidfile(peer[2])
389     if pid < 0:
390         print " process not running"
391         return -1
392
393     os.kill(pid, signal.SIGTERM)
394     i = 0
395     while check_running(peer[0], pid) > 0:
396         time.sleep(0.1)
397         i += 1
398         if i > 150:
399             print "process did not die in 10 secs"
400             return -1
401     return 0
402
403 def peer_running(peer):
404     pid = check_pidfile(peer[2])
405     if pid < 0:
406         return -1
407
408     r = check_running(peer[0], pid)
409     if r < 0:
410         print "Peer " + peer[2] + " has valid pidfile but does not seem to be active"
411     return 0
412
413
414 def make_segdev():
415     try:
416         os.stat(str(CHARDEV_NAME))
417         return -2
418     except:
419         pass
420     cmd = ["mknod", str(CHARDEV_NAME), "c", str(CHARDEV_MAJOR), str(CHARDEV_MINOR)]
421     print cmd
422     try:
423         check_call(cmd, shell=False);
424     except Exception:
425         sys.stderr.write("Segdev device creation failed. \n")
426         return -1
427     return 0
428
429 def remove_segdev():
430     try:
431         os.stat(str(CHARDEV_NAME))
432     except:
433         return -2
434     try:
435         os.unlink(str(CHARDEV_NAME))
436     except:
437         sys.stderr.write("Segdev device removal failed. \n")
438         return -1
439
440
441 def start(args):
442     if status(args) > 0:
443         return -1
444
445     for m in modules:
446         if load_module(m) < 0:
447             stop(args)
448             return -1
449     time.sleep(0.5)
450
451     if make_segdev() < 0:
452         stop(args)
453         return -1
454     
455     time.sleep(0.5)
456     
457     if create_segment() < 0:
458         stop(args)
459         return -1
460     
461     time.sleep(0.5)
462     
463     for p in peers:
464         if start_peer(p) < 0:
465             stop(args)
466             return -1
467
468
469     if load_module(xsegbd) < 0:
470         stop(args)
471         return -1
472     return 0
473
474 def stop(args):
475     #check devices
476     if vlmc_showmapped(args) > 0:
477         print "Cannot stop archipelago. Mapped volumes exist"
478         return -1
479     if unload_module(xsegbd):
480         return -1
481     r = 0
482     for p in reversed(peers):
483         stop_peer(p)
484
485     remove_segdev()
486     
487     for m in reversed(modules):
488         unload_module(m)
489
490 def status(args):
491     r = 0
492     if vlmc_showmapped(args) >= 0:
493         r += 1
494     if loaded_module(xsegbd):
495         print "Xsegbd loaded"
496         r += 1
497     else:
498         print "Xsegbd not loaded"
499     for m in reversed(modules):
500         if loaded_module(m):
501             print m + " loaded"
502             r += 1
503         else:
504             print m + " not loaded"
505     for p in reversed(peers):
506         if peer_running(p) < 0:
507             print p[0] + " not running"
508         else:
509             print p[0] + " running"
510             r += 1
511     return r
512   
513 def restart(args):
514     stop(args)
515     start(args)
516
517 if __name__ == "__main__":
518     # parse arguments and discpatch to the correct func
519     parser = argparse.ArgumentParser(description='vlmc tool')
520     parser.add_argument('-c', '--config', type=str, nargs='?', help='config file')
521     subparsers = parser.add_subparsers()
522
523     start_parser = subparsers.add_parser('start', help='Start archipelago')
524     start_parser.set_defaults(func=start)
525     
526     stop_parser = subparsers.add_parser('stop', help='Stop archipelago')
527     stop_parser.set_defaults(func=stop)
528     
529     status_parser = subparsers.add_parser('status', help='Archipelago status')
530     status_parser.set_defaults(func=status)
531     
532     restart_parser = subparsers.add_parser('restart', help='Restart archipelago')
533     restart_parser.set_defaults(func=restart)
534
535     args = parser.parse_args()
536     args.func(args)