Daniel, thanks for the comments .. most of them are integrated, but I have a problem with changing MAC addresses, because the domain xml is regenerated every time, so I cannot rely on the interface name being "eth0" as fedora during boot creates a new ethX for each new MAC address .. any good advice? On Thu, 2010-04-15 at 15:27 +0100, Daniel P. Berrange wrote: > On Thu, Apr 15, 2010 at 02:35:41PM +0200, Gerhard Stenzel wrote: > > The following patch mainly adds a set of test case to verify that > > several spoofing attacks are prevented by the nwfilter subsystem. > > > > In order to have a well defined test machine, the patch also includes > > test scripts to network install a virtual disk from scratch, to boot the > > virtual test machine prior to running the actual test scripts and to > > shut it down afterwards. > > > > While I have tried to remove as much dependency on my local setup as > > possible there is still some left, so I am currently more interested in > > feedback about the general approach, not necessarily actual inclusion > > into the libvirt-TCK git. > > Your actual test cases look good, so I'll just put comments about > the setup/teardown stuff inline. ok .. sounds good > > > > > For example, I am currently trying to find a suitable location for the > > kickstart file, and also a suitable place for the common_functions.pl. > > The 'lib' directory contains modules which provide common functions & > code for the test scripts. In this case I'd suggest creating a file > > lib/Sys/Virt/TCK/NetworkHelpers.pm (use Sys::Virt::TCK::NetworkHelpers) ok .. done > > > Index: libvirt-tck/scripts/network/README > > =================================================================== > > --- /dev/null > > +++ libvirt-tck/scripts/network/README > > @@ -0,0 +1,14 @@ > > + > > +Test cases: > > + > > +000-install-image.t creates and install a 2GB fedora virtual disk via > > kickstart file from the network > > +001-boot-image.t defines and boots a VM which uses the fedora virtual > > disk > > +100-ping-still-working.t verifies the VM is pingable > > +210-no-mac-spoofing.t verifies mac spoofing is prevented > > +220-no-ip-spoofing.t verifies ip spoofing is prevented > > +230-no-mac-broadcast.t verifies mac broadcasting is prevented > > +240-no-arp-spoofing.t verifies arp spoofing is prevented > > +999-shutdown-image.t shuts the VM down > > One thing about the TCK test cases is that each one should be > self-contained, doing all setup & teardown it requires, not > reliant on any of the other tests cases or ordering of tests. > > So instead of having the 000-install-image.t & 0001-boot-image.t > scripts that do setup, you'd want to create some library code > that can be used to install + boot the guest, and just call that > from each test case. I am currently trying this .. however, what I am struggling with is that the MAC address is different for every boot. > > > Index: libvirt-tck/scripts/network/000-install-image.t > > =================================================================== > > --- /dev/null > > +++ libvirt-tck/scripts/network/000-install-image.t > > @@ -0,0 +1,181 @@ > > +# -*- perl -*- > > +# > > +# Copyright (C) 2010 IBM Corp. > > +# > > +# This program is free software; You can redistribute it and/or modify > > +# it under the GNU General Public License as published by the Free > > +# Software Foundation; either version 2, or (at your option) any > > +# later version > > +# > > +# The file "LICENSE" distributed along with this file provides full > > +# details of the terms and conditions > > +# > > + > > +=pod > > + > > +=head1 NAME > > + > > +network/000-install-image.t - install network test image > > + > > +=head1 DESCRIPTION > > + > > +The test case creates and install a 2GB fedora virtual > > +disk via kickstart file from the network. > > + > > +=cut > > + > > +use strict; > > +use warnings; > > + > > +use Test::More tests => 1; > > + > > +use Sys::Virt::TCK; > > + > > +my $tck = Sys::Virt::TCK->new(); > > +my $conn = eval { $tck->setup(); }; > > +BAIL_OUT "failed to setup test harness: $@" if $@; > > +END { $tck->cleanup if $tck; } > > + > > +# variables which may need to be adapted > > +my $domain_name ="f12nwtest"; > > +my $disk_name = "/var/lib/libvirt/images/${domain_name}.img"; > > +my $disk_size = "2147483648"; > > You want to avoid hardcoding any disk paths in the test cases. > > There's a convenient helper function if you want to create a > scratch disk for a guest > > my $path = $tck->create_sparse_disk("nwfilter", "root.img", 2147483648); create_sparse_disk does not work for me, anaconda does not recognize installable disks .. anyway I got rid of the hardcoded names. > > > +my $kickstart_file ="http://192.168.122.1/ks.cfg"; > > +my $cmdline = "ip=dhcp gateway=192.168.122.1 ks=${kickstart_file}"; > > In the 'conf/default.cfg' file we list the kernel+initrd images for > various distros. We could likely also have an optional kickstart > file listed there, so we move this configuration info out of the test > cases. sounds good .. but not done yet > > > +# see if the domain already exits > > +my $already_defined = 0; > > +diag "searching if ${domain_name} is already defined"; > > +my $nnames = $conn->num_of_defined_domains(); > > +my @names = $conn->list_defined_domain_names($nnames); > > +foreach (@names){ > > + if (/${domain_name}/) { > > + print "$_ already exists, no need to redefine\n"; > > + $already_defined = 1; > > + } > > +} > > +diag $already_defined; > > All guests created by the TCK test cases should have a name prefix > of 'tck' to avoid clashing with existing guests. Each test case > should destroy + undefine all guests it creates. The TCK setup > code will validate that there are no guests having a name starting > with a 'tck' prefix & optionally destroy them. So each of your > test cases should assume the guest doesn't already exist + clean > it up before exiting. ok .. working on that one > > NB, the disk files are not deleted between runs, so you will > still avoid the overhead of installation. > > > I'm talking with the libguestfs folks to see if we can re-use > their guest appliance + daemon so we can avoid the actual install > + ssh stuff at some point in the future. > > > +# check for installation disk and build it if not exists > > +my $already_installed = 0; > > +my $pool = $conn->get_storage_pool_by_name("default"); > > +my $nnames = $pool->num_of_storage_volumes(); > > +my @volNames = $pool->list_storage_vol_names($nnames); > > +foreach (@volNames){ > > + if (/${domain_name}/) { > > + print "$_ already exists, no need to install\n"; > > + $already_installed = 1; > > + } > > +} > > + > > +my $volumexml = "<volume>". > > +" <name>${domain_name}.img</name>". > > +" <key>${disk_name}</key>". > > +" <source>". > > +" </source>". > > +" <capacity>${disk_size}</capacity>". > > +" <allocation>4096</allocation>". > > +" <target>". > > +" <path>${disk_name}</path>". > > +" <format type='raw'/>". > > +" <permissions>". > > +" <mode>0644</mode>". > > +" <owner>0</owner>". > > +" <group>0</group>". > > +" </permissions>". > > +" </target>". > > +"</volume>"; > > > If using the create_sparse_disk helper I mentioned, you can avoid this > chunk of code. Just do a check to see if $path exists or not. ok . solved differently with generic_volume() > > > +# prepare image > > +if ($already_installed == 0) { > > + diag "Creating ${disk_name}"; > > + diag $volumexml; > > + my $vol = $pool->create_volume($volumexml) > > +# system("qemu-img create ${disk_name} ${disk_size}"); > > +} > > + > > +my $topxml = " <name>${domain_name}</name>". > > +" <memory>524288</memory>". > > +" <currentMemory>524288</currentMemory>". > > +" <vcpu>1</vcpu>"; > > + > > +my $osxml = " <os>". > > +" <type arch='x86_64' machine='fedora-13'>hvm</type>". > > +" <kernel>/var/cache/libvirt-tck/os-i686-hvm/vmlinuz</kernel>". > > +" <initrd>/var/cache/libvirt-tck/os-i686-hvm/initrd</initrd>". > > +" <cmdline>${cmdline}</cmdline>". > > +" <boot dev='hd'/>". > > +" </os>"; > > + > > +my $bottomxml = " <features>". > > +" <acpi/>". > > +" <apic/>". > > +" </features>". > > +" <clock offset='utc'/>". > > +" <on_poweroff>destroy</on_poweroff>". > > +" <on_reboot>restart</on_reboot>". > > +" <on_crash>restart</on_crash>". > > +" <devices>". > > +" <emulator>/usr/bin/qemu-kvm</emulator>". > > +" <disk type='file' device='disk'>". > > +" <driver name='qemu' type='raw'/>". > > +" <source file='${disk_name}'/>". > > +" <target dev='hda' bus='ide'/>". > > +" </disk>". > > +" <controller type='ide' index='0'>". > > +" </controller>". > > +" <interface type='network'>". > > +" <source network='default'/>". > > +" <target dev='vnet0'/>". > > +" <model type='virtio'/>". > > +" </interface>". > > +" <serial type='pty'>". > > +" <target port='0'/>". > > +" </serial>". > > +" <console type='pty'>". > > +" <target port='0'/>". > > +" </console>". > > +" <graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' > > keymap='de'/>". > > +" <video>". > > +" <model type='cirrus' vram='9216' heads='1'/>". > > +" </video>". > > +" </devices>"; > > + > > +my $xml = "<domain type='kvm'>" . > > +$topxml. > > +$osxml. > > +$bottomxml. > > +"</domain>"; > > There are a couple of helpers for building XML in a portable manner. > In the Sys::Virt::TCK class there's a method 'generic_domain' which > will find you a bare minimum VM matching the best kernel / arch > found in the TCK config file. It returns an instance of the the > class Sys::Virt::TCK::DomainBuilder which lets you then add > extra XML to the guest. > > eg, so you could do > > my $guest = $tck->generic_domain(); > > $guest->boot_cmdline($cmdline); > > > The 'boot_cmdline' method doesn't exist yet, but feel frree to extend > the shared helper modules as needed. Its much nicer to use them than > to build up XML manually. ok .. done > > > + > > +diag $xml; > > +diag "Defining an inactive domain config"; > > +my $dom; > > + > > +# no need to start if already installed > > +if (($already_installed == 0) && ($already_defined == 0)) { > > + ok_domain(sub { $dom = $conn->define_domain($xml) }, "defined > > persistent domain config"); > > + $xml = $dom->get_xml_description; > > + diag $xml; > > + diag "Starting inactive domain config"; > > + $dom->create; > > + > > + # wait for completion of installation > > + diag "wait for installation to finish .. "; > > + while($dom->is_active()) { > > + sleep(10); > > + diag ".. to view progress connect to virtual machine ${domain_name} .. > > "; > > + } > > + sleep(10); > > + diag " .. done"; > > + # cleanup > > + $dom->undefine; > > +} else { > > + ok_domain { $dom = $conn->get_domain_by_name($domain_name) } "the > > existing domain object"; > > +} > > + > > + > > + > > + > > + > > Index: libvirt-tck/scripts/network/001-boot-image.t > > =================================================================== > > --- /dev/null > > +++ libvirt-tck/scripts/network/001-boot-image.t > > @@ -0,0 +1,133 @@ > > +# -*- perl -*- > > +# > > +# Copyright (C) 2010 IBM Corp. > > +# > > +# This program is free software; You can redistribute it and/or modify > > +# it under the GNU General Public License as published by the Free > > +# Software Foundation; either version 2, or (at your option) any > > +# later version > > +# > > +# The file "LICENSE" distributed along with this file provides full > > +# details of the terms and conditions > > +# > > + > > +=pod > > + > > +=head1 NAME > > + > > +network/001-boot-image.t - boot installed test image > > + > > +=head1 DESCRIPTION > > + > > +The test case defines and boots a VM which uses the > > +fedora virtual disk create ny 000-install-image > > + > > +=cut > > + > > +use strict; > > +use warnings; > > + > > +use Test::More tests => 2; > > + > > +use Sys::Virt::TCK; > > +use XML::LibXML; > > + > > +require 'scripts/network/common_functions.pl'; > > + > > +my $tck = Sys::Virt::TCK->new(); > > +my $conn = eval { $tck->setup(); }; > > +BAIL_OUT "failed to setup test harness: $@" if $@; > > +END { $tck->cleanup if $tck; } > > + > > +my $already_defined = 0; > > +my $domain_name ="f12nwtest"; > > + > > +# see if the domain already exits > > +diag "searching if ${domain_name} is already defined"; > > +my $nnames = $conn->num_of_defined_domains(); > > +my @names = $conn->list_defined_domain_names($nnames); > > +foreach (@names){ > > + if (/${domain_name}/) { > > + print "$_ already exists, no need to redefine\n"; > > + $already_defined = 1; > > + } > > +} > > +diag $already_defined; > > + > > +my $dom; > > +my $xml; > > + > > +if ($already_defined == 1) { > > + ok_domain { $dom = $conn->get_domain_by_name($domain_name) } "the > > existing domain object"; > > +} else { > > + my $topxml = " <name>${domain_name}</name>". > > + " <memory>524288</memory>". > > + " <currentMemory>524288</currentMemory>". > > + " <vcpu>1</vcpu>"; > > + > > + my $osxml = " <os>". > > + " <type arch='x86_64' machine='fedora-13'>hvm</type>". > > + " <boot dev='hd'/>". > > + " </os>"; > > + > > + my $bottomxml = " <features>". > > + " <acpi/>". > > + " <apic/>". > > + " </features>". > > + " <clock offset='utc'/>". > > + " <on_poweroff>destroy</on_poweroff>". > > + " <on_reboot>restart</on_reboot>". > > + " <on_crash>restart</on_crash>". > > + " <devices>". > > + " <emulator>/usr/bin/qemu-kvm</emulator>". > > + " <disk type='file' device='disk'>". > > + " <driver name='qemu' type='raw'/>". > > + " <source file='/var/lib/libvirt/images/${domain_name}.img'/>". > > + " <target dev='hda' bus='ide'/>". > > + " </disk>". > > + " <controller type='ide' index='0'>". > > + " </controller>". > > + " <interface type='network'>". > > + " <source network='default'/>". > > + " <filterref filter='no-spoofing'/>". > > + " <target dev='vnet0'/>". > > + " <model type='virtio'/>". > > + " </interface>". > > + " <serial type='pty'>". > > + " <target port='0'/>". > > + " </serial>". > > + " <console type='pty'>". > > + " <target port='0'/>". > > + " </console>". > > + " <graphics type='vnc' port='-1' autoport='yes' listen='127.0.0.1' > > keymap='de'/>". > > + " <video>". > > + " <model type='cirrus' vram='9216' heads='1'/>". > > + " </video>". > > + " </devices>"; > > + > > + $xml = "<domain type='kvm'>" . > > + $topxml. > > + $osxml. > > + $bottomxml. > > + "</domain>"; > > Again, I think you'd be better using the '$tck->generic_domain' method, > and adding support to Sys::Virt::TCK::DomainBuilder for adding the > '<filterref filter='no-spoofing'/>' bit of the XML. > ok .. done > > > > Index: libvirt-tck/scripts/network/100-ping-still-working.t > > =================================================================== > > --- /dev/null > > +++ libvirt-tck/scripts/network/100-ping-still-working.t > > @@ -0,0 +1,71 @@ > > +# -*- perl -*- > > +# > > +# Copyright (C) 2010 IBM Corp. > > +# > > +# This program is free software; You can redistribute it and/or modify > > +# it under the GNU General Public License as published by the Free > > +# Software Foundation; either version 2, or (at your option) any > > +# later version > > +# > > +# The file "LICENSE" distributed along with this file provides full > > +# details of the terms and conditions > > +# > > + > > +=pod > > + > > +=head1 NAME > > + > > +network/100-ping-still-working.t - verify machines can be pinged from > > host > > + > > +=head1 DESCRIPTION > > + > > +The test case validates that it is possible to ping a guest machine > > from > > +the host. > > + > > +=cut > > + > > +use strict; > > +use warnings; > > + > > +use Test::More tests => 4; > > + > > +use Sys::Virt::TCK; > > +use Test::Exception; > > +use Net::SSH::Perl; > > +use XML::LibXML; > > + > > +require 'scripts/network/common_functions.pl'; > > + > > +my $tck = Sys::Virt::TCK->new(); > > +my $conn = eval { $tck->setup(); }; > > +BAIL_OUT "failed to setup test harness: $@" if $@; > > +END { > > + $tck->cleanup if $tck; > > +} > > + > > +# create first domain and start it > > +diag "Trying domain lookup by name"; > > +my $dom1; > > +my $domain_name ="f12nwtest"; > > This is where you'd want to start the guest domain > > > + > > +ok_domain { $dom1 = $conn->get_domain_by_name($domain_name) } "the > > running domain object"; > > +my $xml = $dom1->get_xml_description; > > +diag $xml; > > +ok($dom1->get_id() > 0, "running domain has an ID > 0"); > > +my $mac1 = get_macaddress($xml); > > +diag $mac1; > > +my $guestip1 = get_ip_from_leases($mac1); > > +diag "ip is $guestip1"; > > + > > +# check ebtables entry > > +my $ebtable1 = `/sbin/ebtables -L;/sbin/ebtables -t nat -L`; > > +diag $ebtable1; > > +# fixme to include mac adress > > +ok($ebtable1 =~ "vnet0", "check ebtables entry"); > > + > > +# ping guest1 > > +my $ping1 = `ping -c 10 $guestip1`; > > +diag $ping1; > > +ok($ping1 =~ "10 received", "ping $guestip1 test"); > > And run 'destroy' here. If you use a transient guest when > initially booting ($conn->create_domain($xml)) then you'll > not have to worry about the undefine step. > > > > + > > +sub get_macaddress { > > + my $xmldesc = shift; > > + > > + my $mac; > > + my $parser = XML::LibXML->new(); > > + > > + my $doc = $parser->parse_string($xmldesc); > > + > > + my $rootel = $doc -> getDocumentElement(); > > + > > + my @devices = $rootel->getChildrenByTagName("devices"); > > + foreach my $device(@devices) { > > + my @interfaces = $device->getChildrenByTagName("interface"); > > + foreach my $interface(@interfaces) { > > + my @targets = $interface->getChildrenByTagName("mac"); > > + foreach my $target(@targets) { > > + $mac = $target->getAttribute("address"); > > + } > > + } > > + } > > + utf8::decode($mac); > > + return $mac; > > +} > > You can simplify this using XPath - the $tck object provides a > convenient method for performing an XPath query on a $dom object. > eg that whole method can just be > > my $resultt = xpath($dom, "/domain/devices/interface/mac/\@address") > my @macaddrs = map { $_->getNodeValue} $result->get_nodelist; > ok .. but not done yet > > Daniel -- Best regards, Gerhard Stenzel, ----------------------------------------------------------------------------------------------------------------------------------- IBM Deutschland Research & Development GmbH Vorsitzender des Aufsichtsrats: Martin Jetter Geschäftsführung: Dirk Wittkopp Sitz der Gesellschaft: Böblingen Registergericht: Amtsgericht Stuttgart, HRB 243294 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list