590 lines
20 KiB
Python
590 lines
20 KiB
Python
#
|
|
# 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; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# 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., 51 Franklin Street, Fifth Floor, Boston,
|
|
# MA 02110-1301 USA.
|
|
|
|
import virtinst
|
|
from virtinst import VirtualDisk
|
|
from virtinst import Interface
|
|
|
|
import utils
|
|
|
|
import unittest
|
|
import logging
|
|
import traceback
|
|
import os
|
|
|
|
# Template for adding arguments to test
|
|
# { 'label' : { \
|
|
# 'VAR' : { \
|
|
# 'invalid' : [param],
|
|
# 'valid' : [param]},
|
|
# '__init__' : { \
|
|
# 'invalid' : [{'initparam':val}],
|
|
# 'valid' : [{'initparam':val}]}
|
|
#
|
|
# Anything in 'valid' should throw no error
|
|
# Anything in 'invalid' should throw ValueError or TypeError
|
|
|
|
# We install several storage pools on the connection to ensure
|
|
# we aren't bumping up against errors in that department.
|
|
testconn = utils.open_testdriver()
|
|
testcaps = virtinst.CapabilitiesParser.parse(testconn.getCapabilities())
|
|
|
|
virtimage = virtinst.ImageParser.parse_file("tests/image-xml/image.xml")
|
|
|
|
volinst = virtinst.Storage.StorageVolume(conn=testconn,
|
|
pool_name="default-pool",
|
|
name="val-vol",
|
|
capacity=1)
|
|
|
|
tmppool = testconn.storagePoolLookupByName("inactive-pool")
|
|
tmppool.destroy()
|
|
|
|
iface_proto1 = Interface.InterfaceProtocol.protocol_class_for_family(
|
|
Interface.InterfaceProtocol.INTERFACE_PROTOCOL_FAMILY_IPV4)()
|
|
iface_proto2 = Interface.InterfaceProtocol.protocol_class_for_family(
|
|
Interface.InterfaceProtocol.INTERFACE_PROTOCOL_FAMILY_IPV6)()
|
|
iface_ip1 = Interface.InterfaceProtocolIPAddress("129.63.1.2")
|
|
iface_ip2 = Interface.InterfaceProtocolIPAddress("fe80::215:58ff:fe6e:5",
|
|
prefix="64")
|
|
|
|
args = {
|
|
|
|
'guest' : {
|
|
'name' : {
|
|
'invalid' : ['123456789', 'im_invalid!', '', 0,
|
|
'verylongnameverylongnameverylongnamevery'
|
|
'longnameveryvery', "test" # In use,
|
|
],
|
|
'valid' : ['Valid_name.01'] },
|
|
'memory' : {
|
|
'invalid' : [-1, 0, ''],
|
|
'valid' : [200, 2000] },
|
|
'maxmemory' : {
|
|
'invalid' : [-1, 0, ''],
|
|
'valid' : [200, 2000], },
|
|
'uuid' : {
|
|
'invalid' : [ '', 0, '1234567812345678123456781234567x'],
|
|
'valid' : ['12345678123456781234567812345678',
|
|
'12345678-1234-1234-ABCD-ABCDEF123456']},
|
|
'vcpus' : {
|
|
'invalid' : [-1, 0, 1000, ''],
|
|
'valid' : [ 1, 32 ] },
|
|
'graphics' : {
|
|
'invalid' : ['', True, 'unknown', {}, ('', '', '', 0),
|
|
('', '', '', 'longerthan16chars'),
|
|
('', '', '', 'invalid!!ch@r')],
|
|
'valid' : [False, 'sdl', 'vnc', (True, 'sdl', '', 'key_map-2'),
|
|
{'enabled' : True, 'type':'vnc', 'opts':5900} ]},
|
|
'type' : {
|
|
'invalid' : [],
|
|
'valid' : ['sometype'] },
|
|
'cdrom' : {
|
|
'invalid' : ['', 0, '/somepath'],
|
|
'valid' : ['/dev/loop0']
|
|
},
|
|
'arch' : {
|
|
'invalid' : [],
|
|
'valid' : ["i386", 'i686', 'x86_64'],
|
|
},
|
|
},
|
|
|
|
'fvguest' : {
|
|
'os_type' : {
|
|
'invalid' : ['notpresent', 0, ''],
|
|
'valid' : ['other', 'windows', 'unix', 'linux']},
|
|
'os_variant': {
|
|
'invalid' : ['', 0, 'invalid'],
|
|
'valid' : ['rhel5', 'sles10']},
|
|
},
|
|
|
|
'disk' : {
|
|
'init_conns' : [ testconn, None ],
|
|
'__init__' : {
|
|
|
|
'invalid' : [
|
|
{'path' : 0},
|
|
{ 'path' : '/root' },
|
|
{ 'path' : 'valid', 'size' : None },
|
|
{ 'path' : "valid", 'size' : 'invalid' },
|
|
{ 'path' : 'valid', 'size' : -1},
|
|
{ 'path' : None },
|
|
{ 'path' : "noexist1", 'size' : 900000, 'sparse' : False },
|
|
{ 'path' : "noexist2", 'type' : VirtualDisk.DEVICE_CDROM},
|
|
{ 'volName' : ("default-pool", "default-vol")},
|
|
{ 'conn' : testconn, 'volName' : ("pool-noexist", "default-vol")},
|
|
{ 'conn' : testconn, 'volName' : ("default-pool", "vol-noexist")},
|
|
{ 'conn' : testconn, 'volName' : ( 1234, "vol-noexist")},
|
|
{ 'path' : 'valid', 'size' : 1, 'driverCache' : 'invalid' },
|
|
{ 'conn' : testconn, "path" : "/full-pool/newvol.img", "size" : 1,
|
|
'sparse' : False },
|
|
# Inactive pool w/ volume
|
|
{ 'conn' : testconn, "path" : "/inactive-pool/inactive-vol"},
|
|
],
|
|
|
|
'valid' : [
|
|
{ 'path' : '/dev/loop0' },
|
|
{ 'path' : 'nonexist', 'size' : 1 },
|
|
{ 'path' :'/dev/null'},
|
|
{ 'path' : None, 'device' : VirtualDisk.DEVICE_CDROM},
|
|
{ 'path' : None, 'device' : VirtualDisk.DEVICE_FLOPPY},
|
|
{ 'conn' : testconn, 'volName' : ("default-pool", "default-vol")},
|
|
{ 'conn' : testconn, 'path' : "/default-pool/default-vol" },
|
|
{ 'conn' : testconn, 'path' : "/default-pool/vol-noexist", 'size' : 1 },
|
|
{ 'conn' : testconn, 'volInstall': volinst},
|
|
{ 'path' : 'nonexist', 'size' : 1, 'driverCache' : 'writethrough' },
|
|
# Full pool, but we are nonsparse
|
|
{ 'conn' : testconn, "path" : "/full-pool/newvol.img", "size" : 1 },
|
|
]
|
|
},
|
|
|
|
'shareable' : {
|
|
'invalid': [ None, 1234 ],
|
|
'valid': [ True, False ]
|
|
},
|
|
},
|
|
|
|
'installer' : {
|
|
'init_conns' : [ testconn, None ],
|
|
'boot' : {
|
|
'invalid' : ['', 0, ('1element'), ['1el', '2el', '3el'],
|
|
{'1element': '1val'},
|
|
{'kernel' : 'a', 'wronglabel' : 'b'}],
|
|
'valid' : [('kern', 'init'), ['kern', 'init'],
|
|
{ 'kernel' : 'a', 'initrd' : 'b'}]},
|
|
'extraargs' : {
|
|
'invalid' : [],
|
|
'valid' : ['someargs']},
|
|
'arch' : {
|
|
'invalid' : [],
|
|
'valid' : ['i686', 'i386', 'x86_64'],
|
|
}
|
|
},
|
|
|
|
'distroinstaller' : {
|
|
'init_conns' : [ testconn, None ],
|
|
'location' : {
|
|
'invalid' : ['nogood', 'http:/nogood', [], None,
|
|
("pool-noexist", "default-vol"),
|
|
("default-pool", "vol-noexist"),
|
|
],
|
|
'valid' : ['/dev/null', 'http://web', 'ftp://ftp', 'nfs:nfsserv',
|
|
'/tmp', # For installing from local dir tree
|
|
("default-pool", "default-vol"),
|
|
]}
|
|
},
|
|
|
|
'livecdinstaller' : {
|
|
'init_conns' : [ testconn, None ],
|
|
'location' : {
|
|
'invalid' : ['path-noexist',
|
|
("pool-noexist", "default-vol"),
|
|
("default-pool", "vol-noexist"),
|
|
],
|
|
'valid' : ['/dev/null', ("default-pool", "default-vol"),
|
|
]}
|
|
},
|
|
|
|
'imageinstaller' : {
|
|
'__init__' : {
|
|
'invalid' : \
|
|
[{'image' : virtimage, 'capabilities': testcaps, 'boot_index': 5},
|
|
{'image' : virtimage, 'capabilities': "foo"}],
|
|
'valid' : \
|
|
[{'image' : virtimage, 'capabilities': testcaps, 'boot_index': 1},
|
|
{'image' : virtimage },
|
|
{'image' : virtimage, 'capabilities': testcaps, 'conn': None}],
|
|
}
|
|
},
|
|
|
|
'network' : {
|
|
'init_conns' : [ testconn, None ],
|
|
'__init__' : {
|
|
'invalid' : [ {'macaddr':0}, {'macaddr':''}, {'macaddr':'$%XD'},
|
|
{'type':'network'} ],
|
|
'valid' : []}
|
|
},
|
|
|
|
'clonedesign' : {
|
|
'original_guest' : {
|
|
'invalid' : ['idontexist'],
|
|
'valid' : ['test']},
|
|
'clone_name': { 'invalid' : [0, 'test' # Already in use
|
|
],
|
|
'valid' : ['some.valid-name_9']},
|
|
'clone_uuid': { 'invalid' : [0],
|
|
'valid' : ['12345678123456781234567812345678']},
|
|
'clone_mac' : { 'invalid' : ['badformat'],
|
|
'valid' : ['AA:BB:CC:DD:EE:FF']},
|
|
'clone_bs' : { 'invalid' : [], 'valid' : ['valid']},
|
|
},
|
|
|
|
'inputdev' : {
|
|
'init_conns' : [ testconn ],
|
|
'type' : {
|
|
'valid' : [ "mouse", "tablet"],
|
|
'invalid' : [ "foobar", 1234]},
|
|
'bus' : {
|
|
'valid' : [ "ps2", "xen", "usb"],
|
|
'invalid' : [ "foobar", 1234]},
|
|
},
|
|
|
|
'chardev' : {
|
|
'init_conns' : [ testconn ],
|
|
'source_path': {
|
|
'invalid' : [],
|
|
'valid' : [ "/some/path" ]},
|
|
'source_mode': {
|
|
'invalid' : [ None ],
|
|
'valid' : virtinst.VirtualCharDevice.char_modes },
|
|
'source_host': {
|
|
'invalid' : [],
|
|
'valid' : [ "some.source.host" ]},
|
|
'source_port': {
|
|
'invalid' : [ "foobar"],
|
|
'valid' : [ 1234 ]},
|
|
'connect_host': {
|
|
'invalid' : [],
|
|
'valid' : [ "some.connect.com" ]},
|
|
'connect_port': {
|
|
'invalid' : [ "foobar"],
|
|
'valid' : [ 1234 ]},
|
|
'protocol': {
|
|
'invalid' : [ None ],
|
|
'valid' : virtinst.VirtualCharDevice.char_protocols },
|
|
},
|
|
|
|
'interface' : {
|
|
'init_conns' : [ testconn ],
|
|
'name' : {
|
|
'invalid' : ["eth0", None, 1234],
|
|
'valid' : ["foobar"], },
|
|
'mtu' : {
|
|
'invalid' : [],
|
|
'valid' : [None, 0, 1, 1234, "1234"], },
|
|
'start_mode' : {
|
|
'invalid' : ["foobar", None],
|
|
'valid' : [Interface.Interface.INTERFACE_START_MODE_ONBOOT], },
|
|
'macaddr' : {
|
|
'invalid' : [0, 100, "22:22:33", "foobar"],
|
|
'valid' : [None, "22:22:33:44:55:66"], },
|
|
|
|
'protocols' : {
|
|
'invalid' : [],
|
|
'valid' : [[], [iface_proto1, iface_proto2]], },
|
|
|
|
# Bond params
|
|
'bond_mode' : {
|
|
'invalid' : [],
|
|
'valid' : ["active-backup", "broadcast"], },
|
|
'monitor_mode' : {
|
|
'invalid' : [],
|
|
'valid' :
|
|
[Interface.InterfaceBond.INTERFACE_BOND_MONITOR_MODE_ARP,
|
|
Interface.InterfaceBond.INTERFACE_BOND_MONITOR_MODE_MII], },
|
|
|
|
'mii_frequency' : {
|
|
'invalid' : [],
|
|
'valid' : ["123", 123], },
|
|
'mii_updelay' : {
|
|
'invalid' : [],
|
|
'valid' : ["123", 123], },
|
|
'mii_downdelay' : {
|
|
'invalid' : [],
|
|
'valid' : ["123", 123], },
|
|
'mii_carrier_mode' : {
|
|
'invalid' : [],
|
|
'valid' : ["ioctl", "netif"], },
|
|
|
|
'arp_interval' : {
|
|
'invalid' : [],
|
|
'valid' : [123, "123"], },
|
|
'arp_target' : {
|
|
'invalid' : [],
|
|
'valid' : ["129.168.1.2"], },
|
|
'arp_validate_mode' : {
|
|
'invalid' : [],
|
|
'valid' : ["active", "backup"], },
|
|
|
|
# VLAN params
|
|
'tag' : {
|
|
'invalid' : [],
|
|
'valid' : [123, "123"], },
|
|
'parent_interface' : {
|
|
'invalid' : [None],
|
|
'valid' : ["eth1", testconn.interfaceLookupByName("eth1")], },
|
|
|
|
# Bridge params
|
|
'stp' : {
|
|
'invalid' : [0, 1, "foo"],
|
|
'valid' : [True, False], },
|
|
'delay' : {
|
|
'invalid' : [],
|
|
'valid' : [0, 1, "4"], },
|
|
},
|
|
|
|
'interface_proto' : {
|
|
'gateway' : {
|
|
'invalid' : [],
|
|
'valid' : ["129.168.1.2"], },
|
|
|
|
'autoconf' : {
|
|
'invalid' : [],
|
|
'valid' : [True, False], },
|
|
'dhcp' : {
|
|
'invalid' : [],
|
|
'valid' : [True, False], },
|
|
'peerdns' : {
|
|
'invalid' : [],
|
|
'valid' : [True, False], },
|
|
'ips' : {
|
|
'invalid' : [],
|
|
'valid' : [[], [iface_ip1, iface_ip2]], },
|
|
|
|
}
|
|
|
|
} # End of validation dict
|
|
|
|
|
|
class TestValidation(unittest.TestCase):
|
|
|
|
def _getInitConns(self, label):
|
|
if "init_conns" in args[label]:
|
|
return args[label]["init_conns"]
|
|
return [testconn]
|
|
|
|
def _runObjInit(self, testclass, valuedict, defaultsdict=None):
|
|
if defaultsdict:
|
|
for key in defaultsdict.keys():
|
|
if key not in valuedict:
|
|
valuedict[key] = defaultsdict.get(key)
|
|
return testclass(*(), **valuedict)
|
|
|
|
def _testInvalid(self, name, obj, testclass, paramname, paramvalue):
|
|
try:
|
|
if paramname == '__init__':
|
|
self._runObjInit(testclass, paramvalue)
|
|
else:
|
|
setattr(obj, paramname, paramvalue)
|
|
|
|
msg = ("Expected TypeError or ValueError: None Raised.\n"
|
|
"For '%s' object, paramname '%s', val '%s':" %
|
|
(name, paramname, paramvalue))
|
|
raise AssertionError(msg)
|
|
|
|
except AssertionError:
|
|
raise
|
|
except (TypeError, ValueError):
|
|
# This is an expected error
|
|
pass
|
|
except Exception, e:
|
|
msg = ("Unexpected exception raised: %s\n" % e +
|
|
"Original traceback was: \n%s\n" % traceback.format_exc() +
|
|
"For '%s' object, paramname '%s', val '%s':" %
|
|
(name, paramname, paramvalue))
|
|
raise AssertionError(msg)
|
|
|
|
def _testValid(self, name, obj, testclass, paramname, paramvalue):
|
|
|
|
try:
|
|
if paramname is '__init__':
|
|
conns = self._getInitConns(name)
|
|
if "conn" in paramvalue:
|
|
conns = [paramvalue["conn"]]
|
|
for conn in conns:
|
|
paramvalue["conn"] = conn
|
|
self._runObjInit(testclass, paramvalue)
|
|
else:
|
|
setattr(obj, paramname, paramvalue)
|
|
except Exception, e:
|
|
msg = ("Validation case failed, expected success.\n" +
|
|
"Exception received was: %s\n" % e +
|
|
"Original traceback was: \n%s\n" % traceback.format_exc() +
|
|
"For '%s' object, paramname '%s', val '%s':" %
|
|
(name, paramname, paramvalue))
|
|
raise AssertionError(msg)
|
|
|
|
def _testArgs(self, obj, testclass, name, exception_check=None,
|
|
manual_dict=None):
|
|
"""@obj Object to test parameters against
|
|
@testclass Full class to test initialization against
|
|
@name String name indexing args"""
|
|
logging.debug("Testing '%s'", name)
|
|
testdict = args[name]
|
|
if manual_dict != None:
|
|
testdict = manual_dict
|
|
|
|
for paramname in testdict.keys():
|
|
if paramname == "init_conns":
|
|
continue
|
|
|
|
for val in testdict[paramname]['invalid']:
|
|
self._testInvalid(name, obj, testclass, paramname, val)
|
|
|
|
for val in testdict[paramname]['valid']:
|
|
if exception_check:
|
|
if exception_check(obj, paramname, val):
|
|
continue
|
|
self._testValid(name, obj, testclass, paramname, val)
|
|
|
|
|
|
# Actual Tests
|
|
def testGuestValidation(self):
|
|
PVGuest = virtinst.ParaVirtGuest(conn=testconn,
|
|
type="xen")
|
|
self._testArgs(PVGuest, virtinst.Guest, 'guest')
|
|
|
|
def testDiskValidation(self):
|
|
disk = VirtualDisk("/dev/loop0")
|
|
self._testArgs(disk, VirtualDisk, 'disk')
|
|
|
|
def testFVGuestValidation(self):
|
|
FVGuest = virtinst.FullVirtGuest(conn=testconn,
|
|
type="xen")
|
|
self._testArgs(FVGuest, virtinst.FullVirtGuest, 'fvguest')
|
|
|
|
def testNetworkValidation(self):
|
|
network = virtinst.VirtualNetworkInterface(conn=testconn)
|
|
self._testArgs(network, virtinst.VirtualNetworkInterface, 'network')
|
|
|
|
# Test MAC Address collision
|
|
hostmac = virtinst.util.get_host_network_devices()
|
|
if len(hostmac) is not 0:
|
|
hostmac = hostmac[0][4]
|
|
for params in ({'macaddr' : hostmac},):
|
|
network = virtinst.VirtualNetworkInterface(*(), **params)
|
|
self.assertRaises(RuntimeError, network.setup, \
|
|
testconn)
|
|
|
|
# Test dynamic MAC/Bridge success
|
|
try:
|
|
network = virtinst.VirtualNetworkInterface()
|
|
network.setup(testconn)
|
|
except Exception, e:
|
|
raise AssertionError(
|
|
"Network setup with no params failed, expected success." +
|
|
" Exception was: %s: %s" %
|
|
(str(e), "".join(traceback.format_exc())))
|
|
|
|
def testDistroInstaller(self):
|
|
def exception_check(obj, paramname, paramvalue):
|
|
if paramname == "location":
|
|
# Skip NFS test as non-root
|
|
if paramvalue[0:3] == "nfs" and os.geteuid() != 0:
|
|
return True
|
|
|
|
# Don't pass a tuple location if installer has no conn
|
|
if not obj.conn and type(paramvalue) == tuple:
|
|
return True
|
|
|
|
return False
|
|
|
|
label = 'distroinstaller'
|
|
for conn in self._getInitConns(label):
|
|
dinstall = virtinst.DistroInstaller(conn=conn)
|
|
self._testArgs(dinstall, virtinst.DistroInstaller, 'installer',
|
|
exception_check)
|
|
self._testArgs(dinstall, virtinst.DistroInstaller, label,
|
|
exception_check)
|
|
|
|
def testLiveCDInstaller(self):
|
|
def exception_check(obj, paramname, paramvalue):
|
|
if paramname == 'location':
|
|
# Don't pass a tuple location if installer has no conn
|
|
if not obj.conn and type(paramvalue) == tuple:
|
|
return True
|
|
|
|
return False
|
|
|
|
label = 'livecdinstaller'
|
|
for conn in self._getInitConns(label):
|
|
dinstall = virtinst.LiveCDInstaller(conn=conn)
|
|
self._testArgs(dinstall, virtinst.LiveCDInstaller, 'installer',
|
|
exception_check)
|
|
self._testArgs(dinstall, virtinst.LiveCDInstaller, label,
|
|
exception_check)
|
|
|
|
def testImageInstaller(self):
|
|
label = 'imageinstaller'
|
|
inst_obj = virtinst.ImageInstaller(image=virtimage,
|
|
capabilities=testcaps)
|
|
#self._testArgs(inst_obj, virtinst.ImageInstaller, 'installer')
|
|
self._testArgs(inst_obj, virtinst.ImageInstaller, label)
|
|
|
|
def testCloneManager(self):
|
|
label = 'clonedesign'
|
|
for conn in self._getInitConns(label):
|
|
cman = virtinst.CloneManager.CloneDesign(conn)
|
|
self._testArgs(cman, virtinst.CloneManager.CloneDesign, label)
|
|
|
|
def testInputDev(self):
|
|
label = 'inputdev'
|
|
for conn in self._getInitConns(label):
|
|
cman = virtinst.VirtualInputDevice(conn)
|
|
self._testArgs(cman, virtinst.VirtualInputDevice, label)
|
|
|
|
def testCharDev(self):
|
|
label = 'chardev'
|
|
paramdict = args[label]
|
|
devs = []
|
|
|
|
for conn in self._getInitConns(label):
|
|
for dev in virtinst.VirtualCharDevice.dev_types:
|
|
for char in virtinst.VirtualCharDevice.char_types:
|
|
devs.append(virtinst.VirtualCharDevice.get_dev_instance(conn, dev, char))
|
|
|
|
for dev in devs:
|
|
custom_dict = {}
|
|
for key in paramdict.keys():
|
|
if hasattr(dev, key):
|
|
custom_dict[key] = paramdict[key]
|
|
self._testArgs(dev, virtinst.VirtualCharDevice, label,
|
|
manual_dict=custom_dict)
|
|
|
|
def testInterface(self):
|
|
label = 'interface'
|
|
paramdict = args[label]
|
|
|
|
for itype in Interface.Interface.INTERFACE_TYPES:
|
|
custom_dict = {}
|
|
iclass = Interface.Interface.interface_class_for_type(itype)
|
|
|
|
for key in paramdict.keys():
|
|
if hasattr(iclass, key):
|
|
custom_dict[key] = paramdict[key]
|
|
|
|
for conn in self._getInitConns(label):
|
|
iobj = iclass("foo-validation-test", conn)
|
|
|
|
self._testArgs(iobj, iclass, label, manual_dict=custom_dict)
|
|
|
|
def testInterfaceProtocol(self):
|
|
label = 'interface_proto'
|
|
paramdict = args[label]
|
|
|
|
for itype in Interface.InterfaceProtocol.INTERFACE_PROTOCOL_FAMILIES:
|
|
custom_dict = {}
|
|
iclass = Interface.InterfaceProtocol.protocol_class_for_family(itype)
|
|
|
|
for key in paramdict.keys():
|
|
if hasattr(iclass, key):
|
|
custom_dict[key] = paramdict[key]
|
|
|
|
iobj = iclass()
|
|
self._testArgs(iobj, iclass, label, manual_dict=custom_dict)
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|