1
#!/usr/bin/env python2.4
2
# -*- mode: python; -*-
3
#============================================================================
4
# Copyright (C) 2005, 2006 Mike Wray <mike.wray@hp.com>
6
# This library is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU Lesser General Public License as published by
8
# the Free Software Foundation; either version 2.1 of the License, or
9
# (at your option) any later version.
11
# This library is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU Lesser General Public License for more details.
16
# You should have received a copy of the GNU Lesser General Public License
17
# along with this library; if not, write to the Free Software
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
#============================================================================
21
# Vnet (network virtualization) control utility.
28
from getopt import getopt, GetoptError
30
from xen.xend import sxp
31
from xen.xend.PrettyPrint import prettyprint
33
# Path of unix-domain socket to vnetd.
34
VNETD_PATH = "/tmp/vnetd"
37
return os.path.exists(VNETD_PATH)
40
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
41
sock.connect(VNETD_PATH)
42
fi = sock.makefile('r', 0)
43
fo = sock.makefile('w', 0)
46
os.defpath += ':/sbin:/usr/sbin:/usr/local/sbin'
47
CMD_IFCONFIG = 'ifconfig'
54
def __init__(self, **kwds):
55
for (k, v) in kwds.items():
58
opts = Opts(verbose=False, dryrun=False)
66
"""Execute command 'prog' with 'args', optionally printing the command.
69
command = " ".join([ prog ] + map(str, args))
75
def vif_bridge_add(bridge, vif):
76
"""Add a network interface to a bridge.
78
cmd(CMD_BRCTL, 'addif', bridge, vif)
80
def vif_bridge_rem(bridge, vif):
81
"""Remove a network interface from a bridge.
83
cmd(CMD_BRCTL, 'delif', bridge, vif)
85
def bridge_create(bridge, **kwd):
87
Defaults hello time to 0, forward delay to 0 and stp off.
89
cmd(CMD_BRCTL, 'addbr', bridge)
90
if kwd.get('hello', None) is None:
92
if kwd.get('fd', None) is None:
94
if kwd.get('stp', None) is None:
96
bridge_set(bridge, **kwd)
97
cmd(CMD_IFCONFIG, bridge, "up")
99
def bridge_set(bridge, hello=None, fd=None, stp=None):
100
"""Set bridge parameters.
102
if hello is not None:
103
cmd(CMD_BRCTL, 'sethello', bridge, hello)
105
cmd(CMD_BRCTL, 'setfd', bridge, fd)
107
cmd(CMD_BRCTL, 'stp', bridge, stp)
109
def bridge_del(bridge):
112
cmd(CMD_IFCONFIG, bridge, 'down')
113
cmd(CMD_BRCTL, 'delbr', bridge)
116
# Network interfaces are at /sys/class/net/*.
117
# A bridge interface has ./bridge dir, ./brif is dir of bridged interfaces
118
# (symlinks to the brport dirs).
119
# If an interface is bridged ./brport is bridged port info,
120
# brport/bridge is a symlink to the bridge.
122
INTERFACE_DIR = "/sys/class/net"
124
def isBridge(klass, dev):
125
"""Test if a network interface is a bridge.
127
devdir = os.path.join(klass.INTERFACE_DIR, dev)
128
brdir = os.path.join(devdir, "bridge")
135
isBridge = classmethod(isBridge)
137
def getInterfaces(klass):
138
"""Get a list of the network interfaces.
141
v = os.listdir(klass.INTERFACE_DIR)
147
getInterfaces = classmethod(getInterfaces)
149
def getInterfaceAddr(klass, intf):
150
intfdir = os.path.join(klass.INTERFACE_DIR, intf)
151
addrfile = os.path.join(intfdir, "address")
153
f = file(addrfile, "rb")
154
except Exception, ex:
158
return f.readline().strip()
162
getInterfaceAddr = classmethod(getInterfaceAddr)
164
def getBridges(klass):
165
"""Get a list of the bridges.
167
return [ dev for dev in klass.getInterfaces() if klass.isBridge(dev) ]
169
getBridges = classmethod(getBridges)
171
def getBridgeInterfaces(klass, dev):
172
"""Get a list of the interfaces attached to a bridge.
174
devdir = os.path.join(klass.INTERFACE_DIR, dev)
175
intfdir = os.path.join(devdir, "brif")
177
v = os.listdir(intfdir)
183
getBridgeInterfaces = classmethod(getBridgeInterfaces)
185
def getBridge(klass, dev):
186
"""Get the bridge an interface is attached to (if any).
188
devdir = os.path.join(klass.INTERFACE_DIR, dev)
189
brfile = os.path.join(devdir, "brport/bridge")
191
brpath = os.readlink(brfile)
192
return os.path.basename(brpath)
196
getBridge = classmethod(getBridge)
199
"""Send a command expression to the vnet implementation.
202
(fi, fo) = vnetd_open()
205
fo = file("/proc/vnet/policy", "wb")
214
"""Flush the varp cache.
216
expr = ['varp.flush']
217
return vnet_cmd(expr)
219
def vif_add(vnetid, vmac):
220
"""Tell the vnet implementation to add a vif to a vnet.
222
expr = ['vif.add', ['vnet', vnetid], ['vmac', vmac]]
223
return vnet_cmd(expr)
225
def vif_del(vnetid, vmac):
226
"""Tell the vnet implementation to delete a vif from a vnet.
228
expr = ['vif.del', ['vnet', vnetid], ['vmac', vmac]]
229
return vnet_cmd(expr)
231
def vnet_add(vnetid, vnetif=None, security=None):
232
"""Tell the vnet implementation to add a vnet.
234
expr = ['vnet.add', ['id', vnetid]]
236
expr.append(['vnetif', vnetif])
238
expr.append(['security', security])
239
return vnet_cmd(expr)
241
def peer_add(addr, port=None):
242
expr = ['peer.add', ['addr', addr]]
244
expr.append(['port', port])
245
return vnet_cmd(expr)
247
def peer_del(addr, port=None):
248
expr = ['peer.del', ['addr', addr]]
249
return vnet_cmd(expr)
251
def vnet_del(vnetid):
252
"""Tell the vnet implementation to delete a vnet.
254
expr = ['vnet.del', ['id', vnetid]]
255
return vnet_cmd(expr)
257
def vnet_create(vnetid, vnetif=None, bridge=None, security=None):
258
"""Tell the vnet implementation to add a vnet.
259
If 'bridge' is non-null, create the bridge and add the vnet interface
262
vnet_add(vnetid, vnetif=vnetif, security=security)
263
val = vnet_lookup(vnetid)
265
vnetif = sxp.child_value(val, "vnetif")
266
vmac = get_mac(vnetif)
267
emac = get_mac("eth0") or get_mac("eth1") or get_mac("eth2")
268
if emac and vmac != emac:
269
set_mac(vnetif, emac)
270
cmd(CMD_IFCONFIG, vnetif, 'up')
272
bridge_create(bridge)
273
vif_bridge_add(bridge, vnetif)
276
def vnet_delete(vnet, delbridge=False):
277
"""Tell the vnet implementation to delete a vnet.
278
If the vnet interface is attached to a bridge,
279
remove it from the bridge, and if delbridge is true
282
v = vnet_lookup(vnet)
284
raise GetoptError("vnet not found: %s" % vnet)
285
vnetid = sxp.child_value(v, "id")
286
vnetif = sxp.child_value(v, "vnetif")
287
bridge = Bridge.getBridge(vnetif)
289
vif_bridge_rem(bridge, vnetif)
292
return vnet_del(vnetid)
295
"""Get the mac address of an interface.
298
return Bridge.getInterfaceAddr(intf)
302
hwre = re.compile(".*\s+HWaddr\s+(?P<mac>\S*)\s+.*")
303
fin = os.popen("%s %s" % (CMD_IFCONFIG, intf), 'r')
315
def set_mac(intf, mac):
316
cmd(CMD_IFCONFIG, intf, 'down')
317
cmd(CMD_IFCONFIG, intf, 'hw', 'ether', mac)
318
cmd(CMD_IFCONFIG, intf, 'up')
321
return socket.gethostbyname(host)
327
"""Normalise a vnet id. Adds leading 0 fields to make up 8 if
328
there aren't enough. Pads all fields to 4 hex digits.
332
l = [ int(x or 0, 16) for x in l ]
333
l = [ 0 ] * (8 - len(l)) + l
334
return ":".join([ "%04x" % x for x in l ])
338
def vnet_lookup(vnet, vnets=None):
339
"""Find the vnet with the given vnet id or vnet interface.
341
@param vnet id or interface
342
@param vnets list of vnet info to use (get from implementation if None)
343
@return vnet info or None if not found
345
vnetid = vnetidof(vnet)
349
vid = sxp.child_value(v, "id")
350
if vid == vnet or vid == vnetid:
352
if sxp.child_value(v, "vnetif") == vnet:
356
def get_vnetid(vnet):
357
"""Get the normalised vnet id of the given vnet id or vnet interface.
358
Raises an error if the vnet cannot be found.
360
v = vnet_lookup(vnet)
362
raise GetoptError("vnet not found: %s" % vnet)
363
vnetid = sxp.child_value(v, "id")
367
"""Get the list of vif info from the vnet implementation.
370
(fi, fo) = vnetd_open()
371
sxp.show(['vif.list'], fo)
374
fi = file("/proc/vnet/vifs")
377
return sxp.parse(fi) or []
382
def vnets_filter(vnetlist, vnets):
383
"""Filter a list of vnet info by a list of vnet ids or interfaces.
390
v = vnet_lookup(x, vnets=vnetlist)
396
def vnet_list(vnets=None):
397
"""Get the list of vnet info from the vnet implementation,
400
@param vnets list of vnet ids or interfaces to filter the results by
403
(fi, fo) = vnetd_open()
404
sxp.show(['vnet.list'], fo)
407
fi = file("/proc/vnet/vnets")
410
val = vnets_filter(sxp.parse(fi) or [], vnets)
411
val.sort(lambda x, y:
412
cmp(sxp.child_value(x, "id"),
413
sxp.child_value(y, "id")))
419
def vnif_list(vnets=None):
420
"""Get the list of vnet interface names from the vnet implementation.
422
@param vnets list of vnet ids or interfaces to filter the results by
425
for v in vnet_list(vnets=vnets):
426
vnetif = sxp.child_value(v, "vnetif")
432
"""Get the list of varp info from the vnet implementation.
435
(fi, fo) = vnetd_open()
436
sxp.show(['varp.list'], fo)
439
fi = file("/proc/vnet/varp")
442
return sxp.parse(fi) or []
449
(fi, fo) = vnetd_open()
450
sxp.show(['peer.list'], fo)
453
fi = file("/proc/vnet/peers")
456
return sxp.parse(fi) or []
462
"""Declares command-line options for a command.
465
def getopt(klass, argv, opts, args):
466
"""Get options and args from argv.
467
The value opts in the return value has an attribute for
468
eacho option or arg. The value args in the return value
469
is the remaining arguments.
471
@param argv arguments
472
@param opts option specifiers (list of Opt objects)
473
@param args arg specififiers (list of Arg objects)
476
shortopts = "".join([ x.optShort() for x in opts ])
477
longopts = [ x.optLong() for x in opts ]
478
(ovals, oargs) = getopt(argv[1:], shortopts, longopts)
486
if len(oargs) < len(args):
487
raise GetoptError("insufficient arguments for %s" % argv[0])
488
for (x, v) in zip(args, oargs):
490
return (odir, oargs[len(args): ])
492
getopt = classmethod(getopt)
494
def gethelp(klass, opts, args):
502
gethelp = classmethod(gethelp)
504
"""A command=-line option.
506
@param name option name (this attribute is set to value in opts)
507
@param short short option flag (single-character string)
508
@param long long option name (defaults to option name, pass "" to suppress)
509
@param arg argument name (option has no arg if not specified)
511
def __init__(self, name, short=None, long=None, arg=False):
525
return "[%s | %s]" % (s, l)
531
return "-%s" % self.short
537
return "--%s" % self.long
545
return "%s=" % self.long
553
return "%s:" % self.short
557
def setDefault(self, vals):
559
setattr(vals, self.name, None)
561
setattr(vals, self.name, False)
563
def setOpt(self, k, v, vals):
564
if k in [ self.keyShort(), self.keyLong() ]:
566
setattr(vals, self.name, v)
568
if v not in [ None, '' ]:
569
raise GetoptError("option %s does not take an argument" % k)
570
setattr(vals, self.name, True)
574
"""A command-line parameter. Args get their values from arguments
575
left over after option processing and are assigned in order.
576
The value is accessible as the attribute called 'name' in opts.
578
@param name argument name
580
def __init__(self, name):
583
def setArg(self, v, vals):
584
setattr(vals, self.name, v)
587
return "<%s>" % self.name
591
"""Methods beginning with this prefix are commands.
592
They must all have arguments like this:
594
op_foo(self, argv, args, opts)
596
argv: original command-line arguments
597
args: arguments left after option processing
598
opts: option and arg values (accessible as attributes)
600
Method options are specified by setting attribute
601
.opts on the method to a list of Option objects.
602
For args set .args to a list of Arg objects.
603
Use .use for short usage string, .help for long help.
605
Each option or arg defines an attribute in opts. For example
606
an option with name 'foo' is accessible as 'opts.foo'.
610
def __init__(self, argv):
616
self.argc = len(argv)
619
print >>sys.stderr, "%s: %s" % (self.name, v)
622
def getFunction(self, opname):
623
key = self.opPrefix + opname.replace("-", "_")
624
fn = getattr(self, key, None)
626
raise ValueError("unknown command: %s" % opname)
635
fn = self.getFunction(args[0])
636
except ValueError, ex:
639
fnopts = self.getOpts(fn)
640
fnargs = self.getArgs(fn)
641
(opts, parms) = Opt.getopt(args, fnopts, fnargs)
642
return fn(args, parms, opts)
643
except GetoptError, ex:
645
except ValueError, ex:
647
except Exception, ex:
648
import traceback; traceback.print_exc()
651
def getOpts(self, meth):
652
return getattr(meth, "opts", [])
654
def getArgs(self, meth):
655
return getattr(meth, "args", [])
657
def getUse(self, meth):
658
return getattr(meth, "use", "")
660
def getHelp(self, meth):
661
return getattr(meth, "help", "") or self.getUse(meth)
663
def fnHelp(self, meth):
664
return Opt.gethelp(self.getOpts(meth), self.getArgs(meth))
666
def printHelp(self, fn, opt_long):
667
meth = getattr(self, fn)
668
opname = fn[len(self.opPrefix):].replace("_", "-")
670
help = self.getHelp(meth)
671
print "\n %s" % opname
675
use = self.getUse(meth)
676
print " %s %s" % (opname, self.fnHelp(meth))
680
def show_vnif(self, dev):
681
cmd(CMD_IFCONFIG, dev)
682
bridge = Bridge.getBridge(dev)
684
print " Bridge:", bridge
685
interfaces = Bridge.getBridgeInterfaces(bridge)
686
if dev in interfaces:
687
interfaces.remove(dev)
689
print " Interfaces:", ", ".join(interfaces)
692
def op_help(self, argv, args, opts):
694
print '%s <command> <options>' % self.name
697
print '%s:' % self.name
701
if fn.startswith(self.opPrefix):
702
self.printHelp(fn, opts.long)
705
op_help.opts = [ Opt('long', short='l') ]
707
def op_vnets(self, argv, args, opts):
708
vnets = vnet_list(vnets=args or None)
710
prettyprint(v, width=50)
714
vnif = sxp.child_value(v, "vnetif")
721
vnetids[sxp.child_value(v, "id")] = v
723
vnet = sxp.child_value(v, "vnet")
724
if vnet not in vnetids:
728
for v in varp_list():
732
op_vnets.opts = [ Opt('all', short='a'), Opt('long', short='l') ]
734
def op_vnifs(self, argv, args, opts):
735
vnifs = vnif_list(vnets=args or None)
739
def op_vifs(self, argv, args, opts):
744
def op_varp(self, argv, args, opts):
745
for v in varp_list():
749
def op_varp_flush(self, argv, args, opts):
752
def op_vnet_create(self, argv, args, opts):
753
return vnet_create(opts.vnet,
756
security=opts.security)
758
op_vnet_create.args = [ Arg('vnet') ]
759
op_vnet_create.opts = [ Opt('security', short='s', arg="SECURITY"),
760
Opt('bridge', short='b', arg="BRIDGE"),
761
Opt('vnetif', short='v', arg="VNETIF") ]
763
def op_vnet_delete(self, argv, args, opts):
764
vnetid = get_vnetid(opts.vnet)
765
return vnet_delete(vnetid, delbridge=opts.bridge)
767
op_vnet_delete.args = [ Arg('vnet') ]
768
op_vnet_delete.opts = [ Opt('bridge', short='b') ]
770
def op_vif_add(self, argv, args, opts):
771
vnetid = get_vnetid(opts.vnet)
773
vmac = get_mac(opts.vmac)
775
raise ValueError("interface not found: %s" % opts.vmac)
778
return vif_add(vnetid, vmac)
780
op_vif_add.args = [ Arg('vnet'), Arg('vmac') ]
781
op_vif_add.opts = [ Opt('interface', short='i') ]
783
def op_vif_delete(self, argv, args, opts):
784
vnetid = get_vnetid(opts.vnet)
786
vmac = get_mac(opts.vmac)
789
return vif_del(vnetid, vmac)
791
op_vif_delete.args = [ Arg('vnet'), Arg('vmac') ]
792
op_vif_delete.opts = [ Opt('interface', short='i') ]
794
def op_peer_add(self, argv, args, opts):
795
addr = get_addr(opts.addr)
797
port = get_port(opts.port)
800
return peer_add(addr, port)
802
op_peer_add.args = [ Arg('addr') ]
803
op_peer_add.opts = [ Opt('port', short='p') ]
805
def op_peer_delete(self, argv, args, opts):
806
addr = get_addr(opts.addr)
807
return peer_del(addr)
809
op_peer_delete.args = [ Arg('addr') ]
811
def op_peers(self, argv, args, opts):
812
for v in peer_list():
816
def op_bridges(self, argv, args, opts):
818
for bridge in Bridge.getBridges():
819
cmd(CMD_IFCONFIG, bridge)
820
interfaces = Bridge.getBridgeInterfaces(bridge)
822
print " Interfaces:", ", ".join(interfaces)
825
for bridge in Bridge.getBridges():
827
interfaces = Bridge.getBridgeInterfaces(bridge)
829
print ":", ", ".join(interfaces)
833
op_bridges.opts = [ Opt('long', short='l') ]
835
def op_insmod(self, argv, args, opts):
836
"""Insert the vnet kernel module."""
837
cmd("/etc/xen/scripts/vnet-insert", *args)
839
long_help = """Control utility for vnets (virtual networking).
840
Report bugs to Mike Wray <mike.wray@hp.com>.
843
op_help.use = "Print help."
844
op_help.help = "Print help, long help if the option -l or --long is given."
846
op_vnets.use = """Print vnets."""
847
op_vnets.help = """Print vnet information, where options are:
848
-a, -all Print vnets, vifs and varp info.
849
-l, --long Print ifconfigs for vnet interfaces."""
851
op_vifs.use = "Print vifs."
853
op_vnifs.use = "Print ifconfigs for vnet network interfaces."
855
op_varp.use = "Print varp info and entries in the varp cache."
857
op_varp_flush.use = "Flush the varp cache."
859
op_vnet_create.use = "Create a vnet."
861
op_vnet_delete.use = "Delete a vnet."
862
op_vnet_delete.help = """Delete a vnet.
863
-b, --bridge Delete the bridge the vnet interface is attached to.
866
op_vif_add.use = "Add a vif to a vnet."
867
op_vif_add.help = """Add a vif to a vnet. Not usually needed as vifs
868
are added automatically.
869
-i, --interface The vmac is the name of an interface to get the mac from."""
871
op_vif_delete.use = "Delete a vif from a vnet."
872
op_vif_delete.help = """Delete a vif from a vnet. Not usually needed as vifs
873
are removed periodically.
874
-i, --interface The vmac is the name of an interface to get the mac from."""
876
op_peer_add.use = "Add a peer."
877
op_peer_add.help = """Add a peer: <addr> <port>
878
Vnets use multicast to discover interfaces, but networks are often configured
879
not to forward multicast. Vnets forward multicasts to peers using UDP.
880
Only add peers if multicasts are not working, check with
884
Only add peers at one machine in a subnet, otherwise you may cause forwarding
888
op_peer_delete.use = "Delete a peer."
889
op_peer_delete.help= "Delete a peer: <addr>"
891
op_peers.use = "List peers."
892
op_peers.help = "List peers."
894
op_bridges.use = "Print bridges."
896
op_insmod.use = "Insert the vnet kernel module, optionally with parameters."
898
if __name__ == "__main__":
899
vn = VnMain(sys.argv)