xentaur

view xentaur.py @ 60:e7b9761c03e8

info() + logo()
author igor
date Sun Nov 11 20:57:50 2007 +0200 (2007-11-11)
parents 308b524d9a70
children 6471afbee150
line source
1 #!/usr/bin/python
2 # vim: set fileencoding=utf-8 :
4 import sys,os,time
6 xentaur_path=os.environ['HOME']+"/xentaur"
8 sys.path.append('/etc/xen')
9 sys.path.append(xentaur_path)
11 #network='snrs_ipsec_rsa_1'
12 node_object={}
13 link_object={}
14 bridge_object={}
16 network='icnd2'
17 domain='sw1'
18 from xendomain import *
20 bridges_turned_down=[]
22 from IPython.Shell import IPShellEmbed
25 screenrc=os.environ['HOME']+"/.screenrc_xentaur"
27 def run(program, *args):
28 pid = os.fork()
29 if not pid:
30 os.execvp(program, (program,) + args)
31 return os.wait()[0]
33 def run_command(line):
34 #cmds=line.split()
35 #run(cmds[0],*cmds[1:])
36 run("/bin/sh", "-c", line)
38 def run_command_return_stdout(line):
39 p = os.popen(line)
40 output = p.read()
41 p.close()
42 return output
44 ################################################################################
45 #Xentaur command-line commands
47 ## Start
49 def start_bridges():
50 unbound_bridges=set(bridges)-set(real_bridges)
51 script=""
52 script="\n".join(map(lambda x: "sudo brctl show | awk '{print $1}' | grep -qx "+x+" || sudo brctl addbr "+x, unbound_bridges))
53 script+="\n"+"\n".join(map(lambda x: "sudo brctl stp "+x+" off", unbound_bridges))
54 script+="\n"+"\n".join(map(lambda x: "sudo ip link set "+x+" up", unbound_bridges))
56 print """#!/bin/sh
57 # create unbound bridges
58 %s
59 """ % (script)
61 def start_domain(domain):
62 print "sudo xm create "+xentaur_path+"/xendomain.py "+" domain="+domain+" network="+network+" && sleep 1 && sudo xm sched-credit -d $(sudo xm list | grep "+domain+" | awk '{print $2}') -c 10 && sleep 1"
64 def start_domains(doms=domains):
65 for domain in doms:
66 if not domain in real_nodes:
67 start_domain(domain)
69 def start_all():
70 graph()
71 screen()
72 start_bridges()
73 start_domains()
75 ## Stop
77 def stop_domain(domain,wait=0):
78 if wait:
79 print "sudo xm shutdown -w "+domain
80 else:
81 print "sudo xm shutdown "+domain
83 def stop_domains(doms=domains, wait=0):
84 for domain in doms:
85 if not domain in real_nodes:
86 stop_domain(domain,wait)
88 def stop_bridges():
89 ###FIXME###
90 return 0
92 def stop_all(wait=0):
93 stop_domains(domains, wait)
94 stop_bridges()
96 def restart_all():
97 stop_all(1)
98 start_all()
100 ####################################################
102 def create_objects():
103 create_node_objects()
104 create_bridge_objects()
105 create_link_objects()
107 def create_node_objects():
108 for dom in domains:
109 node_object[dom]=Node(dom)
111 def create_bridge_objects():
112 for bridge in bridges:
113 bridge_object[bridge]=Bridge(bridge)
115 def create_link_objects():
117 for node, bridges_raw in vbridges_table.iteritems():
118 interface=0
119 j=0
120 for this_bridge in bridges_raw:
121 int_label=""
122 if this_bridge.find(':') != -1:
123 res = this_bridge.split(':')
124 this_bridge= res[0]
125 bridges_raw[j] = this_bridge
126 int_label = res[1]
127 if not [ node, bridges_raw.index(this_bridge), this_bridge ] in temporary_links:
128 name="%s %s %s" % (node,interface,this_bridge)
129 link_object[name]=Link(name,node,interface,this_bridge,int_label)
130 interface+=1
131 vbridges_table[node]=bridges_raw
133 for node, bridges_raw in bridge_bridge_table.iteritems():
134 interface=0
135 j=0
136 for this_bridge in bridges_raw:
137 int_label=""
138 if this_bridge.find(':') != -1:
139 res = this_bridge.split(':')
140 this_bridge= res[0]
141 bridges_raw[j] = this_bridge
142 int_label = res[1]
143 if not [ node, bridges_raw.index(this_bridge), this_bridge ] in temporary_links:
144 name="%s %s %s" % (node,interface,this_bridge)
145 link_object[name]=Link(name,node,interface,this_bridge,int_label)
146 interface+=1
147 bridge_bridge_table[node]=bridges_raw
149 for node,interface,bridge in temporary_links:
150 name="%s %s %s" % (node,interface,bridge)
151 link_object[name]=Link(name,node,interface,this_bridge)
153 for node,interface,bridge in broken_links:
154 name="%s %s %s" % (node,interface,bridge)
155 link_object[name]=Link(name,node,interface,this_bridge)
158 ####################################################
160 def screen():
161 wait_seconds=1
162 screens=[]
163 for domain in domains:
164 screens.append("screen -t %s %s sh -c 'while true; do %s ; echo Retrying in %s secods...; sleep %s ; clear; done'" %
165 (domain,domains.index(domain)+1,node_object[domain].console_string(),wait_seconds,wait_seconds))
166 screenlist="\n".join(screens)
168 hardstatus='hardstatus string "%{rk}Xentaur%{bk}@%H %{gk}%c %{yk}%d.%m %{wk}%?%-Lw%?%{bw}%n*%f%t%?(%u)%?%{wk}%?%+Lw%?"'
170 f=open(screenrc, "w");
171 f.write("""
172 hardstatus on
173 hardstatus alwayslastline
174 %s
176 screen -t console 0 sh -c 'while true; do cd %s; ./xentaur.py shell ; echo Retrying in %s secods...; sleep %s ; clear; done'
177 #screen -t xentaur - sh -c 'while true; do bash ; echo Retrying in %s secods...; sleep %s ; clear; done'
178 %s
179 """ % (hardstatus,xentaur_path,wait_seconds,wait_seconds,wait_seconds,wait_seconds,screenlist))
180 f.close()
181 print "# GNU Screen config file is written to: %s" % screenrc
183 def graph():
184 nodelist=""
185 bridgelist=""
186 linklist=""
187 physicallist=""
188 networklist=""
190 nodelist=";\n ".join(map(lambda node: node_object[node].graphviz_string(),nodes))
191 if nodelist: nodelist += ";"
192 bridgelist=";\n ".join(map(lambda bridge: bridge_object[bridge].graphviz_string(),bridges))
193 if bridgelist: bridgelist += ";"
194 linklist=";\n ".join(map(lambda link: link_object[link].graphviz_string(),link_object.keys()))
195 if linklist: linklist += ";"
197 f = open(network+".dot", "w");
198 f.write ("""
199 graph G {
200 edge [len=1.25];
201 splines=true;
202 // nodes
203 // node [shape=plaintext,color=white,shapefile="shapes/cisco.bmp/router.png"];
204 %s
206 // bridges
207 // node [shape=none,shapefile="shapes/all/switch.png"];
208 %s
210 // physical
211 node [shape=rectangle,color=blue];
212 %s
214 // networks (not bridges, not physical)
215 node [shape=rectangle,color=green];
216 %s
218 // links (between nodes and bridges)
219 %s
221 };
222 """ % (nodelist, bridgelist, physicallist, networklist, linklist))
223 f.close()
224 run_command("neato -Tpng -o %s.png %s.dot "%(network,network))
225 run_command("neato -Tjpg -o %s.jpg %s.dot "%(network,network))
226 run_command("neato -Tsvg -o %s.svg %s.dot "%(network,network))
227 run_command("neato -Tcmapx -o %s.cmapx -NURL=http://google.com %s.dot "%(network,network))
228 print "# Network map is written to files: %s.{png,svg,jpg,dot}" % network
230 def autoredraw():
231 graph()
233 def shell():
234 autoredraw()
235 ipshell = IPShellEmbed()
236 ipshell()
238 def version():
239 print "Xentaur 0.1-PRE"
240 print "(Godzilla-mutant) _"
241 print " / * \\"
242 print " / .-"
243 print " / |"
244 print " | \\ \\\\ \\"
245 print " _ -------| \\ \\\\ \\"
246 print " / / \\_\\ -"
247 print "/ |\\ | |"
248 print "| | \\ .-----. | \\ |"
249 print " | / \\ \\ \\ \\"
250 print " \\/|.\\ \\ \\ \\ \\"
251 print " \\| - . \\_\\ \\_\\"
252 print "-----------------------------------------------"
255 def info():
256 version()
258 print "Network name: ", network
259 print "-----------------------------------------------"
260 print
261 print "Nodes: ", len(domains)
262 print " * virtual nodes: ", len(domains)-len(real_nodes)
263 print " * real nodes:", len(real_nodes)
264 print
265 print "Bridges:", len(bridges)
266 print " * virtual bridges:", len(bridges)-len(real_bridges)-len(cross_bridges)
267 print " * real switches:", len(real_bridges)
268 print " * direct links:", len(cross_bridges)
270 def show_usage():
271 print """Usage:
272 xentaur <command> [<argument>]
274 Commands:
275 start-all -- start bridges and domains
276 start-domains -- start domains only
277 start-bridges -- start bridges only
278 stop-all -- stop bridges and domains
279 stop-domains -- stop domains only
280 stop-bridges -- stop bridges only (domains have to be stopped already)
281 restart-all -- restart bridges and domains
283 start <domain> -- start the <domain>
284 stop <domain> -- stop the <domain>
286 graph -- generate network scheme (result is in <network>.{png,jpg,svg})
287 screen -- generate GNU Screen config file (~/.screenrc_xentaur)
288 shell -- run Xentaur shell
290 """
292 def save():
293 print "network =", xen_config_name
294 print "domains =", domains
295 print "domain_types =", domain_types
296 print "bridges =", bridges
297 print "vbridges_table =", vbridges_table
298 print "hidden_bridges =", hidden_bridges
299 print "broken_links =", broken_links
300 print "temporary_links =", temporary_links
301 print "bridges_turned_down =", bridges_turned_down
303 #-----------------------------------------------------------------------
304 # CLASSES
306 class Bridge:
307 def __init__ (self,name):
308 self.name=name
309 def up(self):
310 bridge_up(self.name)
311 def down(self):
312 bridge_down(self.name)
313 def show(self):
314 show_bridge(self.name)
315 def dump_start(self,filter=""):
316 dump_start(self.name,filter)
318 def is_hidden(self):
319 return self.name in hidden_bridges
320 def is_real(self):
321 return self.name in real_bridges
322 def is_turned_down(self):
323 return self.name in bridges_turned_down
324 def is_cross(self):
325 return self.name in cross_bridges
327 def graphviz_string(self):
328 if self.is_hidden():
329 return ""
330 elif self.is_cross():
331 return "%s [shape=circle,height=0.03,color=black,fillcolor=black,style=filled,label=\"\"]" % (self.name)
332 elif self.is_real():
333 return "%s [color=white,shape=none,shapefile=\"shapes/all/real_switch.png\"]" % (self.name)
334 elif self.is_turned_down():
335 return "%s [color=white,shape=none,shapefile=\"shapes/all/switch_turned_down.png\"]" % (self.name)
336 else:
337 return "%s [color=white,shape=none,shapefile=\"shapes/all/switch.png\"]" % (self.name)
340 class Node:
341 def __init__ (self,name):
342 self.name=name
343 self.type=domain_types[domains.index(name)]
344 def start(self):
345 return ""
346 def stop(self):
347 return ""
348 def start_commandline(self):
349 return ""
350 def get_domain_id(self):
351 return get_domain_id(self.name)
352 def graphviz_string(self):
353 return self.name+" [color=white,shape=plaintext,label=\" "+self.name+"\",shapefile=\"shapes/all/"+\
354 domain_types[domains.index(self.name)]+".png\",fontcolor=black,fontsize=16,target=\"http://google.com\"]"
355 def console_string(self):
356 if self.type == 'quagga' or self.type == 'xenomips':
357 return "sudo xm console "+self.name
358 elif self.name in real_bridges or self.name in real_nodes:
359 return "echo Press enter to connect; read line; "+connection_table[self.name]
362 class Link:
363 def __init__ (self,name,node,interface,bridge,label=""):
364 self.name=name
365 self.node=node
366 self.interface=interface
367 self.bridge=bridge
368 self.label=label
370 def is_temporary(self):
371 return [self.node,self.interface,self.bridge] in temporary_links
373 def is_broken(self):
374 return ([self.node,self.interface,self.bridge] in broken_links)
376 def graphviz_string(self):
377 if self.is_temporary():
378 return self.node+" -- "+self.bridge+" [taillabel=\"fa"+str(self.interface)+"/0\",color=blue,len=10,w=5,weight=5]"
379 if self.is_broken():
380 return self.node+" -- "+self.bridge+" [taillabel=\"fa"+str(self.interface)+"/0\",style=dashed]"
382 ip="\\n.%s.%s" % (bridges.index(self.bridge)+1, domains.index(self.node)+1)
383 if domain_types[domains.index(self.node)] == 'xenomips':
384 int_name="fa"+str(self.interface)+"/0"
385 else:
386 int_name="eth"+str(self.interface)
387 if self.label != "":
388 int_name = self.label
389 return self.node+" -- "+self.bridge+" [taillabel=\""+int_name+ip+"\",fontsize=14,fontname=fixed]"
392 #-----------------------------------------------------------------------
393 # DOMAINS
395 def get_domain_id(domain):
396 return run_command_return_stdout("sudo xm list | awk '{if ($1 == \"'%s'\") print $2}'" % domain).rstrip("\n")
399 #-----------------------------------------------------------------------
400 # BRIDGES and IFACES
402 def bridge_down(bridge):
403 """
404 Turn the bridge <bridge> down
405 """
406 if bridge in real_bridges:
407 print "Bridge %s is a real bridge" % (bridge)
408 return -1
409 if bridge in bridges_turned_down:
410 print "Bridge %s is turned down already" % (bridge)
411 else:
412 bridges_turned_down.append(bridge)
413 run_command("sudo ip link set %s down" % bridge)
414 autoredraw()
416 def bridge_up(bridge):
417 """
418 Turn the bridge <bridge> up
419 """
420 if bridge in real_bridges:
421 print "Bridge %s is a real bridge" % (bridge)
422 return -1
423 if not (bridge in bridges_turned_down):
424 print "Bridge %s is turned up already" % (bridge)
425 else:
426 bridges_turned_down.remove(bridge)
427 run_command("sudo ip link set %s up" % bridge)
428 autoredraw()
430 def show_bridge(bridge):
431 """
432 Show the state of the bridge <bridge>
433 """
434 if bridge in real_bridges:
435 print "Bridge %s is a real bridge" % (bridge)
436 return -1
437 run_command("sudo ip link show %s" % bridge)
440 def int_disconnect(domain, int_number):
441 """
442 Disconnect the interface with the number <int_number>
443 of the domain <domain> from the bridge to which
444 it is connected
445 """
446 dom_id=get_domain_id(domain)
447 bridge=vbridges_table[domain][int_number]
448 if not bridge:
449 print "Interface %s of the %s domain is not connected" % (int_number, domain)
450 return 1
451 run_command("sudo brctl delif %s vif%s.%s" % (bridge, dom_id, int_number))
452 vbridges_table[domain][int_number]=''
453 if [ domain, int_number, bridge ] in temporary_links:
454 temporary_links.remove([ domain, int_number, bridge ])
455 else:
456 broken_links.append([ domain, int_number, bridge ])
457 autoredraw()
459 def int_connect(domain, int_number, bridge):
460 """
461 Connect the interface with the number <int_number>
462 of the domain <domain> to the bridge <bridge>
463 """
464 if bridge in real_bridges:
465 print "Bridge %s is a real bridge" % (bridge)
466 return -1
468 dom_id=get_domain_id(domain)
469 if vbridges_table[domain][int_number]:
470 print "Interface %s of the %s domain is connected already to the %s bridge" % (int_number, domain, vbridges_table[domain][int_number])
471 return 1
472 run_command("sudo brctl addif %s vif%s.%s" % (bridge, dom_id, int_number))
473 vbridges_table[domain][int_number]=bridge
474 if [ domain, int_number, bridge ] in broken_links:
475 broken_links.remove([ domain, int_number, bridge ])
476 else:
477 temporary_links.append([ domain, int_number, bridge ])
478 autoredraw()
480 def int_reconnect(domain, int_number, bridge):
481 """
482 Reconnect the interface with the number <int_number>
483 of the domain <domain> from the bridge to which
484 it is connected to the bridge <bridge>
485 """
486 if bridge in real_bridges:
487 print "Bridge %s is a real bridge" % (bridge)
488 return -1
490 int_disconnect(domain, int_number)
491 int_connect(domain, int_number, bridge)
493 def show_int(domain, int_number):
494 """
495 Show information about the interface <int_nuber>
496 of the domain <domain>
497 """
498 return vbridges_table[domain][int_number]
501 def dump_start(bridge, filter=""):
502 if bridge in real_bridges:
503 print "Bridge %s is a real bridge" % (bridge)
504 return -1
505 try:
506 print "Writing dump... (press Ctrl-C to stop)"
507 run_command("sudo tcpdump -w xentaur.dump -i %s %s > /dev/null 2>&1 " % (bridge,filter))
508 except:
509 print "Done.\n Dump is written to xentaur.dump"
510 return 0
512 def dump_stop():
513 return 0
516 #-----------------------------------------------------------------------
517 # CONFIGURATION TEMPLATES
520 def configure_ip_addresses(doms=domains):
522 cisco_set_ip_on_int="""
523 \n\n\n
524 int fa%s/0
525 no ip address
526 ip address %s 255.255.255.0
527 no shutdown
528 exit
529 """
531 quagga_set_ip_on_int="""
532 int eth%s
533 no ip address
534 ip address %s/24
535 no shutdown
536 exit
537 """
539 for dom in doms:
540 i=domains.index(dom)+1
541 if domain_types[domains.index(dom)] == 'quagga':
542 command = quagga_set_ip_on_int
543 write_to(i,"\nconf t\n")
544 j=0
545 for br in vbridges_table[dom]:
546 write_to(i,command % (j, "192.168.%s.%s"%(bridges.index(br)+1,i)))
547 j+=1
548 write_to(i,"\nend\n")
549 else:
550 command = cisco_set_ip_on_int
551 write_to(i,"\nena\nconf t\n")
552 j=0
553 for br in vbridges_table[dom]:
554 write_to(i,command % (j, "192.168.%s.%s"%(bridges.index(br)+1,i)))
555 j+=1
556 write_to(i,"\nend\n")
557 return 0
559 def configure_no_ip_addresses(doms=domains):
561 cisco_set_ip_on_int="""
562 \n\n\n
563 int fa%s/0
564 no ip address %s 255.255.255.0
565 exit
566 """
568 quagga_set_ip_on_int="""
569 int eth%s
570 no ip address %s/24
571 exit
572 """
574 for dom in doms:
575 i=domains.index(dom)+1
576 if domain_types[domains.index(dom)] == 'quagga':
577 command = quagga_set_ip_on_int
578 write_to(i,"\nconf t\n")
579 j=0
580 for br in vbridges_table[dom]:
581 write_to(i,command % (j, "192.168.%s.%s"%(bridges.index(br)+1,i)))
582 j+=1
583 write_to(i,"\nend\n")
584 else:
585 command = cisco_set_ip_on_int
586 write_to(i,"\nena\nconf t\n")
587 j=0
588 for br in vbridges_table[dom]:
589 write_to(i,command % (j, "192.168.%s.%s"%(bridges.index(br)+1,i)))
590 j+=1
591 write_to(i,"\nend\n")
592 return 0
594 def configure_ospf(doms=domains):
595 for dom in doms:
596 if domain_types[domains.index(dom)] == 'quagga':
597 write_to(dom,"\n\nconf t\nrouter ospf\nnetwork 192.168.0.0/16 area 0\nend\n")
598 else:
599 write_to(dom,"\n\nena\nconf t\nrouter ospf 1\nnetwork 192.168.0.0 0.0.255.255 area 0\nend\n")
600 return 0
602 def configure_hostname(doms=domains):
603 for dom in doms:
604 if domain_types[domains.index(dom)] == 'quagga':
605 write_to(dom,"\n\nconf t\nhostname %s\nend\n" % dom)
606 else:
607 write_to(dom,"\n\nena\nconf t\nhostname %s\nend\n" % dom)
608 return 0
610 def configure_logging_synchronous(doms=domains):
611 for dom in domains:
612 if domain_types[domains.index(dom)] == 'quagga':
613 0
614 else:
615 write_to(dom,"\n\nena\nconf t\nline console 0\nlogging synchronous\nend\n")
616 return 0
618 def configure_exec_timeout_0(doms=domains):
619 for dom in domains:
620 if domain_types[domains.index(dom)] == 'quagga':
621 0
622 else:
623 write_to(dom,"\n\nena\nconf t\nline console 0\nexec-timeout 0\nend\n")
624 return 0
626 def configure_no_cdp_log_mismatch_duplex(doms=domains):
627 for dom in filter_by_type(domains,'xenomips'):
628 write_to(dom,"\n\nena\nconf t\nno cdp log mismatch duplex\nend\n")
630 def configure_save(doms=domains):
631 write_to(doms,"\nwr\n")
633 def configure_root(doms=domains):
634 write_to(doms,"root\n")
636 #-----------------------------------------------------------------------
639 def add_domain(name,type):
640 domains.append(name)
641 domain_types.append(type)
643 def brake_link(domain,bridge):
644 broken_links.append([domain,bridge])
646 wt_timeout=0.5
647 def write_to(screen,string,return_to_screen=""):
648 """
649 write_to(screen,string):
651 Type *string* to the specified screen(s).
652 Screen may be specified with the number *screen*,
653 with array of numbers,
654 with array of names.
656 """
657 screen_numbers=[] # number of the screens to write to
658 if type(screen) == list:
659 screen_numbers=map(lambda x: domains.index(x)+1, screen)
660 elif type(screen) == int:
661 screen_numbers=[screen]
662 else:
663 screen_numbers=[domains.index(screen)+1]
665 for screen_number in screen_numbers:
666 run_command("screen -X select "+str(screen_number))
667 time.sleep(wt_timeout)
668 for line in string.splitlines():
669 f=open('/tmp/xentaurbuf', 'w')
670 f.write(line+"\n")
671 f.close()
672 run_command("screen -X readreg p /tmp/xentaurbuf")
673 time.sleep(wt_timeout)
674 run_command("nohup screen -X paste p >& /dev/null")
675 time.sleep(wt_timeout)
677 if return_to_screen != "":
678 run_command("screen -X select %s" % (return_to_screen))
679 time.sleep(wt_timeout)
681 def filter_by_type(doms,type):
682 """
683 filter_by_type(doms,type)
685 Return only domains of *doms* that have specified *type*
686 """
687 return filter(lambda x: domain_types[domains.index(x)]==type,domains)
689 #-----------------------------------------------------------------------
691 cisco_fa01_up="""
692 ena
693 conf t
694 int fa0/0
695 duplex half
696 no shutdown
697 exit
698 int fa1/0
699 duplex half
700 no shutdown
701 exit
702 exit
703 exit
704 """
706 cisco_set_ip_on_int="""
707 interface fa%s/0
708 no ip address
709 ip address %s 255.255.255.0
710 exit
711 """
713 nodes=domains
715 create_objects()
718 if len(sys.argv) == 2:
719 if sys.argv[1] == 'start-all':
720 start_all()
721 elif sys.argv[1] == 'start-domains':
722 start_domains()
723 elif sys.argv[1] == 'start-bridges':
724 start_bridges()
725 elif sys.argv[1] == 'stop-all':
726 stop_all()
727 elif sys.argv[1] == 'stop-domains':
728 stop_domains()
729 elif sys.argv[1] == 'stop-bridges':
730 stop_bridges()
731 elif sys.argv[1] == 'restart-all':
732 restart_all()
733 elif sys.argv[1] == 'screen':
734 screen()
735 elif sys.argv[1] == 'graph':
736 graph()
737 elif sys.argv[1] == 'shell':
738 shell()
739 elif sys.argv[1] == 'info':
740 info()
741 else:
742 show_usage()
743 sys.exit(1)
744 elif len(sys.argv) == 3:
745 if sys.argv[1] == 'start':
746 start_domain(sys.argv[2])
747 elif sys.argv[1] == 'stop':
748 stop_domain(sys.argv[2])
749 elif sys.argv[1] == 'restart':
750 stop_domain(sys.argv[2])
751 start_domain(sys.argv[2])
752 else:
753 show_usage()
754 sys.exit(1)
755 else:
756 show_usage()
757 sys.exit(1)
759 sys.exit(0)