# HG changeset patch # User stekloff@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # Node ID 38ccc3bbb68f80511363b961e84fc912d29e6fce # Parent 45aaca4aed38771243e32414cc6964f306751bda Add new networking infrastructure to Xm-Test. The goal is to make creating domains with networking very easy. This patch: 1) Adds new XenDevice class, with the XenNetDevice subclass. These classes represent devices for xm-test and are tied to XenDomains. This can eventually be used for block devices as well. Currently, devices must be added to domains prior to starting the domain. The attach and detach needs to be handled. 2) Adds a new NetConfig class to handle configuring the network environment in which the tests run. This patch only handles ranges of IPs in a bridged environment. DHCP needs to be added as well as handling NAT and routed environments. 3) Modifies XenDomain class to handle XenDevices. 4) Adds new configuration options for defining a range of IPs, their network address, and their netmask. 5) Removes the old Network.py and Network class. 6) Modifies the existing tests to use the new infrastructure. 7) Adds some documentation to help creating domains. Signed-off-by: Daniel Stekloff diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/configure.ac --- a/tools/xm-test/configure.ac Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/configure.ac Mon Apr 24 21:52:26 2006 @@ -37,6 +37,36 @@ AM_CONDITIONAL(HVM, test x$ENABLE_HVM = xTrue) AC_SUBST(ENABLE_HVM) + +# Network needs to know ips to use: dhcp or a range of IPs in the form +# of: 192.168.1.1-192.168.1.100 +# If not dhcp, a netmask and network address must be supplied. Defaults to +# zeroconf range. +NET_IP_RANGE="169.254.0.1-169.254.255.255" +AC_ARG_WITH(net-ip-range, + [ --with-net-ip-range=ip-range Set a range of ip addresses to use for xm-test guest domain networks. Can specify dhcp or a range of IPs: 192.168.1.1-192.168.1.100 [[default="169.254.0.1-169.254.255.255"]]], + [ NET_IP_RANGE="$withval" ]) + +iprange=`echo $NET_IP_RANGE | perl -e 'while(<>) { print if /\d+\.\d+\.\d+\.\d+-\d+\.\d+\.\d+\.\d+/ }'` + +NETWORK_ADDRESS="169.254.0.0" +AC_ARG_WITH(network-address, + [ --with-network-address=ip Set network address to use with ip range [[default="169.254.0.0"]]], + [ NETWORK_ADDRESS="$withval" ]) + +NETMASK="255.255.0.0" +AC_ARG_WITH(netmask, + [ --with-netmask=mask Set netmask to use with ip range [[default="255.255.0.0"]]], + [ NETMASK="$withval" ]) + +if test "x$NET_IP_RANGE" != "xdhcp" && test -z "$iprange" +then + AC_MSG_ERROR(Invalid net-ip-range.) +fi + +AC_SUBST(NET_IP_RANGE) +AC_SUBST(NETWORK_ADDRESS) +AC_SUBST(NETMASK) AC_ARG_WITH(hvm-kernel, [[ --with-hvm-kernel=kernel Use this kernel for hvm disk.img testing]], diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/lib/XmTestLib/XenDomain.py --- a/tools/xm-test/lib/XmTestLib/XenDomain.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/lib/XmTestLib/XenDomain.py Mon Apr 24 21:52:26 2006 @@ -28,6 +28,7 @@ from Test import * from config import * from Console import * +from XenDevice import * BLOCK_ROOT_DEV = "hda" @@ -195,6 +196,8 @@ self.config = config self.console = None + self.devices = {} + self.netEnv = "bridge" # Set domain type, either PV for ParaVirt domU or HVM for # FullVirt domain @@ -216,6 +219,10 @@ if self.getDomainType() == "HVM": waitForBoot() + # Go through device list and run console cmds + for dev in self.devices.keys(): + self.devices[dev].execAddCmds() + if self.console and noConsole == True: self.closeConsole() @@ -229,6 +236,8 @@ prog = "xm" cmd = " shutdown " + self.removeAllDevices() + if self.console: self.closeConsole() @@ -239,6 +248,8 @@ def destroy(self): prog = "xm" cmd = " destroy " + + self.removeAllDevices() if self.console: self.closeConsole() @@ -273,6 +284,50 @@ self.console.sendInput("input") return self.console + + def newDevice(self, Device, *args): + """Device Factory: Generic factory for creating new XenDevices. + All device creation should be done through the XenDomain + factory. Supply a XenDevice instance and its args and the + constructor will be called.""" + # Make sure device with id hasn't already been added + if self.devices.has_key(args[0]): + raise DeviceError("Error: Domain already has device %s" % args[0]) + + # Call constructor for supplied Device instance + dargs = (self,) + dargs += args + dev = apply(Device, dargs) + + if self.isRunning(): + # Note: This needs to be done, XenDevice should have an attach + # method. + print "Domain is running, need to attach new device to domain." + + self.devices[dev.id] = dev + self.config.appOpt(dev.configNode, str(dev)) + return dev + + def removeDevice(self, id): + if self.devices.has_key(id): + self.devices[id].removeDevice() + + def removeAllDevices(self): + for k in self.devices.keys(): + self.removeDevice(k) + + def isRunning(self): + return isDomainRunning(self.name) + + def getNetEnv(self): + # We need to know the network environment: bridge, NAT, or routed. + return self.netEnv + + def getDevice(self, id): + dev = self.devices[id] + if dev: + return dev + print "Device %s not found for domain %s" % (id, self.getName()) class XmTestDomain(XenDomain): @@ -298,6 +353,30 @@ def minSafeMem(self): return 32 +class XmTestNetDomain(XmTestDomain): + + def __init__(self, name=None, extraConfig=None, baseConfig=configDefaults): + """Create a new xm-test domain with one network device + @param name: The requested domain name + @param extraConfig: Additional configuration options + @param baseConfig: The initial configuration defaults to use + """ + config = XenConfig() + config.setOpts(baseConfig) + if extraConfig: + config.setOpts(extraConfig) + + if name: + config.setOpt("name", name) + elif not config.getOpt("name"): + config.setOpt("name", getUniqueName()) + + XenDomain.__init__(self, config.getOpt("name"), config=config) + + # Add one network devices to domain + self.newDevice(XenNetDevice, "eth0") + + if __name__ == "__main__": c = XenConfig() diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/lib/XmTestLib/__init__.py --- a/tools/xm-test/lib/XmTestLib/__init__.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/lib/XmTestLib/__init__.py Mon Apr 24 21:52:26 2006 @@ -4,11 +4,15 @@ # from Console import * -from Network import * from Test import * from Xm import * from XenDomain import * from config import * +from XenDevice import * +from NetConfig import * + +# Make sure xen modules are in path +sys.path.append('/usr/lib/python') # Give this test a clean slate destroyAllDomUs(); @@ -18,6 +22,8 @@ else: verbose = False - if verbose: timeStamp() + +# We need to track network configuration, like ips, etc. +xmtest_netconf = NetConfig() diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/lib/XmTestLib/config.py.in --- a/tools/xm-test/lib/XmTestLib/config.py.in Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/lib/XmTestLib/config.py.in Mon Apr 24 21:52:26 2006 @@ -1,4 +1,6 @@ #!/usr/bin/python ENABLE_HVM_SUPPORT = @ENABLE_HVM@ - +NETWORK_IP_RANGE = "@NET_IP_RANGE@" +NETWORK = "@NETWORK_ADDRESS@" +NETMASK = "@NETMASK@" diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/tests/create/13_create_multinic_pos.py --- a/tools/xm-test/tests/create/13_create_multinic_pos.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/tests/create/13_create_multinic_pos.py Mon Apr 24 21:52:26 2006 @@ -5,17 +5,14 @@ from XmTestLib import * -# The current device model, qemu-dm, only supports 8 MAX_NICS currently. +# The device model, qemu-dm, only supports 8 MAX_NICS currently. if ENABLE_HVM_SUPPORT: MAX_NICS = 8 - nic = "type=ioemu, bridge=xenbr0" else: MAX_NICS = 10 - nic = '' for i in range(0,MAX_NICS): - config = {"vif": [ nic ] * i} - domain = XmTestDomain(extraConfig=config) + domain = XmTestNetDomain() try: console = domain.start() diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/tests/network/02_network_local_ping_pos.py --- a/tools/xm-test/tests/network/02_network_local_ping_pos.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/tests/network/02_network_local_ping_pos.py Mon Apr 24 21:52:26 2006 @@ -16,24 +16,17 @@ pingsizes = [ 1, 48, 64, 512, 1440, 1500, 1505, 4096, 4192, 32767, 65507 ] - - from XmTestLib import * rc = 0 -Net = XmNetwork() +# Test creates 1 domain, which requires 2 ips: 1 for the domains and 1 for +# aliases on dom0 +if xmtest_netconf.canRunNetTest(2) == False: + SKIP("Don't have enough free configured IPs to run this test") -# read an IP address from the config -ip = Net.ip("dom1", "eth0") -mask = Net.mask("dom1", "eth0") +domain = XmTestDomain() +domain.newDevice(XenNetDevice, "eth0") -# Fire up a guest domain w/1 nic -if ENABLE_HVM_SUPPORT: - config = {"vif" : ['type=ioemu']} -else: - config = {"vif" : ['ip=%s' % ip ]} - -domain = XmTestDomain(extraConfig=config) try: console = domain.start() except DomainError, e: @@ -43,10 +36,7 @@ FAIL(str(e)) try: - # Bring up the "lo" interface. - console.runCmd("ifconfig lo 127.0.0.1") - - console.runCmd("ifconfig eth0 inet "+ip+" netmask "+mask+" up") + console.setHistorySaveCmds(value=True) # First the loopback pings lofails="" @@ -57,6 +47,8 @@ # Next comes eth0 eth0fails="" + netdev = domain.getDevice("eth0") + ip = netdev.getNetDevIP() for size in pingsizes: out = console.runCmd("ping -q -c 1 -s " + str(size) + " " + ip) if out["return"]: @@ -66,6 +58,7 @@ except NetworkError, e: FAIL(str(e)) +domain.stop() # Tally up failures failures="" diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/tests/network/03_network_local_tcp_pos.py --- a/tools/xm-test/tests/network/03_network_local_tcp_pos.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/tests/network/03_network_local_tcp_pos.py Mon Apr 24 21:52:26 2006 @@ -17,28 +17,18 @@ trysizes = [ 1, 48, 64, 512, 1440, 1448, 1500, 1505, 4096, 4192, 32767, 65495 ] - from XmTestLib import * rc = 0 -Net = XmNetwork() - -try: - # read an IP address from the config - ip = Net.ip("dom1", "eth0") - mask = Net.mask("dom1", "eth0") -except NetworkError, e: - FAIL(str(e)) +# Test creates 1 domain, which requires 2 ips: 1 for the domains and 1 for +# aliases on dom0 +if xmtest_netconf.canRunNetTest(2) == False: + SKIP("Don't have enough free configured IPs to run this test") # Fire up a guest domain w/1 nic -if ENABLE_HVM_SUPPORT: - brg = "xenbr0" - config = {"vif" : ['type=ioemu, bridge=%s' % brg]} -else: - brg = None - config = {"vif" : ['ip=%s' % ip]} +domain = XmTestDomain() +domain.newDevice(XenNetDevice, "eth0") -domain = XmTestDomain(extraConfig=config) try: console = domain.start() except DomainError, e: @@ -48,10 +38,7 @@ FAIL(str(e)) try: - # Bring up the "lo" interface. - console.runCmd("ifconfig lo 127.0.0.1") - - console.runCmd("ifconfig eth0 inet "+ip+" netmask "+mask+" up") + console.setHistorySaveCmds(value=True) # First do loopback lofails="" @@ -63,6 +50,8 @@ # Next comes eth0 eth0fails="" + netdev = domain.getDevice("eth0") + ip = netdev.getNetDevIP() for size in trysizes: out = console.runCmd("hping2 " + ip + " -E /dev/urandom -q -c 20 " + "--fast -d "+ str(size)) @@ -73,6 +62,7 @@ except NetworkError, e: FAIL(str(e)) +domain.stop() # Tally up failures failures="" diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/tests/network/04_network_local_udp_pos.py --- a/tools/xm-test/tests/network/04_network_local_udp_pos.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/tests/network/04_network_local_udp_pos.py Mon Apr 24 21:52:26 2006 @@ -20,24 +20,14 @@ from XmTestLib import * rc = 0 -Net = XmNetwork() +# Test creates 1 domain, which requires 2 ips: 1 for the domains and 1 for +# aliases on dom0 +if xmtest_netconf.canRunNetTest(2) == False: + SKIP("Don't have enough free configured IPs to run this test") -try: - # read an IP address from the config - ip = Net.ip("dom1", "eth0") - mask = Net.mask("dom1", "eth0") -except NetworkError, e: - FAIL(str(e)) +domain = XmTestDomain() +domain.newDevice(XenNetDevice, "eth0") -# Fire up a guest domain w/1 nic -if ENABLE_HVM_SUPPORT: - brg = "xenbr0" - config = {"vif" : ['type=ioemu, bridge=%s' % brg]} -else: - brg = None - config = {"vif" : ['ip=%s' % ip]} - -domain = XmTestDomain(extraConfig=config) try: console = domain.start() except DomainError, e: @@ -47,10 +37,7 @@ FAIL(str(e)) try: - # Bring up the "lo" interface. - console.runCmd("ifconfig lo 127.0.0.1") - - console.runCmd("ifconfig eth0 inet "+ip+" netmask "+mask+" up") + console.setHistorySaveCmds(value=True) # First do loopback lofails="" @@ -63,6 +50,8 @@ # Next comes eth0 eth0fails="" + netdev = domain.getDevice("eth0") + ip = netdev.getNetDevIP() for size in trysizes: out = console.runCmd("hping2 " + ip + " -E /dev/urandom -2 -q -c 20 " + "--fast -d " + str(size)) @@ -74,6 +63,7 @@ except NetworkError, e: FAIL(str(e)) +domain.stop() # Tally up failures failures="" diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/tests/network/05_network_dom0_ping_pos.py --- a/tools/xm-test/tests/network/05_network_dom0_ping_pos.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/tests/network/05_network_dom0_ping_pos.py Mon Apr 24 21:52:26 2006 @@ -16,29 +16,18 @@ pingsizes = [ 1, 48, 64, 512, 1440, 1500, 1505, 4096, 4192, 32767, 65507 ] - - from XmTestLib import * rc = 0 -Net = XmNetwork() - -try: - # read an IP address from the config - ip = Net.ip("dom1", "eth0") - mask = Net.mask("dom1", "eth0") -except NetworkError, e: - FAIL(str(e)) +# Test creates 1 domain, which requires 2 ips: 1 for the domains and 1 for +# aliases on dom0 +if xmtest_netconf.canRunNetTest(2) == False: + SKIP("Don't have enough free configured IPs to run this test") # Fire up a guest domain w/1 nic -if ENABLE_HVM_SUPPORT: - brg = "xenbr0" - config = {"vif" : ['type=ioemu, bridge=%s' % brg]} -else: - config = {"vif" : ['ip=%s' % ip ]} - brg = None +domain = XmTestDomain() +domain.newDevice(XenNetDevice, "eth0") -domain = XmTestDomain(extraConfig=config) try: console = domain.start() except DomainError, e: @@ -48,16 +37,10 @@ FAIL(str(e)) try: - # Add a suitable dom0 IP address - dom0ip = Net.ip("dom0", "eth0", todomname=domain.getName(), toeth="eth0", bridge=brg) -except NetworkError, e: - FAIL(str(e)) - -try: - console.runCmd("ifconfig eth0 inet "+ip+" netmask "+mask+" up") - # Ping dom0 fails="" + netdev = domain.getDevice("eth0") + dom0ip = netdev.getDom0AliasIP() for size in pingsizes: out = console.runCmd("ping -q -c 1 -s " + str(size) + " " + dom0ip) if out["return"]: @@ -65,6 +48,7 @@ except ConsoleError, e: FAIL(str(e)) +domain.stop() + if len(fails): FAIL("Ping to dom0 failed for size" + fails + ".") - diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/tests/network/06_network_dom0_tcp_pos.py --- a/tools/xm-test/tests/network/06_network_dom0_tcp_pos.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/tests/network/06_network_dom0_tcp_pos.py Mon Apr 24 21:52:26 2006 @@ -16,31 +16,21 @@ trysizes = [ 1, 48, 64, 512, 1440, 1500, 1505, 4096, 4192, 32767, 65495 ] - - from XmTestLib import * rc = 0 -Net = XmNetwork() +# Test creates 1 domain, which requires 2 ips: 1 for the domains and 1 for +# aliases on dom0 +if xmtest_netconf.canRunNetTest(2) == False: + SKIP("Don't have enough free configured IPs to run this test") + +# Fire up a guest domain w/1 nic +domain = XmTestDomain() +domain.newDevice(XenNetDevice, "eth0") try: - # read an IP address from the config - ip = Net.ip("dom1", "eth0") - mask = Net.mask("dom1", "eth0") -except NetworkError, e: - FAIL(str(e)) - -# Fire up a guest domain w/1 nic -if ENABLE_HVM_SUPPORT: - brg = "xenbr0" - config = {"vif" : ['type=ioemu, bridge=%s' % brg]} -else: - brg = None - config = {"vif" : ["ip=%s" % ip]} - -domain = XmTestDomain(extraConfig=config) -try: console = domain.start() + console.setHistorySaveCmds(value=True) except DomainError, e: if verbose: print "Failed to create test domain because:" @@ -48,16 +38,10 @@ FAIL(str(e)) try: - # Add a suitable dom0 IP address - dom0ip = Net.ip("dom0", "eth0", todomname=domain.getName(), toeth="eth0", bridge=brg) -except NetworkError, e: - FAIL(str(e)) - -try: - console.runCmd("ifconfig eth0 inet "+ip+" netmask "+mask+" up") - # Ping dom0 fails="" + netdev = domain.getDevice("eth0") + dom0ip = netdev.getDom0AliasIP() for size in trysizes: out = console.runCmd("hping2 " + dom0ip + " -E /dev/urandom -q -c 20 " + "--fast -d " + str(size)) @@ -67,6 +51,7 @@ except ConsoleError, e: FAIL(str(e)) +domain.stop() + if len(fails): FAIL("TCP hping2 to dom0 failed for size" + fails + ".") - diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/tests/network/07_network_dom0_udp_pos.py --- a/tools/xm-test/tests/network/07_network_dom0_udp_pos.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/tests/network/07_network_dom0_udp_pos.py Mon Apr 24 21:52:26 2006 @@ -16,29 +16,18 @@ trysizes = [ 1, 48, 64, 512, 1440, 1500, 1505, 4096, 4192, 32767, 65495 ] - - from XmTestLib import * rc = 0 -Net = XmNetwork() - -try: - # read an IP address from the config - ip = Net.ip("dom1", "eth0") - mask = Net.mask("dom1", "eth0") -except NetworkError, e: - FAIL(str(e)) +# Test creates 1 domain, which requires 2 ips: 1 for the domains and 1 for +# aliases on dom0 +if xmtest_netconf.canRunNetTest(2) == False: + SKIP("Don't have enough free configured IPs to run this test") # Fire up a guest domain w/1 nic -if ENABLE_HVM_SUPPORT: - brg = "xenbr0" - config = {"vif" : ['type=ioemu, bridge=%s' % brg]} -else: - brg = None - config = {"vif" : ["ip=%s" % ip]} +domain = XmTestDomain() +domain.newDevice(XenNetDevice, "eth0") -domain = XmTestDomain(extraConfig=config) try: console = domain.start() except DomainError, e: @@ -48,16 +37,10 @@ FAIL(str(e)) try: - # Add a suitable dom0 IP address - dom0ip = Net.ip("dom0", "eth0", todomname=domain.getName(), toeth="eth0", bridge=brg) -except NetworkError, e: - FAIL(str(e)) - -try: - console.runCmd("ifconfig eth0 inet "+ip+" netmask "+mask+" up") - # Ping dom0 fails="" + netdev = domain.getDevice("eth0") + dom0ip = netdev.getDom0AliasIP() for size in trysizes: out = console.runCmd("hping2 " + dom0ip + " -E /dev/urandom -2 -q -c 20" + " --fast -d " + str(size)) @@ -67,6 +50,7 @@ except ConsoleError, e: FAIL(str(e)) +domain.stop() + if len(fails): FAIL("UDP hping2 to dom0 failed for size" + fails + ".") - diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/tests/network/11_network_domU_ping_pos.py --- a/tools/xm-test/tests/network/11_network_domU_ping_pos.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/tests/network/11_network_domU_ping_pos.py Mon Apr 24 21:52:26 2006 @@ -17,50 +17,37 @@ from XmTestLib import * -def netDomain(ip): - if ENABLE_HVM_SUPPORT: - config = {"vif" : ['type=ioemu']} - else: - config = {"vif" : ['ip=%s' % ip ]} +def netDomain(): - dom = XmTestDomain(extraConfig=config) + dom = XmTestDomain() + dom.newDevice(XenNetDevice, "eth0") try: console = dom.start() + console.setHistorySaveCmds(value=True) except DomainError, e: if verbose: print "Failed to create test domain because:" print e.extra FAIL(str(e)) - return console + return dom rc = 0 -Net = XmNetwork() +# Test creates 2 domains, which requires 4 ips: 2 for the domains and 2 for +# aliases on dom0 +if xmtest_netconf.canRunNetTest(4) == False: + SKIP("Don't have enough free configured IPs to run this test") + +# Fire up a pair of guest domains w/1 nic each +pinger = netDomain() +pinger_console = pinger.getConsole() +victim = netDomain() try: - # pick an IP address - ip1 = Net.ip("dom1", "eth2") - mask1 = Net.mask("dom1", "eth2") -except NetworkError, e: - FAIL(str(e)) - -try: - # pick another IP address - ip2 = Net.ip("dom2", "eth2") - mask2 = Net.mask("dom2", "eth2") -except NetworkError, e: - FAIL(str(e)) - -# Fire up a pair of guest domains w/1 nic each -pinger_console = netDomain(ip1) -victim_console = netDomain(ip2) - -try: - pinger_console.runCmd("ifconfig eth0 inet "+ip1+" netmask "+mask1+" up") - victim_console.runCmd("ifconfig eth0 inet "+ip2+" netmask "+mask2+" up") - # Ping the victim over eth0 fails="" + v_netdev = victim.getDevice("eth0") + ip2 = v_netdev.getNetDevIP() for size in pingsizes: out = pinger_console.runCmd("ping -q -c 1 -s " + str(size) + " " + ip2) if out["return"]: @@ -68,6 +55,8 @@ except ConsoleError, e: FAIL(str(e)) +pinger.stop() +victim.stop() + if len(fails): FAIL("Ping failed for size" + fails + ".") - diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/tests/network/12_network_domU_tcp_pos.py --- a/tools/xm-test/tests/network/12_network_domU_tcp_pos.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/tests/network/12_network_domU_tcp_pos.py Mon Apr 24 21:52:26 2006 @@ -17,50 +17,37 @@ from XmTestLib import * -def netDomain(ip): - if ENABLE_HVM_SUPPORT: - config = {"vif" : ['type=ioemu']} - else: - config = {"vif" : ["ip=%s" % ip]} +def netDomain(): - dom = XmTestDomain(extraConfig=config) + dom = XmTestDomain() + dom.newDevice(XenNetDevice, "eth0") try: console = dom.start() + console.setHistorySaveCmds(value=True) except DomainError, e: if verbose: print "Failed to create test domain because:" print e.extra FAIL(str(e)) - return console + return dom rc = 0 -Net = XmNetwork() +# Test creates 2 domains, which requires 4 ips: 2 for the domains and 2 for +# aliases on dom0 +if xmtest_netconf.canRunNetTest(4) == False: + SKIP("Don't have enough free configured IPs to run this test") + +# Fire up a pair of guest domains w/1 nic each +src = netDomain() +src_console = src.getConsole() +dst = netDomain() try: - # pick an IP address - ip1 = Net.ip("dom1", "eth2") - mask1 = Net.mask("dom1", "eth2") -except NetworkError, e: - FAIL(str(e)) - -try: - # pick another IP address - ip2 = Net.ip("dom2", "eth2") - mask2 = Net.mask("dom2", "eth2") -except NetworkError, e: - FAIL(str(e)) - -# Fire up a pair of guest domains w/1 nic each -src_console = netDomain(ip1) -dst_console = netDomain(ip2) - -try: - src_console.runCmd("ifconfig eth0 inet "+ip1+" netmask "+mask1+" up") - dst_console.runCmd("ifconfig eth0 inet "+ip2+" netmask "+mask2+" up") - # Ping the victim over eth0 fails="" + dst_netdev = dst.getDevice("eth0") + ip2 = dst_netdev.getNetDevIP() for size in pingsizes: out = src_console.runCmd("hping2 " + ip2 + " -E /dev/urandom -q -c 20 " + "--fast -d " + str(size)) @@ -70,6 +57,8 @@ except ConsoleError, e: FAIL(str(e)) +src.stop() +dst.stop() + if len(fails): FAIL("TCP hping2 failed for size" + fails + ".") - diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/tests/network/13_network_domU_udp_pos.py --- a/tools/xm-test/tests/network/13_network_domU_udp_pos.py Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/tests/network/13_network_domU_udp_pos.py Mon Apr 24 21:52:26 2006 @@ -17,50 +17,37 @@ from XmTestLib import * -def netDomain(ip): - if ENABLE_HVM_SUPPORT: - config = {"vif" : ['type=ioemu']} - else: - config = {"vif" : ["ip=%s" % ip]} +def netDomain(): - dom = XmTestDomain(extraConfig=config) + dom = XmTestDomain() + dom.newDevice(XenNetDevice, "eth0") try: console = dom.start() + console.setHistorySaveCmds(value=True) except DomainError, e: if verbose: print "Failed to create test domain because:" print e.extra FAIL(str(e)) - return console + return dom rc = 0 -Net = XmNetwork() +# Test creates 2 domains, which requires 4 ips: 2 for the domains and 2 for +# aliases on dom0 +if xmtest_netconf.canRunNetTest(4) == False: + SKIP("Don't have enough free configured IPs to run this test") + +# Fire up a pair of guest domains w/1 nic each +src = netDomain() +src_console = src.getConsole() +dst = netDomain() try: - # pick an IP address - ip1 = Net.ip("dom1", "eth2") - mask1 = Net.mask("dom1", "eth2") -except NetworkError, e: - FAIL(str(e)) - -try: - # pick another IP address - ip2 = Net.ip("dom2", "eth2") - mask2 = Net.mask("dom2", "eth2") -except NetworkError, e: - FAIL(str(e)) - -# Fire up a pair of guest domains w/1 nic each -src_console = netDomain(ip1) -dst_console = netDomain(ip2) - -try: - src_console.runCmd("ifconfig eth0 inet "+ip1+" netmask "+mask1+" up") - dst_console.runCmd("ifconfig eth0 inet "+ip2+" netmask "+mask2+" up") - # Ping the victim over eth0 fails="" + dst_netdev = dst.getDevice("eth0") + ip2 = dst_netdev.getNetDevIP() for size in pingsizes: out = src_console.runCmd("hping2 " + ip2 + " -E /dev/urandom -2 -q " + "-c 20 --fast -d " + str(size)) @@ -70,6 +57,8 @@ except ConsoleError, e: FAIL(str(e)) +src.stop() +dst.stop() + if len(fails): FAIL("UDP hping2 failed for size" + fails + ".") - diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/lib/XmTestLib/NetConfig.py --- /dev/null Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/lib/XmTestLib/NetConfig.py Mon Apr 24 21:52:26 2006 @@ -0,0 +1,264 @@ +#!/usr/bin/python +""" + Copyright (C) International Business Machines Corp., 2005, 2006 + Authors: Dan Smith + Daniel Stekloff + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; under version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" + +import sys +import commands +import os +import re +import time +import random +from xen.xend.sxp import Parser + +from Xm import * +from Test import * +from config import * + +class NetworkError(Exception): + def __init__(self, msg): + self.errMsg = msg + + def __str__(self): + return str(self.errMsg) + +def getXendNetConfig(): + # Find out what environment we're in: bridge, nat, or route + xconfig = os.getenv("XEND_CONFIG") + if not xconfig: + xconfig = "/etc/xen/xend-config.sxp" + + configfile = open(xconfig, 'r') + S = configfile.read() + pin = Parser() + pin.input(S) + pin.input_eof() + val = pin.get_val() + while val[0] != 'network-script': + val = pin.get_val() + + if val[1] == "network-bridge": + netenv = "bridge" + elif val[1] == "network-route": + netenv = "route" + elif val[1] == "network-nat": + netenv = "nat" + else: + raise NetworkError("Failed to get network env from xend config") + + configfile.close() + return netenv + +def checkZeroconfAddresses(): + # Make sure there aren't existing zeroconf addresses. + rc, out = traceCommand("ip addr show |grep \"inet 169.254\" | grep -v vif") + if rc == 0: + raise NetworkError("Zeroconf addresses already used: %s" % out) + +class NetConfig: + + def __init__(self): + self.netenv = getXendNetConfig() + self.used_ips = {} + self.free_oct_ips = [ 0, 0, 0, 0 ] + self.total_ips = 0 + + if NETWORK_IP_RANGE == 'dhcp': + self.netmask = NETWORK_IP_RANGE + self.network = NETWORK_IP_RANGE + self.max_ip = NETWORK_IP_RANGE + self.min_ip = NETWORK_IP_RANGE + else: + self.netmask = NETMASK + self.network = NETWORK + s_ip = '' + + # Get starting ip and max ip from configured ip range + s_ip = NETWORK_IP_RANGE + ips = s_ip.split("-") + self.max_ip = ips[1] + self.min_ip = ips[0] + + self.__setMaxNumberIPs() + + if self.network == "169.254.0.0": + checkZeroconfAddresses() + + # Clean out any aliases in the network range for vif0.0. If + # an alias exists, a test xendevice add command could fail. + if NETWORK_IP_RANGE != "dhcp": + self.__cleanDom0Aliases() + + def __setMaxNumberIPs(self): + # Count the number of IPs available, to help tests know whether they + # have enough to run or not + masko = self.netmask.split('.') + maxo = self.max_ip.split('.') + mino = self.min_ip.split('.') + ips = 0 + + # Last octet + self.free_oct_ips[3] = (int(maxo[3]) - int(mino[3])) + 1 + + # 3rd octet + self.free_oct_ips[2] = (int(maxo[2]) - int(mino[2])) + 1 + + # 2nd octet + self.free_oct_ips[1] = (int(maxo[1]) - int(mino[1])) + 1 + + # 1st octet + self.free_oct_ips[0] = (int(maxo[0]) - int(mino[0])) + 1 + + self.total_ips = self.free_oct_ips[3] + if self.free_oct_ips[2] > 1: + self.total_ips = (self.total_ips * self.free_oct_ips[2]) + if self.free_oct_ips[1] > 1: + self.total_ips = (self.total_ips * self.free_oct_ips[1]) + if self.free_oct_ips[0] > 1: + self.total_ips = (self.total_ips * self.free_oct_ips[0]) + + def __cleanDom0Aliases(self): + # Remove any aliases within the supplied network IP range on dom0 + scmd = 'ip addr show dev vif0.0' + + status, output = traceCommand(scmd) + if status: + raise NetworkError("Failed to show vif0.0 aliases: %d" % status) + + lines = output.split("\n") + for line in lines: + ip = re.search('(\d+\.\d+\.\d+\.\d+)', line) + if ip and self.isIPInRange(ip.group(1)) == True: + dcmd = 'ip addr del %s dev vif0.0' % ip.group(1) + dstatus, doutput = traceCommand(dcmd) + if dstatus: + raise NetworkError("Failed to remove vif0.0 aliases: %d" % status) + + def getNetEnv(self): + return self.netenv + + def setUsedIP(self, domname, interface, ip): + self.used_ips['%s:%s' % (domname, interface)] = ip + + def __findFirstOctetIP(self, prefix, min, max): + for i in range(min, max): + ip = '%s%s' % (prefix, str(i)) + found = False + for k in self.used_ips.keys(): + if self.used_ips[k] == ip: + found = True + if found == False: + return ip + + if found == True: + return None + + def getFreeIP(self, domname, interface): + # Get a free IP. It uses the starting ip octets and then the + # total number of allowed numbers for that octet. It only + # calculates ips for the last two octets, we shouldn't need more + start_octets = self.min_ip.split(".") + ip = None + + # Only working with ips from last two octets, shouldn't need more + max = int(start_octets[2]) + self.free_oct_ips[2] + for i in range(int(start_octets[2]), max): + prefix = '%s.%s.%s.' % (start_octets[0], start_octets[1], str(i)) + ip = self.__findFirstOctetIP(prefix, int(start_octets[3]), self.free_oct_ips[3]) + if ip: + break + + if not ip: + raise NetworkError("Ran out of configured addresses.") + + self.setUsedIP(domname, interface, ip) + return ip + + def getNetMask(self): + return self.netmask + + def getNetwork(self): + return self.network + + def getIP(self, domname, interface): + # Depending on environment, set an IP. Uses the configured range + # of IPs, network address, and netmask + if NETWORK_IP_RANGE == "dhcp": + return None + + # Make sure domain and interface aren't already assigned an IP + if self.used_ips.has_key('%s:%s' % (domname, interface)): + raise NetworkError("Domain %s interface %s is already has IP" + % (domname, interface)) + + return self.getFreeIP(domname, interface) + + def setIP(self, domname, interface, ip): + # Make sure domain and interface aren't already assigned an IP + if self.used_ips.has_key('%s:%s' % (domname, interface)): + raise NetworkError("Domain %s interface %s is already has IP" + % (domname, interface)) + + self.setUsedIP(domname, interface, ip) + + def releaseIP(self, domname, interface, ip): + if self.used_ips.has_key('%s:%s' % (domname, interface)): + del self.used_ips['%s:%s' % (domname, interface)] + + def getNumberAllowedIPs(self): + return self.total_ips + + def canRunNetTest(self, ips): + # Check to see if a test can run, returns true or false. Input is + # number of ips needed. + if NETWORK_IP_RANGE == "dhcp": + return True + + if self.total_ips >= ips: + return True + + return False + + def isIPInRange(self, ip): + # Checks to see if supplied ip is in the range of allowed ips + maxo = self.max_ip.split('.') + mino = self.min_ip.split('.') + ipo = ip.split('.') + + if int(ipo[0]) < int(mino[0]): + return False + elif int(ipo[0]) > int(maxo[0]): + return False + + if int(ipo[1]) < int(mino[1]): + return False + elif int(ipo[1]) > int(maxo[1]): + return False + + if int(ipo[2]) < int(mino[2]): + return False + elif int(ipo[2]) > int(maxo[2]): + return False + + if int(ipo[3]) < int(mino[3]): + return False + elif int(ipo[3]) > int(maxo[3]): + return False + + return True diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/lib/XmTestLib/XenDevice.py --- /dev/null Mon Apr 24 21:44:01 2006 +++ b/tools/xm-test/lib/XmTestLib/XenDevice.py Mon Apr 24 21:52:26 2006 @@ -0,0 +1,271 @@ +#!/usr/bin/python +""" + Copyright (C) International Business Machines Corp., 2005, 2006 + Authors: Dan Smith + Daniel Stekloff + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; under version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +""" + +import sys +import commands +import os +import re +import time + +from Xm import * +from Test import * +from config import * +from XenDomain import * +from NetConfig import * +from XmTestLib import * +from __init__ import * + +class XenNetDevCmd: + + def __init__(self, netDevice, addCmd, removeCmd): + """Object representing a network device command""" + self.addcmd = addCmd + self.removecmd = removeCmd + self.addhasrun = False + self.rmvhasrun = False + self.netdevice = netDevice + + def getAddCmd(self): + return self.addcmd + + def getRemoveCmd(self): + return self.removecmd + + def hasAddRun(self): + return self.addhasrun + + def hasRemoveRun(self): + self.rmvhasrun + + def runAddCmd(self, runOnDom0=False): + # Defaults running command on dom0, if console then will run there + if runOnDom0 == False: + dom = self.netdevice.getDomain() + console = dom.getConsole() + console.runCmd(self.addcmd) + else: + status, output = traceCommand(self.addcmd) + if status: + raise NetworkError("Device add cmd failed: %s Status: %d" + % (self.addcmd, status)) + self.addhasrun = True + + def runRemoveCmd(self, runOnDom0=False): + # Defaults running command on dom0, if console then will run there + if runOnDom0 == False: + dom = self.netdevice.getDomain() + console = dom.getConsole() + console.runCmd(self.removecmd) + else: + status, output = traceCommand(self.removecmd) + if status: + raise NetworkError("Device remove cmd failed: %s Status: %d" + % (self.removecmd, status)) + self.removehasrun = True + +class XenDevice: + + def __init__(self, domain, id, devConfig=None): + """An object to represent Xen Devices like network and block + @param domain: Domain the device will be added to + @param id: Device identifier + @param devConfig: Initial configuration dictionary for XenDevice + """ + if config: + self.config = devConfig + else: + self.config = {} + + self.id = id + self.domain = domain + self.configNode = None + # Commands run when domain is started or devices added and removed. + self.dom0_cmds = [] + self.domU_cmds = [] + + def __str__(self): + """Convert device config to XenConfig node compatible string""" + confstr = '' + for k, v in self.config.items(): + if len(confstr) > 0: + confstr += ', ' + if isinstance(v, int): + confstr += "%s=%i" % (k, v) + elif isinstance(v, list) and v: + confstr += "%s=%s" % (k, v) + elif isinstance(v, str) and v: + confstr += "%s=%s" % (k, v) + + return confstr + + def execAddCmds(self): + # Cmds for when a device is added to the system + if len(self.dom0_cmds) > 0: + for i in range(0, len(self.dom0_cmds)): + if self.dom0_cmds[i].getAddCmd(): + self.dom0_cmds[i].runAddCmd(runOnDom0=True) + + if len(self.domU_cmds) > 0: + for i in range(0, len(self.domU_cmds)): + if self.domU_cmds[i].getAddCmd(): + self.domU_cmds[i].runAddCmd() + + def execRemoveCmds(self): + # Cmds for when a device is removed from the system + if len(self.dom0_cmds) > 0: + for i in range(0, len(self.dom0_cmds)): + if (self.dom0_cmds[i].getRemoveCmd() + and self.dom0_cmds[i].hasAddRun() == True): + self.dom0_cmds[i].runRemoveCmd(runOnDom0=True) + + if len(self.domU_cmds) > 0: + for i in range(0, len(self.domU_cmds)): + if (self.domU_cmds[i].getRemoveCmd() + and self.domU_cmds[i].hasAddRun() == True): + self.domU_cmds[i].runRemoveCmd() + + def removeDevice(self): + self.execRemoveCmds() + + def getId(self): + return self.id + + def getConfigOpt(self): + return self.configNode + + def getDomain(self): + return self.domain + +class XenNetDevice(XenDevice): + + def __init__(self, domain, id, devConfig=None): + """An object to represent Xen Network Device + @param domain: Domain the device is being added to + @param id: Network device identifier, interface name like eth0 + @param devConfig: Initial dictionary configuration for XenNetDevice + """ + if devConfig: + self.config = devConfig + else: + self.config = {} + + self.id = id + self.domain = domain + self.configNode = "vif" + self.dom0_cmds = [] + self.domU_cmds = [] + self.network = None + self.netmask = None + self.ip = None + self.dom0_alias_ip = None + + if domain.getDomainType() == "HVM": + self.config["type"] = "ioemu" + if not self.config.has_key('bridge'): + self.config["bridge"] = "xenbr0" + + if self.config.has_key("ip"): + self.setNetDevIP(ip=self.config["ip"]) + else: + if NETWORK_IP_RANGE != "dhcp": + self.setNetDevIP() + + def __del__(self): + # Make sure we clean up NetConfig's list of ips, so the ip can be + # reused + self.releaseNetDevIP() + + def addIfconfigCmd(self, domU=True): + # Method to add start and remove ifconfig functions + if domU == True: + locmd = XenNetDevCmd(self, addCmd="ifconfig lo 127.0.0.1", removeCmd=None) + ifcmd = [] + + + # Start or Add cmd + acmd = 'ifconfig %s inet %s netmask %s up' % (self.id, self.ip, self.netmask) + rcmd = 'ifconfig %s down' % self.id + ifcmd = XenNetDevCmd(self, addCmd=acmd, removeCmd=rcmd) + + if domU == True: + self.domU_cmds.append(locmd) + self.domU_cmds.append(ifcmd) + else: + self.dom0_cmds.append(ifcmd) + + def removeDevice(self): + self.releaseNetDevIP() + + def addDom0AliasCmd(self, dev="vif0.0"): + # Method to add start and remove dom0 alias cmds + acmd = 'ip addr add %s dev %s' % (self.dom0_alias_ip, dev) + rcmd = 'ip addr del %s dev %s' % (self.dom0_alias_ip, dev) + aliascmd = XenNetDevCmd(self, addCmd=acmd, removeCmd=rcmd) + + self.dom0_cmds.append(aliascmd) + + def releaseNetDevIP(self): + # Must remove start cmds for ip configuration and then release from + # NetConfig + self.execRemoveCmds() + self.dom0_cmds = [] + self.domU_cmds = [] + if self.config.has_key("ip"): + del self.config["ip"] + + if self.dom0_alias_ip: + xmtest_netconf.releaseIP("domain0", self.domain.getName(), self.dom0_alias_ip) + xmtest_netconf.releaseIP(self.domain.getName(), self.id, self.ip) + + def getNetDevIP(self): + return self.ip + + def getDom0AliasIP(self): + return self.dom0_alias_ip + + def getNetwork(self): + return self.network + + def setNetDevIP(self, ip=None): + # Function to set a new IP for NetDevice. + if NETWORK_IP_RANGE == "dhcp": + raise NetworkError("System configured for dhcp, cannot set new ip.") + + if (self.ip and not ip) or ((self.ip and ip) and (self.ip != ip)): + self.releaseNetDevIP() + + if not self.netmask: + self.netmask = xmtest_netconf.getNetMask() + + if not self.network: + self.network = xmtest_netconf.getNetwork() + + if ip: + xmtest_netconf.setIP(self.domain.getName(), self.id, ip) + self.ip = ip + else: + self.ip = xmtest_netconf.getIP(self.domain.getName(), self.id) + + self.addIfconfigCmd() + + # Setup an alias for Dom0 + self.dom0_alias_ip = xmtest_netconf.getIP("domain0", self.domain.getName()) + self.addDom0AliasCmd() diff -r 45aaca4aed38 -r 38ccc3bbb68f tools/xm-test/lib/XmTestLib/Network.py --- a/tools/xm-test/lib/XmTestLib/Network.py Mon Apr 24 21:44:01 2006 +++ /dev/null Mon Apr 24 21:52:26 2006 @@ -1,110 +0,0 @@ -#!/usr/bin/python -""" - Network.py - Common utilities for network tests - - Copyright (C) International Business Machines Corp., 2005 - Author: Jim Dykman - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; under version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" -import sys; -import os; -import atexit; -import random; - -from Test import * -from Xm import * -from config import * - -class NetworkError(Exception): - def __init__(self, msg): - self.errMsg = msg - - def __str__(self): - return str(self.errMsg) - -def undo_dom0_alias(eth, ip): - traceCommand("ip addr del " + ip + " dev " + eth) - -def net_from_ip(ip): - return ip[:ip.rfind(".")] + ".0/24" - -class XmNetwork: - - def __init__(self): - # Check for existing zeroconf address. We are using the zeroconf - # address range as static IP addresses.... if someone is using - # real zeroconf addresses, then we're going to skip tests to - # avoid interfering with them. - rc, out = traceCommand( - "ip addr show |grep \"inet 169.254\" | grep -v vif") - - if rc == 0: - SKIP("Zeroconf address found: " + out) - - # Randomize one octet of the IP addresses we choose, so that - # multiple machines running network tests don't interfere - # with each other. - self.subnet = random.randint(1,254) - - def calc_ip_address(self, dom, interface): - # Generate an IP address from the dom# and eth#: - # 169.254.(self.subnet).(eth#)*16 + (dom# + 1) - ethnum = int(interface[len("eth"):]) - if (ethnum > 15): - raise NetworkError("ethnum > 15 : " + interface) - domnum = int(dom[len("dom"):]) - if (domnum > 14): - raise NetworkError("domnum > 14 : " + dom) - - return "169.254."+ str(self.subnet) + "." + str(ethnum*16+domnum+1) - - def ip(self, dom, interface, todomname=None, toeth=None, bridge=None): - newip = self.calc_ip_address(dom, interface) - - # If the testcase is going to talk to dom0, we need to add an - # IP address in the proper subnet - if dom == "dom0": - if ENABLE_HVM_SUPPORT: - # HVM uses ioemu which uses a bridge - if not bridge: - SKIP("no bridge supplied") - else: - vifname = bridge - else: - # The domain's vif is a convenient place to add to - vifname = "vif" + str(domid(todomname)) + "." + toeth[3:] - - # register the exit handler FIRST, just in case - atexit.register(undo_dom0_alias, vifname, newip) - - # add the alias - status, output = traceCommand("ip addr add " + newip + - " dev " + vifname) - if status: - SKIP("\"ip addr add\" failed") - - if ENABLE_HVM_SUPPORT: - # We need to add a route to the bridge device - network = net_from_ip(newip) - status, output = traceCommand("ip route add " + network + " dev " + vifname + " scope link") - - if status: - SKIP("\"ip route add\" failed") - - return newip - - def mask(self, dom, interface): - return "255.255.255.240"