summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBill Peck <bpeck@redhat.com>2014-06-18 10:58:23 -0400
committerBill Peck <bpeck@redhat.com>2014-06-18 10:58:23 -0400
commitb81acc58e24227379093000edd61818029441686 (patch)
treed339f267c74bc86bfedb93a83d20f4fb9506baec
parentcb96f1c472f0bcfcddf38c108c24d385a59d2328 (diff)
downloadtests-b81acc58e24227379093000edd61818029441686.tar.gz
tests-b81acc58e24227379093000edd61818029441686.tar.xz
tests-b81acc58e24227379093000edd61818029441686.zip
new /virt/import test which will use pre-existing images
-rw-r--r--distribution/virt/import/Makefile124
-rw-r--r--distribution/virt/import/PURPOSE81
-rwxr-xr-xdistribution/virt/import/filetopty.sh80
-rwxr-xr-xdistribution/virt/import/get_guest_fqdn.py39
-rwxr-xr-xdistribution/virt/import/get_guest_info.py44
-rwxr-xr-xdistribution/virt/import/getconsolefile17
-rwxr-xr-xdistribution/virt/import/getguestnames.sh15
-rwxr-xr-xdistribution/virt/import/guest1cmd70
-rwxr-xr-xdistribution/virt/import/guestcheck4down12
-rwxr-xr-xdistribution/virt/import/guestcheck4up12
-rwxr-xr-xdistribution/virt/import/guestgethostname55
-rwxr-xr-xdistribution/virt/import/iptables_after_libvirtd.initd41
-rwxr-xr-xdistribution/virt/import/isfileconsole18
-rwxr-xr-xdistribution/virt/import/ishvm16
-rwxr-xr-xdistribution/virt/import/iskvm11
-rwxr-xr-xdistribution/virt/import/isparavirt16
-rwxr-xr-xdistribution/virt/import/isptyconsole18
-rwxr-xr-xdistribution/virt/import/isxen11
-rwxr-xr-xdistribution/virt/import/log_guest.exp22
-rw-r--r--distribution/virt/import/logguestconsoles.c945
-rwxr-xr-xdistribution/virt/import/logguestconsoles.initd109
-rwxr-xr-xdistribution/virt/import/lxml_guestname_resolution.py54
-rwxr-xr-xdistribution/virt/import/minidom_guestname_resolution.py54
-rwxr-xr-xdistribution/virt/import/ptytofile.sh74
-rwxr-xr-xdistribution/virt/import/rhel5_write_consolelogs.initd111
-rwxr-xr-xdistribution/virt/import/rhel5_write_consolelogs.py532
-rwxr-xr-xdistribution/virt/import/rhts_submit_virt_logs58
-rwxr-xr-xdistribution/virt/import/rhts_virt_funcs.sh166
-rwxr-xr-xdistribution/virt/import/runtest.sh1112
-rwxr-xr-xdistribution/virt/import/scp.exp62
-rwxr-xr-xdistribution/virt/import/start_recipe.py23
-rwxr-xr-xdistribution/virt/import/startguest61
-rwxr-xr-xdistribution/virt/import/stopguest56
-rwxr-xr-xdistribution/virt/import/virshstartguest68
-rwxr-xr-xdistribution/virt/import/virshstopguest56
-rwxr-xr-xdistribution/virt/import/wait4guest51
-rwxr-xr-xdistribution/virt/import/wait4guesttasks201
-rwxr-xr-xdistribution/virt/import/wait4login48
-rwxr-xr-xdistribution/virt/import/wait4shutdown41
-rwxr-xr-xdistribution/virt/import/xmltramp.py361
-rwxr-xr-xdistribution/virt/import/xmstartguest61
-rwxr-xr-xdistribution/virt/import/xmstopguest52
-rwxr-xr-xdistribution/virt/import/zrhel5_write_consolelogs.initd111
-rwxr-xr-xdistribution/virt/import/zrhel5_write_consolelogs.py537
44 files changed, 5706 insertions, 0 deletions
diff --git a/distribution/virt/import/Makefile b/distribution/virt/import/Makefile
new file mode 100644
index 0000000..4c718bb
--- /dev/null
+++ b/distribution/virt/import/Makefile
@@ -0,0 +1,124 @@
+# The toplevel namespace within which the test lives.
+TOPLEVEL_NAMESPACE=/distribution
+
+# The name of the package under test:
+PACKAGE_NAME=virt
+
+# The path of the test below the package:
+RELATIVE_PATH=import
+
+# Preserve the RPM name from the old repo location:
+export RHTS_RPM_NAME=distribution-distribution-virt-import
+
+# Version of the Test. Used with make tag.
+export TESTVERSION=4.0
+
+# The compiled namespace of the test.
+export TEST=$(TOPLEVEL_NAMESPACE)/$(PACKAGE_NAME)/$(RELATIVE_PATH)
+
+# Define to copy files
+define cpfile
+ echo "Chmod a+x $(1)" ; \
+ chmod a+x ./$(1) ; \
+ echo "Copying $(1)" ; \
+ cp ./$(1) /usr/local/bin/. ; \
+ echo "Restore SELinux context $(1)" ; \
+ restorecon -vv /usr/local/bin/$(1) ;
+endef
+
+BINFILES= guestcheck4down \
+ guestcheck4up \
+ guestgethostname \
+ startguest \
+ stopguest \
+ virshstartguest \
+ virshstopguest \
+ wait4shutdown \
+ wait4login \
+ wait4guest \
+ xmstopguest \
+ xmstartguest \
+ getguestnames.sh \
+ isparavirt \
+ ishvm \
+ isxen \
+ iskvm \
+ isptyconsole \
+ isfileconsole \
+ filetopty.sh \
+ ptytofile.sh \
+ getconsolefile \
+ get_guest_info.py \
+ get_guest_fqdn.py \
+ rhts_submit_virt_logs \
+ rhts_virt_funcs.sh \
+ guest1cmd \
+ scp.exp \
+ zrhel5_write_consolelogs.py \
+ lxml_guestname_resolution.py \
+ minidom_guestname_resolution.py \
+ wait4guesttasks
+
+FILES= $(METADATA) \
+ $(BINFILES) \
+ runtest.sh \
+ Makefile \
+ get_guest_info.py \
+ get_guest_fqdn.py \
+ start_recipe.py \
+ logguestconsoles.c \
+ logguestconsoles.initd \
+ zrhel5_write_consolelogs.initd \
+ iptables_after_libvirtd.initd
+
+binsetup:
+ $(foreach f,$(BINFILES),$(call cpfile,$(f)))
+
+run: binsetup
+ chmod a+x ./runtest.sh ./*.exp ./*.py
+ ./runtest.sh
+
+# Include Common Makefile
+include /usr/share/rhts/lib/rhts-make.include
+
+# Generate the testinfo.desc here:
+$(METADATA):
+ @touch $(METADATA)
+ @echo "Owner: Beaker Developers <beaker-devel@lists.fedorahosted.org>" > $(METADATA)
+ @echo "Name: $(TEST)" >> $(METADATA)
+ @echo "Path: $(TEST_DIR)" >> $(METADATA)
+ @echo "TestVersion: $(TESTVERSION)" >> $(METADATA)
+ @echo "Description: Test to install xen/kvm guests. Uses libvirt userspace tools." >> $(METADATA)
+ @echo "TestTime: 43200" >> $(METADATA)
+ @echo "Requires: $(PACKAGE_NAME)" >> $(METADATA)
+ @echo "Architectures: i386 x86_64 ia64" >> $(METADATA)
+ @echo "Priority: Manual" >> $(METADATA)
+ @echo "Requires: autofs" >> $(METADATA)
+ @echo "Requires: bridge-utils" >> $(METADATA)
+ @echo "Requires: expect" >> $(METADATA)
+ @echo "Requires: genisoimage" >> $(METADATA)
+ @echo "Requires: kernel-xen" >> $(METADATA)
+ @echo "Requires: kvm" >> $(METADATA)
+ @echo "Requires: libvirt" >> $(METADATA)
+ @echo "Requires: mkisofs" >> $(METADATA)
+ @echo "Requires: openssh-server" >> $(METADATA)
+ @echo "Requires: python-virtinst" >> $(METADATA)
+ @echo "Requires: virt-manager" >> $(METADATA)
+ @echo "Requires: virt-viewer" >> $(METADATA)
+ @echo "Requires: vnc" >> $(METADATA)
+ @echo "Requires: vnc-server" >> $(METADATA)
+ @echo "Requires: xen" >> $(METADATA)
+ @echo "Requires: xorg-x11-server-Xvfb" >> $(METADATA)
+ @echo "Requires: xmlrpc-c-devel" >> $(METADATA)
+ @echo "Requires: openssl-devel" >> $(METADATA)
+ @echo "Requires: libcurl-devel" >> $(METADATA)
+ @echo "Requires: libxml2-devel" >> $(METADATA)
+ @echo "Requires: @virtualization" >> $(METADATA)
+ @echo "Requires: @virtualization-platform" >> $(METADATA)
+ @echo "Requires: @virtualization-tools" >> $(METADATA)
+ @echo "Requires: @virtualization-client" >> $(METADATA)
+ @echo "Requires: @x11" >> $(METADATA)
+ @echo "Requires: @fonts" >> $(METADATA)
+ @echo "RunFor: xen-libs" >> $(METADATA)
+ @echo "RhtsOptions: -Compatible -CompatService" >> $(METADATA)
+ @echo "License: GPLv2" >> $(METADATA)
diff --git a/distribution/virt/import/PURPOSE b/distribution/virt/import/PURPOSE
new file mode 100644
index 0000000..4f7d0ce
--- /dev/null
+++ b/distribution/virt/import/PURPOSE
@@ -0,0 +1,81 @@
+This test is used to install guests on the dom0 host it's executing on. It
+doesn't need any arguments as the arguments are provided to it by the lab
+controller when the job has guestrecipe . This testis required to install the
+guest machines defined in the guestrecipes.
+
+For more information on how to write and execute virtualization testcases refer
+to the Virtualization workflow information in Beaker's documentation:
+ http://beaker-project.org/docs/user-guide/virtualization-workflow.html
+
+Guest console logs:
+For RHEL5 hypervisors, the guest console forwarding will be set up, unless the
+tester passes on "NORHEL5CONSOLELOGS" parameter to the test. If this
+environment variable is set, then the serial console forwarding won't be set.
+
+ Currently for rhel6 and over, it'll be set up during the installation time
+that all the console consoles will be forwarded to a file. This file will be at
+/mnt/tests/distribution/virt/install/guests/${guestname}/logs/${guestname}_console.log
+
+If the tester wants to login to the system and connect to the serial console of
+the guest, s/he will have to change the guest's configuration not to forward the
+serial console to the file. There are a couple scripts provided for this
+purpose, and they are:
+ filetopty.sh: cancel out the file forwarding and free up the console of the
+guest, so that one can connect it via "virsh console $guestname"
+ ptytofile.sh: The reverse of above.
+
+For the guests in rhel6 hosts, we had forwarded the serial console
+to a file output, but that had blocked the console access and required manual
+invertention if you wanted to change it to console login access via virsh
+console $guest and they were always mutually exclusive. Now you can have both by
+default. It'll still be forwarding serial console output to a file and upload it
+but it'll also allow you login to ttyS1 console via virsh console $guestname. By
+default virsh console tries to login to ttyS0, so to you have to provide it
+correct devname with:
+ virsh console $guestname --devname $devname
+
+To find out the devname just do virsh dumpxml $guestname and see what the name
+is for the pty console. For example:
+ $ virsh dumpxml i386_kvm
+ ...
+ <serial type='pty'>
+ <source path='/dev/pts/1'/>
+ <target port='1'/>
+ <alias name='serial1'/>
+ </serial>
+ ...
+
+So for this instance you'd do :
+ virsh console i386_kvm --devname serial1
+
+One caveat is that, we have rhts-compat service that's run and started up by
+beah, which grabs the console so the console i/o is garbled and unusable. To
+mitigate this, add a task in your guest recipes after /distribution/install to
+stop and disable the rhts-compat service.
+If you are doing this manually, you can reboot the guest in single mode and
+disable the service yourself using the console.
+
+==============================================================================
+Networking:
+By default this test sets up a bridge and uses bridged network for kvm guests.
+Multiple nics can be specified with "--network model=name" in guestargs, for
+example, "--network model=e1000 --network model=virtio" will install a guest
+with two nics, one e1000 the other virtio driver.
+
+==============================================================================
+
+
+Guest domain names:
+ /distribution/virt/start job will be editing host's
+ /etc/hosts file to have the guestname resolve to its respective IP. So, in the
+above instance, you can just do:
+ ssh root@i386_kvm
+and you should be in the guest.
+===============================================================================
+Test execution synchronization between the guests and hypervisor..
+In the case of a need to wait some specific tasks in a guest to execute before
+running some tasks in the hypervisor, wait4guesttasks script, provided in this
+task, can be used. For more information refer to the script itself.
+
+
+
diff --git a/distribution/virt/import/filetopty.sh b/distribution/virt/import/filetopty.sh
new file mode 100755
index 0000000..49473ef
--- /dev/null
+++ b/distribution/virt/import/filetopty.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+if [[ $# != 1 ]]; then
+ echo "Usage: $0 [guestname] "
+ exit 1
+fi
+
+# if the guest isn't shutdown, shut it down.. try at least 2 times
+guestup=0
+i=0
+while (( $i < 2 )) ;
+do
+ if virsh list | grep -w $1; then
+ guestup=1
+ if ! virsh shutdown $1 ; then
+ echo "problem with virsh shutdown $1"
+ exit 1
+ fi
+ sleep 90
+ fi
+ let "i=${i}+1"
+done
+
+if virsh list | grep -w $1; then
+ echo "the guest can't be brought down"
+ exit 1
+fi
+
+if ! virsh dumpxml $1 > $1.xml ; then
+ echo "problem with virsh dumpxml $1"
+ exit 1
+fi
+
+sed -n '
+# if the first line copy the pattern to the hold buffer
+1h
+# if not the first line then append the pattern to the hold buffer
+1!H
+# if the last line then ...
+$ {
+ # copy from the hold to the pattern buffer
+ g
+ # do the search and replace
+# <serial type='pty'>
+# <target port='0'/>
+# </serial>
+# <console type='pty'>
+# <target port='0'/>
+# </console>
+#
+ s/<serial type='\''file'\''>.*<\/console>/<serial type='\''pty'\''>\
+ <target port='\''0'\''\/>\
+ <\/serial>\
+ <console type='\''pty'\''>\
+ <target port='\''0'\''\/>\
+ <\/console>/g
+ # print
+ p
+}
+' $1.xml > $1.xml.tmp ;
+
+#redefine the guest with the edited xml
+if ! virsh define ./$1.xml.tmp; then
+ echo "problem with virsh define ./$1.xml.tmp"
+ exit 1
+fi
+
+if [[ ${guestup} == 1 ]]; then
+ if ! virsh start $1; then
+ echo "problem restarting guest $1 "
+ exit 1
+ fi
+ # give it some time to start up.
+ sleep 90
+
+ if ! virsh list | grep -w $1 ; then
+ echo "guest doesn't seem to be up after restart"
+ exit 1
+ fi
+fi
diff --git a/distribution/virt/import/get_guest_fqdn.py b/distribution/virt/import/get_guest_fqdn.py
new file mode 100755
index 0000000..9709fe2
--- /dev/null
+++ b/distribution/virt/import/get_guest_fqdn.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+import os
+import sys
+import xmlrpclib
+import xml.dom.minidom
+import time
+
+if len(sys.argv) is not 2:
+ print "Usage: %s RECIPEID" % __file__
+ sys.exit(1)
+recipeid = sys.argv[1]
+try:
+ int(recipeid)
+except:
+ print "RECIPEID must be an integer"
+ sys.exit(1)
+
+xml_tries = 1
+interval = 300
+proxy = xmlrpclib.ServerProxy('http://%s:8000/RPC2' % os.environ['LAB_CONTROLLER'])
+while xml_tries < 5:
+ try:
+ recipe_xml = proxy.get_my_recipe(dict(recipe_id=recipeid))
+ break
+ except:
+ print "Couldn't get guestinfo from %s . sleeping %i secs" % (os.environ['LAB_CONTROLLER'] , interval)
+ time.sleep(interval)
+ xml_tries += 1
+
+if xml_tries == 5:
+ print "Can't get guestinfo from %s" % os.environ['LAB_CONTROLLER']
+ sys.exit(1)
+
+
+doc = xml.dom.minidom.parseString(recipe_xml)
+
+for guestrecipe in doc.getElementsByTagName('guestrecipe'):
+ print guestrecipe.getAttribute('system')
diff --git a/distribution/virt/import/get_guest_info.py b/distribution/virt/import/get_guest_info.py
new file mode 100755
index 0000000..b184626
--- /dev/null
+++ b/distribution/virt/import/get_guest_info.py
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+
+import os
+import sys
+import xmlrpclib
+import xml.dom.minidom
+import time
+
+xml_tries = 1
+interval = 300
+proxy = xmlrpclib.ServerProxy('http://%s:8000/RPC2' % os.environ['LAB_CONTROLLER'])
+while xml_tries < 5:
+ try:
+ recipe_xml = proxy.get_my_recipe(dict(recipe_id=os.environ['RECIPEID']))
+ break
+ except:
+ print "Couldn't get guestinfo from %s . sleeping %i secs" % (os.environ['LAB_CONTROLLER'] , interval)
+ time.sleep(interval)
+ xml_tries += 1
+
+if xml_tries == 5:
+ print "Can't get guestinfo from %s" % os.environ['LAB_CONTROLLER']
+ sys.exit(1)
+
+
+doc = xml.dom.minidom.parseString(recipe_xml)
+
+if len(sys.argv) >= 2 and sys.argv[1] == '--kvm-num': # this is kind of a hack...
+ num = len(doc.getElementsByTagName('guestrecipe'))
+ kvm_num = len([guestrecipe for guestrecipe in doc.getElementsByTagName('guestrecipe')
+ if '--kvm' in guestrecipe.getAttribute('guestargs')])
+ if kvm_num and kvm_num < num:
+ sys.exit(2)
+ print kvm_num
+ sys.exit(0)
+
+for guestrecipe in doc.getElementsByTagName('guestrecipe'):
+ print ' '.join([
+ guestrecipe.getAttribute('id') or 'RECIPEIDMISSING',
+ guestrecipe.getAttribute('guestname')
+ or 'guestrecipe%s' % guestrecipe.getAttribute('id'),
+ guestrecipe.getAttribute('mac_address') or 'RANDOM',
+ guestrecipe.getAttribute('guestargs'),
+ ])
diff --git a/distribution/virt/import/getconsolefile b/distribution/virt/import/getconsolefile
new file mode 100755
index 0000000..6a783e5
--- /dev/null
+++ b/distribution/virt/import/getconsolefile
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+if [[ $# != 1 ]]; then
+ echo "Usage: $0 [guestname]"
+ exit 1
+fi
+
+if ! virsh list --all | grep -q $1; then
+ echo "guest $1 doesn't seem to exist"
+ exit 1
+fi
+
+if ! virsh dumpxml $1 | grep -A 3 "<serial type='file'>" | grep "source path=" | awk -F"'" '{print $2}'; then
+ exit 1
+else
+ exit 0
+fi
diff --git a/distribution/virt/import/getguestnames.sh b/distribution/virt/import/getguestnames.sh
new file mode 100755
index 0000000..fe2a007
--- /dev/null
+++ b/distribution/virt/import/getguestnames.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+#
+# this script prints out a comma-delimited list of the name of the virtual
+# guests that should be installed on this machine.
+#
+
+RESULT=""
+for dir in /mnt/tests/distribution/virt/install/guests/* ; do
+ guest_name=$(basename $dir)
+ RESULT="${guest_name}"",""${RESULT}"
+done
+RESULT=${RESULT%,}
+echo $RESULT
+exit 0
+
diff --git a/distribution/virt/import/guest1cmd b/distribution/virt/import/guest1cmd
new file mode 100755
index 0000000..c22c0dc
--- /dev/null
+++ b/distribution/virt/import/guest1cmd
@@ -0,0 +1,70 @@
+#!/usr/bin/expect
+
+if { $argc != 2 } {
+ send_user "Usage: $argv0 guestname cmd\n";
+ exit 1;
+}
+set guestname [lindex $argv 0]
+set cmd [lindex $argv 1]
+set prompt "\n(.*?(%|#|\\\$)) $"
+set user "root"
+set pass "rhts"
+set timeout 30
+set i 0
+log_user 0
+#exp_internal 1
+
+spawn virsh console $guestname
+sleep 3
+send "\n"
+send "\n"
+expect {
+ -exact "login: " { send "$user\n"; exp_continue; }
+ -re "(P|p)assword:" { send "$pass\n"; sleep 10; send  }
+ timeout { send  }
+ eof { }
+}
+spawn virsh console $guestname
+send "\n"
+send "\n"
+sleep 5
+expect {
+ -re $prompt {
+ set thelist [split $expect_out(0,string) "\n" ]
+ set theprompt [string trim [lindex $thelist end]]
+ send "$cmd\n"; send ;
+ }
+
+ timeout {
+ if { $i < 3 } {
+ send "\n"
+ incr i
+ exp_continue
+ } else {
+ send_user "didn't get prompt in $timeout seconds \n";
+ exit 1;
+ }
+ }
+ eof {
+ send_user "Unexpected EOF ..\n";
+ exit 1;
+ }
+}
+
+spawn virsh console $guestname
+expect {
+ -re $prompt {
+ set idx [string first "$theprompt" $expect_out(1,string)]
+ set out [string range $expect_out(1,string) 0 [expr $idx - 1]]
+ send_user "${out}"
+ }
+ timeout {
+ send_user "timeout waiting for the prompt\n"
+ }
+ eof {
+ send_user "eof waiting for the prompt\n"
+ }
+}
+
+exit 0
+
diff --git a/distribution/virt/import/guestcheck4down b/distribution/virt/import/guestcheck4down
new file mode 100755
index 0000000..1199992
--- /dev/null
+++ b/distribution/virt/import/guestcheck4down
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+if [[ $# < 1 ]]; then
+ echo "Usage: $0 guestname"
+ exit 1
+fi
+
+if ! virsh list | grep -q $1; then
+ exit 0
+else
+ exit 1
+fi
diff --git a/distribution/virt/import/guestcheck4up b/distribution/virt/import/guestcheck4up
new file mode 100755
index 0000000..5d31065
--- /dev/null
+++ b/distribution/virt/import/guestcheck4up
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+if [[ $# < 1 ]]; then
+ echo "Usage: $0 guestname"
+ exit 1
+fi
+
+if virsh list | grep -q $1; then
+ exit 0
+else
+ exit 1
+fi
diff --git a/distribution/virt/import/guestgethostname b/distribution/virt/import/guestgethostname
new file mode 100755
index 0000000..85c5ef1
--- /dev/null
+++ b/distribution/virt/import/guestgethostname
@@ -0,0 +1,55 @@
+#!/usr/bin/expect
+
+if { $argc != 1 } {
+ send_user "Usage: $argv0 guestname \n";
+ exit 1;
+}
+set guestname [lindex $argv 0]
+set prompt "\n(.*?(%|#|\\\$)) $"
+set user "root"
+set pass "rhts"
+set i 0
+log_user 0
+
+spawn virsh console $guestname
+expect {
+ -exact "login: " { send "$user\n"; exp_continue; }
+ -re "(P|p)assword:" { send "$pass\n"; exp_continue; }
+ -re $prompt {
+ set thelist [split $expect_out(0,string) "\n" ]
+ set theprompt [string trim [lindex $thelist end]]
+ send "hostname\n";
+ }
+
+ timeout {
+ if { $i < 2 } {
+ send "\n"
+ incr i
+ exp_continue
+ } else {
+ send_user "didn't get prompt in $timeout seconds \n";
+ exit 1;
+ }
+ }
+ eof {
+ send_user "Unexpected EOF ..\n";
+ exit 1;
+ }
+}
+
+expect {
+ -re $prompt {
+ set idx [string first "$theprompt" $expect_out(1,string)]
+ set host [string range $expect_out(1,string) 0 [expr $idx - 1]]
+ send_user "${host}"
+ }
+ timeout {
+ send_user "timeout waiting for the prompt\n"
+ }
+ eof {
+ send_user "eof waiting for the prompt\n"
+ }
+}
+
+exit 0
+
diff --git a/distribution/virt/import/iptables_after_libvirtd.initd b/distribution/virt/import/iptables_after_libvirtd.initd
new file mode 100755
index 0000000..b5257b9
--- /dev/null
+++ b/distribution/virt/import/iptables_after_libvirtd.initd
@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# chkconfig: 2345 98 99
+# description: Agent for reporting virtual guest IDs to subscription-manager
+
+### BEGIN INIT INFO
+# Provides: iptables_after_libvirtd
+# Required-Start: $network libvirtd
+# Required-Stop:
+# Should-Start:
+# Should-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: start and stop iptables_after_libvirtd
+# Description: Agent for reporting virtual guest IDs to subscription-manager
+### END INIT INFO
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+start() {
+ iptables -I INPUT -p tcp --dport 8000 -j ACCEPT
+ iptables -I INPUT -p udp --dport 8000 -j ACCEPT
+}
+
+stop() {
+ echo "iptables_after_libvirtd."
+}
+
+case "$1" in
+ start)
+ start
+ RETVAL=$?
+ ;;
+ stop)
+ stop
+ RETVAL=$?
+ ;;
+esac
+exit $RETVAL
+
diff --git a/distribution/virt/import/isfileconsole b/distribution/virt/import/isfileconsole
new file mode 100755
index 0000000..d9da20b
--- /dev/null
+++ b/distribution/virt/import/isfileconsole
@@ -0,0 +1,18 @@
+#/bin/bash
+#
+
+if [[ $# != 1 ]]; then
+ echo "Usage: $0 [guestname]"
+ exit 1
+fi
+
+if ! virsh dumpxml $1; then
+ echo "problem with virsh dumpxml $1"
+ exit 1
+fi
+
+if virsh dumpxml $1 | grep -q "<serial type='file'>"; then
+ exit 0
+else
+ exit 1
+fi
diff --git a/distribution/virt/import/ishvm b/distribution/virt/import/ishvm
new file mode 100755
index 0000000..2ad7d9f
--- /dev/null
+++ b/distribution/virt/import/ishvm
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# script to determine if it's an hvm guest
+# returns 0 if it is, non-zero if it is not.
+#
+
+if [[ $# != 1 ]]; then
+ echo "Need a guest name as an argument"
+ exit 1;
+fi
+
+if virsh dumpxml $1 | grep -q '>hvm<'; then
+ exit 0
+else
+ exit 1
+fi
diff --git a/distribution/virt/import/iskvm b/distribution/virt/import/iskvm
new file mode 100755
index 0000000..284d97b
--- /dev/null
+++ b/distribution/virt/import/iskvm
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+# a stupid script to determine is we're running on kvm or not
+# returns zero if it's, non-zero if it's not
+#
+
+if lsmod | grep -q kvm; then
+ exit 0
+else
+ exit 1
+fi
diff --git a/distribution/virt/import/isparavirt b/distribution/virt/import/isparavirt
new file mode 100755
index 0000000..59ad35b
--- /dev/null
+++ b/distribution/virt/import/isparavirt
@@ -0,0 +1,16 @@
+#!/bin/bash
+#
+# script to determine if it's paravirt guest
+# returns 0 if it is, non-zero if it is not.
+#
+
+if [[ $# != 1 ]]; then
+ echo "Need a guest name as an argument"
+ exit 1;
+fi
+
+if virsh dumpxml $1 | grep -q '>hvm<'; then
+ exit 1
+else
+ exit 0
+fi
diff --git a/distribution/virt/import/isptyconsole b/distribution/virt/import/isptyconsole
new file mode 100755
index 0000000..1361085
--- /dev/null
+++ b/distribution/virt/import/isptyconsole
@@ -0,0 +1,18 @@
+#/bin/bash
+#
+
+if [[ $# != 1 ]]; then
+ echo "Usage: $0 [guestname]"
+ exit 1
+fi
+
+if ! virsh dumpxml $1; then
+ echo "problem with virsh dumpxml $1"
+ exit 1
+fi
+
+if virsh dumpxml $1 | grep -q "<serial type='pty'>"; then
+ exit 0
+else
+ exit 1
+fi
diff --git a/distribution/virt/import/isxen b/distribution/virt/import/isxen
new file mode 100755
index 0000000..44c2650
--- /dev/null
+++ b/distribution/virt/import/isxen
@@ -0,0 +1,11 @@
+#!/bin/bash
+#
+# a stupid script to determine is the system is running on xen kernel or not
+# returns zero if it is, non-zero if it's not..
+
+if uname -r | grep -q xen; then
+ exit 0
+else
+ exit 1
+fi
+
diff --git a/distribution/virt/import/log_guest.exp b/distribution/virt/import/log_guest.exp
new file mode 100755
index 0000000..9c92427
--- /dev/null
+++ b/distribution/virt/import/log_guest.exp
@@ -0,0 +1,22 @@
+#!/usr/bin/expect
+
+if { $argc != 1 } {
+ send_user "Usage: $argv0 <guestname> \n";
+ exit 1;
+}
+
+set timeout -1
+set guestname [lindex $argv 0]
+set logfile [open /var/log/xen/console/guest-${guestname}.log a]
+fconfigure $logfile -buffering line
+while {1} {
+
+ spawn virsh console $guestname
+ set console_spawn $spawn_id
+ expect {
+ -i $console_spawn .* { puts $logfile $expect_out(buffer); exp_continue; }
+ -i $console_spawn eof { sleep 3; wait -i $console_spawn }
+ }
+
+}
+
diff --git a/distribution/virt/import/logguestconsoles.c b/distribution/virt/import/logguestconsoles.c
new file mode 100644
index 0000000..d9e3e2e
--- /dev/null
+++ b/distribution/virt/import/logguestconsoles.c
@@ -0,0 +1,945 @@
+/*
+ * yum -y install xmlrpc-c-devel
+ * gcc -g -O0 -Wall logguestconsoles.c -o logguestconsoles -lssl $(xmlrpc-c-config client --libs) \
+ * -lcurl `pkg-config libvirt --libs` `xml2-config --cflags` `xml2-config --libs`
+ *
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <libgen.h>
+#include <regex.h>
+#include <sys/inotify.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <openssl/evp.h>
+#include <xmlrpc-c/base.h>
+#include <xmlrpc-c/client.h>
+#include <curl/curl.h>
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/tree.h>
+
+#define EVENT_SIZE ((sizeof(struct inotify_event)+FILENAME_MAX)*512)
+#define BUF_SIZE 4096
+#define PANIC_REGEX "(Kernel panic|Oops|general protection fault|general protection handler: wrong gs|\\(XEN\\) Panic)"
+#define STDOUT_REDIRECT "/var/log/logguestconsoles.out"
+#define STDERR_REDIRECT "/var/log/logguestconsoles.err"
+
+#define DEBUG(X,fmt,...) if (DEBUG_LEVEL >= X ) { fprintf(stderr, "DEBUG%d : %s, %d " fmt "\n", X, __FILE__, __LINE__, __VA_ARGS__ ); }
+#define DEBUG0(fmt,...) DEBUG(0,fmt,__VA_ARGS__)
+#define DEBUG1(fmt,...) DEBUG(1,fmt,__VA_ARGS__)
+#define DEBUG2(fmt,...) DEBUG(2,fmt,__VA_ARGS__)
+#define DEBUG3(fmt,...) DEBUG(3,fmt,__VA_ARGS__)
+#define DEBUG4(fmt,...) DEBUG(4,fmt,__VA_ARGS__)
+#define DEBUG5(fmt,...) DEBUG(5,fmt,__VA_ARGS__)
+#define DEBUG6(fmt,...) DEBUG(6,fmt,__VA_ARGS__)
+
+#define LIST_INIT(mylist, thestructptr) \
+ mylist.firstel = &thestructptr; \
+ mylist.lastel = &thestructptr;
+
+#define LIST_ADD(mylist, thestructptr) \
+ mylist.lastel->next = thestructptr; \
+ mylist.lastel = thestructptr;
+
+typedef struct file_record file_record;
+struct file_record {
+ char *infilename;
+ char *outfilename;
+ int in_fd;
+ int out_fd;
+ int wd;
+ off_t in_pos;
+ off_t pos;
+ off_t last_uploaded_pos;
+ int recipe_id;
+ int panic; /* 0 : no info, 1 : paniced , -1 : watchdog=panic */
+ int panic_taskid;
+ struct file_record *next;
+};
+typedef struct file_record *file_record_ptr;
+
+typedef struct file_record_lst {
+ file_record_ptr firstel;
+ file_record_ptr lastel;
+} file_record_list;
+
+/* function prototypes */
+size_t get_recipe_http_res(char *, size_t , size_t , void *);
+int prepare_and_upload(file_record_ptr);
+static void startelement(void *, const xmlChar *, const xmlChar **);
+static int read_config_file(int , const char *);
+static void get_task_id(file_record_ptr);
+static void abort_recipe(int);
+/* end of prototypes */
+
+/* global vars */
+int DEBUG_LEVEL = 0;
+file_record_list thelist;
+char * url = NULL;
+static const char base64table[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+int inotify_fd;
+char * config_file;
+regex_t regex;
+
+/* libcurl-related stuff */
+struct http_res_struct {
+ char *response;
+ int size;
+};
+typedef struct http_res_struct http_res_struct;
+typedef http_res_struct * http_res_struct_ptr;
+/* end of libcurl-related vars */
+
+///// libxml2 vars //////////
+enum states {
+ IN_RECIPE, /* inside the recipe we are interested in*/
+ NOT_IN_RECIPE, /* not in the recipe we are searching for */
+ FOUND
+} parser_state;
+
+static xmlSAXHandler handler = {
+ NULL, /* internalSubset */
+ NULL, /* isStandalone */
+ NULL, /* hasInternalSubset */
+ NULL, /* hasExternalSubset */
+ NULL, /* resolveEntity */
+ NULL, /* getEntity */
+ NULL, /* entityDecl */
+ NULL, /* notationDecl */
+ NULL, /* attributeDecl */
+ NULL, /* elementDecl */
+ NULL, /* unparsedEntityDecl */
+ NULL, /* setDocumentLocator */
+ NULL, /* startDocument */
+ NULL, /* endDocument */
+ startelement, /* startElement */
+ NULL, /* endElement */
+ NULL, /* reference */
+ NULL, /* characters */
+ NULL, /* ignorableWhitespace */
+ NULL, /* processingInstruction */
+ NULL, /* comment */
+ NULL, /* xmlParserWarning */
+ NULL, /* xmlParserError */
+ NULL, /* xmlParserError */
+ NULL, /* getParameterEntity */
+ NULL, /* cdataBlock; */
+ NULL, /* externalSubset; */
+ 1,
+ NULL,
+ NULL, /* startElementNs */
+ NULL, /* endElementNs */
+ NULL /* xmlStructuredErrorFunc */
+};
+
+static xmlSAXHandlerPtr saxhandler = &handler;
+//// end of libxml2 //////
+
+size_t get_recipe_http_res(char *res_ptr, size_t size, size_t nb_byte, void *userdata) {
+
+ int total_bytes;
+ total_bytes = size * nb_byte;
+ http_res_struct_ptr my_response = (http_res_struct_ptr) userdata;
+
+ my_response->response = realloc(my_response->response, (my_response->size + total_bytes + 1));
+ if ( my_response->response == NULL ) {
+ fprintf(stderr, "problem allocating memory for my_response->response");
+ return -1;
+ }
+ memcpy(&(my_response->response[my_response->size]), res_ptr, total_bytes);
+ if ( &my_response->response[my_response->size] == NULL ) {
+ fprintf(stderr, "problem with copying to my_response->response");
+ return -1;
+ }
+ my_response->size += total_bytes;
+ my_response->response[my_response->size] = '\0';
+
+ DEBUG4("total bytes :%d", total_bytes);
+ DEBUG4("total size: %d", my_response->size);
+ return total_bytes;
+}
+
+//////////////// LIBXML2 ////////////////////////////////////////////////
+/* startelement callback function for saxparser */
+static void
+startelement(void *user_data, const xmlChar *name, const xmlChar **atts)
+{
+ int i=0, recipe_id_tmp;
+ int recipe_id;
+ file_record_ptr tmp_structptr;
+
+ tmp_structptr = (file_record_ptr) user_data;
+ recipe_id = tmp_structptr->recipe_id;
+ DEBUG3(" callback for recipe %d ", recipe_id);
+
+ if ( parser_state == FOUND || tmp_structptr->panic == -1 ) {
+ DEBUG3("%s" , "parser state is found or panic is set to -1");
+ return;
+ }
+ if ( !strcmp((char *)name, "guestrecipe") ) {
+ if (atts != NULL ) {
+ for (i=0; (atts[i] != NULL); i++ ) {
+ if (!strcmp((char *)atts[i],"id")) {
+ i++;
+ recipe_id_tmp = atoi((const char *)atts[i]);
+ if ( recipe_id_tmp == recipe_id ) {
+ parser_state = IN_RECIPE;
+ } else {
+ parser_state = NOT_IN_RECIPE;
+ }
+ }
+ }
+ }
+ return;
+ }
+ /* <watchdog panic="ignore"/> */
+ if ( !strcmp((char *)name, "watchdog") && parser_state == IN_RECIPE ) {
+ if ( atts != NULL ) {
+ for (i=0; (atts[i] != NULL); i++ ) {
+ if (!strcmp((char *)atts[i],"panic")) {
+ i++;
+ if(!strcmp((char *)atts[i],"ignore")) {
+ DEBUG3("panic ignored for recipe %d", recipe_id);
+ tmp_structptr->panic = -1;
+ }
+ }
+ }
+ }
+ return;
+ }
+
+ if ( !strcmp((char *)name, "task" ) && parser_state == IN_RECIPE ) {
+ if ( atts != NULL ) {
+ for (i=0; (atts[i] != NULL); i++ ) {
+ if (!strcmp((char *)atts[i],"id")) {
+ i++;
+ tmp_structptr->panic_taskid = atoi((const char *)atts[i]);
+ tmp_structptr->panic = 1;
+ }
+ if(!strcmp((char *)atts[i],"status")) {
+ i++;
+ if(!strcmp((char *)atts[i],"Running")) {
+ parser_state = FOUND;
+ }
+
+ }
+ }
+ }
+ }
+ return;
+}
+/*
+ * reads config file which has the original console log file name per line
+ */
+static int read_config_file(int inotify_fd, const char *configfile ) {
+ char *infilename = NULL;
+ char *outfilename = NULL;
+ int in_fd, out_fd, wd, errno;
+ long int recipe_id;
+ off_t pos_ret, in_pos_ret;
+ FILE *fp;
+ ssize_t line_read;
+ size_t line_len = 0;
+ int file_len;
+ char *line = NULL;
+ char *check_str = NULL;
+ char *ch = NULL;
+ char *recipe_str = NULL;
+ char *endptr;
+ const char *suffix = ".out";
+ int i = 0;
+ file_record_ptr newrecord = NULL;
+
+ if ( configfile == NULL ) {
+ fprintf(stderr, "Problem in read_config_file");
+ return 1;
+ }
+ fp = fopen(configfile, "r");
+ if (!fp) {
+ perror("problem opening configfile: ");
+ return 1;
+ }
+
+ while ( (line_read = getline(&line, &line_len, fp) ) != -1 ) {
+ check_str = NULL;
+ i = 0;
+ /* ignore comments */
+ if ( line[0] == '#' || isspace(line[0])) {
+ continue;
+ }
+
+ ch = strchr(line, ' ');
+ if(!ch) {
+ fprintf(stderr, "Wrong format of config file. Filename Recipeid\n");
+ fclose(fp);
+ return 1;
+ }
+ file_len = ch - line;
+ infilename = malloc( (size_t) ( file_len + 1 ) );
+ memcpy(infilename, &line[0], file_len);
+ infilename[file_len] = '\0';
+
+ /* if we already watching this file, continue */
+ file_record_ptr el;
+ for (el = thelist.firstel; el != NULL; el=el->next) {
+ if (strcmp(el->infilename,infilename) == 0 ) {
+ check_str = el->infilename;
+ break;
+ }
+ }
+ if ( check_str != NULL ) {
+ fprintf(stderr,"%s is already being watched\n",el->infilename);
+ continue;
+ }
+
+ /* got the filename to watch , now get the recipeid */
+ recipe_str=(char *)malloc((size_t) (line_read - file_len));
+ if(!recipe_str) {
+ perror("Error mallocing recipe_str: ");
+ fclose(fp);
+ return 1;
+ }
+ while ( *ch != '\0' && *ch != '\n' ) {
+ if ( isspace(*ch) ) {
+ ch++;
+ continue;
+ }
+ recipe_str[i++] = *ch++;
+ }
+ recipe_str[i] = '\0';
+ errno = 0;
+ recipe_id = strtol(recipe_str, &endptr, 10);
+ if(errno) {
+ perror("Problem converting recipeid to int");
+ return 1;
+ }
+ if ( *endptr != '\0') {
+ fprintf(stderr, "%s is not a valid number\n", recipe_str);
+ return 1;
+ }
+ fprintf(stdout, "recipe_id is: %ld \n", recipe_id);
+ free(recipe_str);
+
+ /* allocate space for the output file */
+ outfilename = malloc( (size_t) file_len + 5 );
+ if (!outfilename) {
+ perror("Error mallocing for outfilename: ");
+ return 1;
+ }
+ memcpy(outfilename, infilename, file_len);
+ strcpy(&outfilename[file_len], suffix);
+ fprintf(stdout, "outfilename is: %s \n", outfilename);
+ in_fd = open(infilename, O_RDONLY|O_CREAT, 0777);
+ if ( in_fd == -1 ) {
+ perror("open in_fd: ");
+ return 1;
+ }
+ out_fd = open(outfilename, O_WRONLY|O_CREAT|O_SYNC , 0664);
+ if ( out_fd == -1 ) {
+ perror("open out_fd: ");
+ return 1;
+ }
+
+ in_pos_ret = lseek(in_fd, 0, SEEK_END);
+ if (in_pos_ret < 0 ) {
+ fprintf(stderr, "lseek in_fd\n");
+ in_pos_ret = 0;
+ }
+ fprintf(stdout, "in_pos_ret is: %d\n", (int)in_pos_ret);
+
+ pos_ret = lseek(out_fd, 0, SEEK_END);
+ fprintf(stdout, "pos_ret is: %d\n", (int)pos_ret);
+ if (pos_ret == (off_t) -1 ) {
+ fprintf(stderr, "Problem seeking outputfile\n");
+ close(in_fd);
+ close(out_fd);
+ return 1;
+ }
+ /* initialize a struct */
+ newrecord = (file_record_ptr) malloc(sizeof(file_record));
+ if (!newrecord) {
+ perror("Error mallocing for newrecord: ");
+ return 1;
+ }
+ wd = inotify_add_watch (inotify_fd, infilename, IN_MODIFY|IN_CLOSE_WRITE|IN_CLOSE_NOWRITE);
+ if ( wd < 0 ) {
+ perror("Error in inotify_add_watch in __FUNC__ ");
+ return 1;
+ }
+ fprintf(stdout, "added file %s of recipe %ld for %s to descriptor %d \n", infilename, recipe_id, outfilename, inotify_fd);
+ fprintf(stdout, "in_fd %d , outfd: %d \n", in_fd, out_fd );
+ fflush(stdout);
+
+ newrecord->infilename = strdup(infilename);
+ newrecord->outfilename = outfilename;
+ newrecord->in_fd = in_fd;
+ newrecord->out_fd = out_fd;
+ newrecord->wd = wd;
+ newrecord->in_pos = in_pos_ret;
+ newrecord->pos = pos_ret;
+ newrecord->last_uploaded_pos = pos_ret;
+ newrecord->recipe_id = recipe_id;
+ newrecord->panic = 0;
+ newrecord->panic_taskid = -1;
+ newrecord->next = NULL;
+
+
+ if (thelist.firstel == NULL ) {
+ thelist.firstel = newrecord;
+ thelist.lastel = newrecord;
+ } else {
+ LIST_ADD(thelist, newrecord);
+ }
+
+ check_str = NULL;
+
+ }
+ fclose(fp);
+ return 0;
+}
+
+static void submit_panic(int task_id) {
+ xmlrpc_env env;
+ xmlrpc_client *clientP;
+ xmlrpc_value *resultP;
+
+ xmlrpc_env_init(&env);
+ if(env.fault_occurred) {
+ fprintf(stderr, "XML-RPC Fault in xmlrpc_env_init: %s (%d)\n",
+ env.fault_string, env.fault_code);
+ }
+ xmlrpc_client_setup_global_const(&env);
+ xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, "xmlrpc client", "1.0", NULL, 0, &clientP);
+ if(env.fault_occurred) {
+ fprintf(stderr, "XML-RPC Fault in xmlrpc_client_create: %s (%d)\n",
+ env.fault_string, env.fault_code);
+ }
+ /* task_result(task()['id'], 'panic', '/', 0, panic.group()) */
+ xmlrpc_client_call2f(&env, clientP, url, "task_result", &resultP, "(issis)",
+ (xmlrpc_int) task_id, "panic", "/", (xmlrpc_int)0, " ");
+ if(env.fault_occurred) {
+ fprintf(stderr, "XML-RPC Fault in xmlrpc_client_call2f: %s (%d)\n",
+ env.fault_string, env.fault_code);
+ }
+ xmlrpc_env_clean(&env);
+ xmlrpc_client_destroy(clientP);
+ xmlrpc_client_teardown_global_const();
+
+ return;
+
+}
+
+static void abort_recipe(int recipe_id) {
+ CURL *curl;
+ CURLcode res;
+ char recipe_str[32] = { '\0' };
+ char * recipe_url_str;
+ int host_len;
+ /* url: http://%s:8000/RPC2 */
+ /* /recipes/(recipe_id)/status */
+ sprintf(&recipe_str[0], "%d" , recipe_id);
+ host_len = strlen(url) - 4 ;
+ recipe_url_str = (char *) malloc(host_len + 15 + strlen(recipe_str) + 1 );
+ recipe_url_str = strncpy(recipe_url_str, url, host_len);
+ sprintf(&recipe_url_str[host_len], "recipes/%s/status", recipe_str);
+ recipe_url_str[host_len+15+strlen(recipe_str)] = '\0';
+
+ curl = curl_easy_init();
+ if(curl) {
+
+ curl_easy_setopt(curl, CURLOPT_URL, recipe_url_str);
+ curl_easy_setopt(curl, CURLOPT_POST, 1);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "status=Aborted");
+ /* Perform the request, res will get the return code */
+ res = curl_easy_perform(curl);
+ /* Check for errors */
+ if(res != CURLE_OK) {
+ fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
+ } else if ( res == 400 ) {
+ DEBUG0("status update for recipe %d failed ..", recipe_id);
+ }
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ free(recipe_url_str);
+ }
+}
+
+static void get_task_id(file_record_ptr therecord) {
+ CURL *curl;
+ CURLcode res;
+ xmlParserCtxtPtr ctxt;
+ http_res_struct response_struct;
+ char recipe_str[32] = { '\0' };
+ char * recipe_url_str;
+ int host_len;
+ int recipe_id = therecord->recipe_id;
+ response_struct.response = (char *) malloc(1);
+ response_struct.size = 0;
+ parser_state = NOT_IN_RECIPE;
+
+ sprintf(&recipe_str[0], "%d" , recipe_id);
+ host_len = strlen(url) - 4 ;
+ recipe_url_str = (char *) malloc(host_len + 8 + strlen(recipe_str) + 2 );
+ recipe_url_str = strncpy(recipe_url_str, url, host_len);
+ sprintf(&recipe_url_str[host_len], "recipes/%s/", recipe_str);
+ recipe_url_str[host_len+8+strlen(recipe_str)+1] = '\0';
+
+ curl = curl_easy_init();
+ if(curl) {
+ curl_easy_setopt(curl, CURLOPT_URL, recipe_url_str);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, get_recipe_http_res);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_struct);
+ /* Perform the request, res will get the return code */
+ res = curl_easy_perform(curl);
+ /* Check for errors */
+ if(res != CURLE_OK) {
+ fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
+ }
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ free(recipe_url_str);
+ }
+
+ /* parse xml */
+ ctxt = xmlCreatePushParserCtxt(saxhandler, (void *)therecord, NULL, 0, NULL);
+ xmlParseChunk(ctxt, response_struct.response, (strlen(response_struct.response) + 1), 0);
+ xmlParseChunk(ctxt, response_struct.response, 0, 1);
+ xmlFreeParserCtxt(ctxt);
+ free(response_struct.response);
+ return;
+
+}
+
+int prepare_and_upload(file_record_ptr therecord) {
+
+ /* file i/o */
+ off_t start_pos, end_pos;
+ char *filename;
+ FILE *fp;
+ off_t rc;
+ char *read_buf;
+ size_t read_size;
+ size_t read_rc;
+ int i = 0;
+ /* md5 stuff */
+ EVP_MD_CTX mdctx;
+ const EVP_MD *md;
+ unsigned char md_value[EVP_MAX_MD_SIZE];
+ char md_hexdigest[EVP_MAX_MD_SIZE*2];
+ unsigned int md_len;
+ int j;
+ /* xmlrpc */
+ xmlrpc_env env;
+ xmlrpc_client *clientP;
+ xmlrpc_value *resultP;
+ /* base64 */
+ int len = 0;
+ int inlen = 0;
+ int padit = 0;
+ int outlen = 0;
+ int outsize;
+ char *outstream;
+
+ filename = therecord->outfilename;
+ start_pos = therecord->last_uploaded_pos;
+ end_pos = therecord->pos;
+
+ OpenSSL_add_all_digests();
+
+ md = EVP_get_digestbyname("md5");
+ if(!md) {
+ printf("Unknown message digest ");
+ exit(1);
+ }
+
+ read_size = (size_t) end_pos - start_pos;
+ if ( read_size == 0 ) {
+ return 0;
+ }
+ //printf("readsize: %d startpos: %d endpos: %d \n", (int)read_size, (int)start_pos, (int)end_pos);
+ /* read_size + 1 for the terminating \0 for regexec() function */
+ read_buf = (char *)malloc(read_size+1);
+ if (!read_buf) {
+ perror("Error mallocing read_buf: ");
+ return 1;
+ }
+ memset(read_buf, '\0', read_size+1);
+ fp = fopen(filename, "r");
+ if(!fp) {
+ perror("Error opening file in __FUNC__ :");
+ return 1;
+ }
+
+ rc = fseek(fp, start_pos, SEEK_SET);
+ if ( rc == (off_t) -1 ) {
+ perror("Error in lseek: ");
+ fclose(fp);
+ return 1;
+ }
+ read_rc = fread( read_buf, 1, read_size, fp);
+ if (read_rc < 0 && (feof(fp) > 0 ) ) {
+ fprintf(stderr, "Problem with reading from %s, %d\n", filename, (int)read_rc);
+ fprintf(stderr, "read_buf is now: |%s| \n", read_buf);
+ fclose(fp);
+ free(read_buf);
+ return 1;
+ }
+ fclose(fp);
+ DEBUG5("Read buf: %s", read_buf);
+
+ /* get rid of ^M s */
+ for(i = 0; i < read_size; i++) {
+ if (read_buf[i] == 0x0d) {
+ read_buf[i] = 0x20;
+ }
+ }
+
+ /* see if there is a panic string in the console */
+ rc = regexec(&regex, read_buf, 0, NULL, 0);
+ if ( rc == 0 && therecord->panic == 0 ) {
+ DEBUG4("Found panic string on recipe %d ", therecord->recipe_id);
+ /* if match , submit panic result */
+ get_task_id(therecord);
+ /* make sure that we get panic_task_id */
+ if ( therecord->panic == 1 && therecord->panic_taskid != -1 ) {
+ DEBUG1("submitting panic for recipe %d taskid %d", therecord->recipe_id,therecord->panic_taskid);
+ submit_panic(therecord->panic_taskid);
+ }
+
+ }
+
+
+ /* encode the read_buffer */
+ outsize = (( read_size / 3 ) * 4 );
+ if ( read_size%3 > 0 ) {
+ outsize += 4;
+ }
+ /* for terminating \0 */
+ outsize++;
+ outstream = (char *) malloc((size_t)outsize);
+ if(!outstream) {
+ perror("Allocating for outstream:\n");
+ free(read_buf);
+ return 1;
+ }
+ memset(outstream, '\0', outsize);
+ while ( len < read_size ) {
+
+ for( i = 0; i < 3; i++,inlen++) {
+ if ( inlen+1 > read_size ) {
+ padit++;
+ }
+
+ if ( i == 2 && (read_size > (len+2))) {
+ outstream[outlen++] = (unsigned char) base64table[ (int)(read_buf[inlen-2] >> 2) ];
+ outstream[outlen++] = (unsigned char) base64table[ (int)(((read_buf[inlen-2] & 0x03) << 4) | ((read_buf[inlen-1] & 0xf0) >> 4)) ];
+ outstream[outlen++] = (unsigned char) (padit >= 2 ? '=' : base64table[ (int)(((read_buf[inlen-1] & 0x0f) << 2) | ((read_buf[inlen] & 0xc0) >> 6)) ]);
+ outstream[outlen++] = (unsigned char) (padit >= 1 ? '=' : base64table[ (int)(read_buf[inlen] & 0x3f) ] );
+ } else if ( i == 2 && (read_size == len+2) ) {
+ outstream[outlen++] = (unsigned char) base64table[ (int)(read_buf[inlen-2] >> 2) ];
+ outstream[outlen++] = (unsigned char) base64table[ (int)(((read_buf[inlen-2] & 0x03) << 4) | ((read_buf[inlen-1] & 0xf0) >> 4)) ];
+ outstream[outlen++] = (unsigned char) base64table[ (int)(((read_buf[inlen-1] & 0x0f) << 2))];
+ outstream[outlen++] = '=';
+ } else if ( i == 2 && (read_size == len+1) ) {
+ outstream[outlen++] = (unsigned char) base64table[ (int)(read_buf[inlen-2] >> 2) ];
+ outstream[outlen++] = (unsigned char) base64table[ (int)(((read_buf[inlen-2] & 0x03) << 4)) ];
+ outstream[outlen++] = '=';
+ outstream[outlen++] = '=';
+ }
+ }
+ len += 3;
+ }
+
+ outstream[outlen] = '\0';
+
+ /* md5 hashsum of the blob */
+ EVP_MD_CTX_init(&mdctx);
+ EVP_DigestInit_ex(&mdctx, md, NULL);
+ EVP_DigestUpdate(&mdctx, read_buf, read_size);
+ EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
+ EVP_MD_CTX_cleanup(&mdctx);
+
+ for(i = 0, j = 0; i < md_len; i++){
+ sprintf(&md_hexdigest[j], "%02x", md_value[i]);
+ j += 2;
+ }
+ md_hexdigest[j] = '\0';
+
+
+ xmlrpc_env_init(&env);
+ if(env.fault_occurred) {
+ fprintf(stderr, "XML-RPC Fault in xmlrpc_env_init: %s (%d)\n",
+ env.fault_string, env.fault_code);
+ }
+ xmlrpc_client_setup_global_const(&env);
+ xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, "xmlrpc client", "1.0", NULL, 0, &clientP);
+ if(env.fault_occurred) {
+ fprintf(stderr, "XML-RPC Fault in xmlrpc_client_create: %s (%d)\n",
+ env.fault_string, env.fault_code);
+ }
+ xmlrpc_client_call2f(&env, clientP, url, "recipe_upload_file", &resultP, "(issisis)",
+ (xmlrpc_int) therecord->recipe_id, "/", "console.log", (xmlrpc_int)read_size,
+ md_hexdigest,(xmlrpc_int)start_pos, outstream);
+ if(env.fault_occurred) {
+ fprintf(stderr, "XML-RPC Fault in xmlrpc_client_call2f: %s (%d)\n",
+ env.fault_string, env.fault_code);
+ }
+ xmlrpc_env_clean(&env);
+ xmlrpc_client_destroy(clientP);
+ xmlrpc_client_teardown_global_const();
+
+ therecord->last_uploaded_pos = (end_pos - 1);
+ /* if we got panic and no ignore, update the the recipe status */
+ if ( therecord->panic == 1 ) {
+ DEBUG1("Aborting recipe: %d ", therecord->recipe_id);
+ abort_recipe(therecord->recipe_id);
+ }
+ free(outstream);
+ free(read_buf);
+ return 0;
+}
+
+void sighandler (int sig)
+{
+ DEBUG0("calling sighandler for %d", sig);
+
+ if ( sig == SIGTERM || sig == SIGINT ) {
+ DEBUG0("calling sighandler for %d", sig);
+ fflush(stdout);
+ fflush(stderr);
+ } else if ( sig == SIGUSR1 ) {
+ DEBUG0("got SIGUSR1, reloading the config file %s", config_file);
+ fflush(stdout);
+ fflush(stderr);
+ read_config_file(inotify_fd, config_file);
+ }
+}
+
+int main(int argc, char *argv[], char *envp[]) {
+ int wr_ret;
+ int in_fd, out_fd;
+ ssize_t len = -1 , i = 0;
+ char events[EVENT_SIZE] = {0};
+ ssize_t read_bytes;
+ char read_buf[BUF_SIZE];
+ off_t in_pos, in_pos_end;
+ int rc;
+ int found = 0;
+ char *tmp_url = NULL;
+ char *pidfile = NULL;
+ char *baseprogname = NULL;
+ char *lc_arg = NULL;
+ FILE *PIDFP;
+ pid_t pid, sid, childpid;
+ thelist.firstel = NULL;
+ thelist.lastel = NULL;
+ struct sigaction reload_action;
+
+ memset(&reload_action, 0, sizeof(reload_action));
+ reload_action.sa_handler = sighandler;
+
+
+
+ if ( argc < 3 ) {
+ fprintf(stderr, "Usage: %s [--config configfile] <--lc lab_controller>\n", argv[0]);
+ return 1;
+ }
+
+ for(i = 0; i < argc; i++) {
+ if ( strncmp(argv[i],"--config",8) == 0 ) {
+ i++;
+ config_file=strdup(argv[i]);
+ } else if ( strncmp(argv[i],"--pidfile",9) == 0 ) {
+ i++;
+ pidfile=strdup(argv[i]);
+ } else if ( strncmp(argv[i],"--lc", 4) == 0 ) {
+ i++;
+ lc_arg=strdup(argv[i]);
+ } else if ( strncmp(argv[i],"--v",3) == 0 ) {
+ int debug_len = strlen(argv[i]) - 2;
+ char * tmp_str = (char *) malloc(debug_len+1);
+
+ memset(tmp_str, 'v', debug_len);
+ tmp_str[debug_len] = '\0';
+ if ( strncmp(tmp_str, &argv[i][2], debug_len) == 0 ) {
+ DEBUG_LEVEL = debug_len;
+ printf("Debug level : %d \n", DEBUG_LEVEL);
+ }
+ }
+ }
+ if (!config_file) {
+ fprintf(stderr, "Usage: %s [--config configfile] <--pidfile pidfile>\n", argv[0]);
+ return 1;
+ }
+ if ( !pidfile ) {
+ baseprogname = basename(argv[0]);
+ i = strlen(baseprogname);
+ pidfile = (char *)malloc(i+14);
+ strncpy(pidfile,"/var/run/", 9);
+ strncpy(&pidfile[9],baseprogname,i);
+ strcpy(&pidfile[9+i],".pid");
+ }
+
+ sigaction(SIGTERM, &reload_action, NULL);
+ sigaction(SIGINT, &reload_action, NULL);
+ sigaction(SIGUSR1, &reload_action, NULL);
+ /* daemonize */
+ pid = fork();
+ if ( pid < 0 ) {
+ perror("Error with forking\n");
+ return 1;
+ } else if ( pid > 0 ) {
+ return 0;
+ }
+
+ sid = setsid();
+ if ( sid < 0 ) {
+ perror("problem with setsid(): \n");
+ return 1;
+ }
+
+ if ( chdir("/") != 0 ) {
+ perror("problem with chdir(): \n");
+ return 1;
+ }
+ childpid = getpid();
+ fprintf(stdout,"childpid : %d \n", childpid);
+ PIDFP = fopen(pidfile, "w" );
+ if (!PIDFP) {
+ fprintf(stderr, "Can't open pidfile");
+ return 1;
+ }
+ rc = fprintf(PIDFP, "%d\n", childpid);
+ if ( rc < 0 ) {
+ fprintf(stderr, "Problem with writing the pidfile");
+ return 1;
+ }
+ fflush(PIDFP);
+
+ close(STDIN_FILENO);
+ freopen(STDOUT_REDIRECT, "a", stdout);
+ if(!stdout) {
+ perror("Error redirecting stdout: \n");
+ return 1;
+ }
+ freopen(STDERR_REDIRECT, "a", stderr);
+ if(!stderr) {
+ perror("Error redirecting stderr: \n");
+ return 1;
+ }
+
+ /* if we get lan controller given on the command line, use it , or else see if
+ * there is LAB_CONTROLLER env. variable
+ */
+ if ( lc_arg ) {
+ url = lc_arg;
+ } else {
+ url = getenv("LAB_CONTROLLER");
+ }
+
+ if ( url == NULL ) {
+ fprintf(stderr, "No Lab controller given on command line or env. vars\n");
+ return 1;
+ }
+ /* http://$LAB_CONTROLLER:8000/RPC2 */
+ tmp_url = (char *) malloc(strlen(url) + 18);
+ snprintf(tmp_url, (size_t) (strlen(url) + 18), "http://%s:8000/RPC2", url);
+ url = tmp_url;
+
+ fprintf(stdout, "LAB CONTROLLER is : %s \n", url);
+
+ /* compile regex */
+ rc = regcomp(&regex, PANIC_REGEX, REG_EXTENDED);
+ if ( rc ) {
+ perror("Problem with compiling regex: ");
+ return 1;
+ }
+
+ inotify_fd = inotify_init();
+ if ( inotify_fd < 0 ) {
+ perror("Error inotify_init: ");
+ return 1;
+ }
+ rc = read_config_file(inotify_fd, config_file);
+ if(rc) {
+ fprintf(stderr, "Error reading config file\n");
+ return 1;
+ }
+ while (1) {
+ len = read (inotify_fd, events, EVENT_SIZE);
+ for (i=0; i < len; i++) {
+ struct inotify_event *event = (struct inotify_event *)&events[i];
+ /* find the wd in the list */
+ file_record_ptr el = NULL;
+ found = 0;
+ for (el = thelist.firstel; el != NULL; el=el->next) {
+ if (el->wd == event->wd ) {
+ in_fd = el->in_fd;
+ out_fd = el->out_fd;
+ found = 1;
+ DEBUG3("event for recipe: %d", el->recipe_id);
+ break;
+ }
+ }
+
+ if ( found == 0 ) {
+ continue;
+ }
+
+ if (event->mask & IN_MODIFY) {
+ in_pos = lseek(in_fd, 0, SEEK_CUR);
+ if (in_pos < 0) {
+ fprintf(stderr, "IN_MODIFY lseek err\n");
+ } else {
+ in_pos_end = lseek(in_fd, 0, SEEK_END);
+ if (in_pos_end < 0) {
+ fprintf(stderr, "lseek SEEK_END err\n");
+ } else {
+ /* in file was truncated, seek to beginning */
+ if (in_pos_end < el->in_pos)
+ in_pos = 0;
+ }
+ lseek(in_fd, in_pos, SEEK_SET);
+ }
+
+ while( (read_bytes = read(in_fd, read_buf, BUF_SIZE) ) > 0 ) {
+ wr_ret = write(out_fd,read_buf,read_bytes);
+ DEBUG3("written in_fd: %d", in_fd);
+ DEBUG3("written %d bytes:", wr_ret);
+ if ( wr_ret == -1 ) {
+ perror("Error writing out: ");
+ return 1;
+ }
+ el->pos = lseek(out_fd, 0L, SEEK_CUR);
+ if ( el->pos == (off_t) -1 ) {
+ fprintf(stderr,"Problem reading pos from outfd\n");
+ return 1;
+ }
+ if ((off_t) (el->pos - el->last_uploaded_pos) > 1024) {
+ prepare_and_upload(el);
+ }
+ }
+ in_pos_end = lseek(in_fd, 0, SEEK_CUR);
+ if (in_pos_end < 0) {
+ fprintf(stderr, "lseek in_fd after write err\n");
+ } else {
+ el->in_pos = in_pos_end;
+ }
+ } else if ((event->mask & IN_CLOSE_WRITE) || (event->mask & IN_CLOSE_NOWRITE) ) {
+ prepare_and_upload(el);
+ }
+
+ }
+ bzero(&events[0],EVENT_SIZE);
+ }
+ return 0;
+}
+
diff --git a/distribution/virt/import/logguestconsoles.initd b/distribution/virt/import/logguestconsoles.initd
new file mode 100755
index 0000000..8698f6d
--- /dev/null
+++ b/distribution/virt/import/logguestconsoles.initd
@@ -0,0 +1,109 @@
+#!/bin/sh
+#
+# logguestconsoles Agent for reporting virtual guest IDs to subscription-manager
+#
+# chkconfig: 2345 95 99
+# description: Agent for reporting virtual guest IDs to subscription-manager
+
+### BEGIN INIT INFO
+# Provides: logguestconsoles
+# Required-Start: $network libvirtd
+# Required-Stop:
+# Should-Start:
+# Should-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: start and stop logguestconsoles
+# Description: Agent for reporting virtual guest IDs to subscription-manager
+### END INIT INFO
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+exec="/usr/local/bin/logguestconsoles"
+prog="logguestconsoles"
+config="/usr/local/etc/logguestconsoles.conf"
+pidfile="/var/run/$prog.pid"
+args="--config $config"
+
+# Export all variables in /etc/sysconfig/logguestconsoles
+set -a
+[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
+# below is where we'll get the LAB_CONTROLLER
+[ -e /etc/profile.d/rh-env.sh ] && . /etc/profile.d/rh-env.sh
+set +a
+
+lockfile=/var/lock/subsys/logguestconsoles
+
+start() {
+ [ -x $exec ] || exit 5
+ echo -n $"Starting $prog: "
+ daemon --pidfile $pidfile $exec $args
+ retval=$?
+ echo
+ [ $retval -eq 0 ] && touch $lockfile
+ return $retval
+}
+
+stop() {
+ echo -n $"Stopping $prog: "
+ killproc -p $pidfile $prog
+ retval=$?
+ echo
+ [ $retval -eq 0 ] && rm -f $lockfile
+ return $retval
+}
+
+restart() {
+ stop
+ start
+}
+
+reload() {
+ kill -s USR1 $(cat $pidfile)
+}
+
+force_reload() {
+ restart
+}
+
+rh_status() {
+ status -p $pidfile $prog
+}
+
+rh_status_q() {
+ rh_status >/dev/null 2>&1
+}
+
+
+case "$1" in
+ start)
+ rh_status_q && exit 0
+ $1
+ ;;
+ stop)
+ rh_status_q || exit 0
+ $1
+ ;;
+ restart)
+ $1
+ ;;
+ reload)
+ rh_status_q || exit 7
+ $1
+ ;;
+ force-reload)
+ force_reload
+ ;;
+ status)
+ rh_status
+ ;;
+ condrestart|try-restart)
+ rh_status_q || exit 0
+ restart
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
+ exit 2
+esac
+exit $?
diff --git a/distribution/virt/import/lxml_guestname_resolution.py b/distribution/virt/import/lxml_guestname_resolution.py
new file mode 100755
index 0000000..551a238
--- /dev/null
+++ b/distribution/virt/import/lxml_guestname_resolution.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+#
+# this script is used to update the /etc/hosts file in the host/dom0 so that the
+# guestname will be resolved to the guest's IP address.
+#
+
+import sys
+import os
+import time
+import xmlrpclib
+from lxml import etree
+from cStringIO import StringIO
+from optparse import OptionParser
+import socket
+
+#start here
+usage = "usage: %prog --recipeid "
+parser = OptionParser()
+parser.add_option("-r", "--recipeid", dest="recipeid", help="recipe id of the guest to hostname of.")
+(options, args) = parser.parse_args()
+recipeid = options.recipeid
+
+xml_tries = 1
+interval = 300
+proxy = xmlrpclib.ServerProxy('http://%s:8000/RPC2' % os.environ['LAB_CONTROLLER'])
+while xml_tries < 5:
+ try:
+ recipe_xml = proxy.get_my_recipe(dict(recipe_id=recipeid))
+ break
+ except:
+ print "Couldn't get guestinfo from %s . sleeping %i secs" % (os.environ['LAB_CONTROLLER'] , interval)
+ time.sleep(interval)
+ xml_tries += 1
+
+if xml_tries == 5:
+ print "Can't get guestinfo from %s" % os.environ['LAB_CONTROLLER']
+ sys.exit(1)
+
+thetree = etree.parse(StringIO(recipe_xml))
+for node in thetree.xpath('/job/recipeSet/recipe/guestrecipe'):
+ print "in node"
+ if node.attrib['id'] == recipeid:
+ guestname = node.attrib['guestname']
+ guesthost = node.attrib['system']
+ guestip = socket.gethostbyname(guesthost)
+ print "guestname: " + guestname + " guesthost: " + guesthost + " ip: " + guestip
+ # update /etc/hosts with this info
+ fh = open('/etc/hosts', 'a')
+ fh.write(guestip+" "+guestname+"\n")
+ fh.close()
+
+sys.exit(0)
+
+
diff --git a/distribution/virt/import/minidom_guestname_resolution.py b/distribution/virt/import/minidom_guestname_resolution.py
new file mode 100755
index 0000000..ec88246
--- /dev/null
+++ b/distribution/virt/import/minidom_guestname_resolution.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+#
+# this script is used to update the /etc/hosts file in the host/dom0 so that the
+# guestname will be resolved to the guest's IP address.
+#
+
+import sys
+import os
+import time
+import xmlrpclib
+import xml.dom.minidom
+from cStringIO import StringIO
+from optparse import OptionParser
+import socket
+
+#start here
+usage = "usage: %prog --recipeid "
+parser = OptionParser()
+parser.add_option("-r", "--recipeid", dest="recipeid", help="recipe id of the guest to hostname of.")
+(options, args) = parser.parse_args()
+recipeid = options.recipeid
+
+xml_tries = 1
+interval = 300
+proxy = xmlrpclib.ServerProxy('http://%s:8000/RPC2' % os.environ['LAB_CONTROLLER'])
+while xml_tries < 5:
+ try:
+ recipe_xml = proxy.get_my_recipe(dict(recipe_id=recipeid))
+ break
+ except:
+ print "Couldn't get guestinfo from %s . sleeping %i secs" % (os.environ['LAB_CONTROLLER'] , interval)
+ time.sleep(interval)
+ xml_tries += 1
+
+if xml_tries == 5:
+ print "Can't get guestinfo from %s" % os.environ['LAB_CONTROLLER']
+ sys.exit(1)
+
+dom = xml.dom.minidom.parseString(recipe_xml)
+for el in dom.getElementsByTagName('guestrecipe'):
+ mydict = dict(el.attributes.items())
+ for key in mydict:
+ if key == 'id' and mydict[key] == recipeid:
+ guestname = mydict['guestname']
+ guesthost = mydict['system']
+ guestip = socket.gethostbyname(guesthost)
+ print "guestname: " + guestname + " guesthost: " + guesthost + " ip: " + guestip
+ # update /etc/hosts with this info
+ fh = open('/etc/hosts', 'a')
+ fh.write(guestip+" "+guestname+"\n")
+ fh.close()
+sys.exit(0)
+
+
diff --git a/distribution/virt/import/ptytofile.sh b/distribution/virt/import/ptytofile.sh
new file mode 100755
index 0000000..68f3b78
--- /dev/null
+++ b/distribution/virt/import/ptytofile.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+if [[ $# != 1 ]]; then
+ echo "Usage: $0 [guestname] "
+ exit 1
+fi
+
+# if the guest isn't shutdown, shut it down.. try at least 2 times
+guestup=0
+i=0
+while (( $i < 2 )) ;
+do
+ if virsh list | grep -w $1; then
+ guestup=1
+ if ! virsh shutdown $1 ; then
+ echo "problem with virsh shutdown $1"
+ exit 1
+ fi
+ sleep 90
+ fi
+ let "i=${i}+1"
+done
+
+if virsh list | grep -w $1; then
+ echo "the guest can't be brought down"
+ exit 1
+fi
+
+if ! virsh dumpxml $1 > $1.xml ; then
+ echo "problem with virsh dumpxml $1"
+ exit 1
+fi
+
+# workaround for BZ 731115
+l_guest_name=$(echo $1 | tr [:upper:] [:lower:])
+REPL_STR=$(cat << EOF
+ <serial type='file'>\n
+ <source path='/mnt/tests/distribution/virt/install/${l_guest_name}/logs/${l_guest_name}_console.log'/>\n
+ <target port='0'/>\n
+ </serial>\n
+ <console type='file'>\n
+ <source path='/mnt/tests/distribution/virt/install/${l_guest_name}/logs/${l_guest_name}_console.log'/>\n
+ <target port='0'/>\n
+ </console>
+EOF
+)
+
+serialline=$(grep -n '<serial ' $1.xml | awk -F: '{print $1}')
+let "serialline=${serialline}-1"
+consoleline=$(grep -n '</console>' $1.xml | awk -F: '{print $1}')
+let "consoleline=${consoleline}+1"
+sed -n '1,'"${serialline}"'p' $1.xml > $1.xml.tmp
+echo -e $REPL_STR >> $1.xml.tmp
+sed -n ''"${consoleline}"',$p' $1.xml >> $1.xml.tmp
+
+#redefine the guest with the edited xml
+if ! virsh define ./$1.xml.tmp; then
+ echo "problem with virsh define ./$1.xml.tmp"
+ exit 1
+fi
+
+if [[ ${guestup} == 1 ]]; then
+ if ! virsh start $1; then
+ echo "problem restarting guest $1 "
+ exit 1
+ fi
+ # give it some time to start up.
+ sleep 90
+
+ if ! virsh list | grep -w $1 ; then
+ echo "guest doesn't seem to be up after restart"
+ exit 1
+ fi
+fi
diff --git a/distribution/virt/import/rhel5_write_consolelogs.initd b/distribution/virt/import/rhel5_write_consolelogs.initd
new file mode 100755
index 0000000..2365e80
--- /dev/null
+++ b/distribution/virt/import/rhel5_write_consolelogs.initd
@@ -0,0 +1,111 @@
+#!/bin/sh
+#
+# logguestconsoles Agent for reporting virtual guest IDs to subscription-manager
+#
+# chkconfig: 2345 95 95
+# description: Agent for reporting virtual guest IDs to subscription-manager
+
+### BEGIN INIT INFO
+# Provides: logguestconsoles
+# Required-Start: $network libvirtd
+# Required-Stop:
+# Should-Start:
+# Should-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: start and stop logguestconsoles
+# Description: Agent for reporting virtual guest IDs to subscription-manager
+### END INIT INFO
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+exec="/usr/local/bin/rhel5_write_consolelogs"
+prog="rhel5_write_consolelogs"
+#config="/usr/local/etc/logguestconsoles.conf"
+pidfile="/var/run/$prog.pid"
+args=""
+
+# Export all variables in /etc/sysconfig/logguestconsoles
+set -a
+[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
+# below is where we'll get the LAB_CONTROLLER
+[ -e /etc/profile.d/rh-env.sh ] && . /etc/profile.d/rh-env.sh
+set +a
+
+lockfile=/var/lock/subsys/rhel5_write_consolelogs
+
+start() {
+ [ -x $exec ] || exit 5
+ echo -n $"Starting $prog: "
+ #daemon --pidfile $pidfile nohup $exec &
+ nohup $exec &
+ childpid=$!
+ if kill -0 ${childpid}; then
+ echo
+ touch $lockfile
+ echo "${childpid}" > ${pidfile}
+ return 0
+ else
+ return -1
+ fi
+}
+
+stop() {
+ echo -n $"Stopping $prog: "
+ killproc -p $pidfile $prog
+ retval=$?
+ echo
+ [ $retval -eq 0 ] && rm -f $lockfile
+ return $retval
+}
+
+restart() {
+ stop
+ start
+}
+
+force_reload() {
+ restart
+}
+
+rh_status() {
+ status -p $pidfile $prog
+}
+
+rh_status_q() {
+ rh_status >/dev/null 2>&1
+}
+
+
+case "$1" in
+ start)
+ rh_status_q && exit 0
+ $1
+ ;;
+ stop)
+ rh_status_q || exit 0
+ $1
+ ;;
+ restart)
+ $1
+ ;;
+ reload)
+ rh_status_q || exit 7
+ $1
+ ;;
+ force-reload)
+ force_reload
+ ;;
+ status)
+ rh_status
+ ;;
+ condrestart|try-restart)
+ rh_status_q || exit 0
+ restart
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
+ exit 2
+esac
+exit $?
diff --git a/distribution/virt/import/rhel5_write_consolelogs.py b/distribution/virt/import/rhel5_write_consolelogs.py
new file mode 100755
index 0000000..b34cc6e
--- /dev/null
+++ b/distribution/virt/import/rhel5_write_consolelogs.py
@@ -0,0 +1,532 @@
+#!/usr/bin/python -u
+#
+#
+#
+#################################################################################
+# Start off by implementing a general purpose event loop for anyones use
+#################################################################################
+
+import sys
+import getopt
+import os
+import libvirt
+import select
+import errno
+import time
+import threading
+import subprocess
+import signal
+import pty
+from xml.dom import minidom
+from optparse import OptionParser
+
+debugstr = 0
+
+#
+# This general purpose event loop will support waiting for file handle
+# I/O and errors events, as well as scheduling repeatable timers with
+# a fixed interval.
+#
+# It is a pure python implementation based around the poll() API
+#
+class virEventLoopPure:
+ # This class contains the data we need to track for a
+ # single file handle
+ class virEventLoopPureHandle:
+ def __init__(self, handle, fd, events, cb, opaque):
+ self.handle = handle
+ self.fd = fd
+ self.events = events
+ self.cb = cb
+ self.opaque = opaque
+
+ def get_id(self):
+ return self.handle
+
+ def get_fd(self):
+ return self.fd
+
+ def get_events(self):
+ return self.events
+
+ def set_events(self, events):
+ self.events = events
+
+ def dispatch(self, events):
+ self.cb(self.handle,
+ self.fd,
+ events,
+ self.opaque[0],
+ self.opaque[1])
+
+ # This class contains the data we need to track for a
+ # single periodic timer
+ class virEventLoopPureTimer:
+ def __init__(self, timer, interval, cb, opaque):
+ self.timer = timer
+ self.interval = interval
+ self.cb = cb
+ self.opaque = opaque
+ self.lastfired = 0
+
+ def get_id(self):
+ return self.timer
+
+ def get_interval(self):
+ return self.interval
+
+ def set_interval(self, interval):
+ self.interval = interval
+
+ def get_last_fired(self):
+ return self.lastfired
+
+ def set_last_fired(self, now):
+ self.lastfired = now
+
+ def dispatch(self):
+ self.cb(self.timer,
+ self.opaque[0],
+ self.opaque[1])
+
+
+ def __init__(self, debug=False):
+ self.debugOn = debug
+ self.poll = select.poll()
+ self.pipetrick = os.pipe()
+ self.nextHandleID = 1
+ self.nextTimerID = 1
+ self.handles = []
+ self.timers = []
+ self.quit = False
+
+ # The event loop can be used from multiple threads at once.
+ # Specifically while the main thread is sleeping in poll()
+ # waiting for events to occur, another thread may come along
+ # and add/update/remove a file handle, or timer. When this
+ # happens we need to interrupt the poll() sleep in the other
+ # thread, so that it'll see the file handle / timer changes.
+ #
+ # Using OS level signals for this is very unreliable and
+ # hard to implement correctly. Thus we use the real classic
+ # "self pipe" trick. A anonymous pipe, with one end registered
+ # with the event loop for input events. When we need to force
+ # the main thread out of a poll() sleep, we simple write a
+ # single byte of data to the other end of the pipe.
+ self.debug("Self pipe watch %d write %d" %(self.pipetrick[0], self.pipetrick[1]))
+ self.poll.register(self.pipetrick[0], select.POLLIN)
+
+ def debug(self, msg):
+ if self.debugOn:
+ print msg
+
+
+ # Calculate when the next timeout is due to occurr, returning
+ # the absolute timestamp for the next timeout, or 0 if there is
+ # no timeout due
+ def next_timeout(self):
+ next = 0
+ for t in self.timers:
+ last = t.get_last_fired()
+ interval = t.get_interval()
+ if interval < 0:
+ continue
+ if next == 0 or (last + interval) < next:
+ next = last + interval
+
+ return next
+
+ # Lookup a virEventLoopPureHandle object based on file descriptor
+ def get_handle_by_fd(self, fd):
+ for h in self.handles:
+ if h.get_fd() == fd:
+ return h
+ return None
+
+ # Lookup a virEventLoopPureHandle object based on its event loop ID
+ def get_handle_by_id(self, handleID):
+ for h in self.handles:
+ if h.get_id() == handleID:
+ return h
+ return None
+
+
+ # This is the heart of the event loop, performing one single
+ # iteration. It asks when the next timeout is due, and then
+ # calcuates the maximum amount of time it is able to sleep
+ # for in poll() pending file handle events.
+ #
+ # It then goes into the poll() sleep.
+ #
+ # When poll() returns, there will zero or more file handle
+ # events which need to be dispatched to registered callbacks
+ # It may also be time to fire some periodic timers.
+ #
+ # Due to the coarse granularity of schedular timeslices, if
+ # we ask for a sleep of 500ms in order to satisfy a timer, we
+ # may return upto 1 schedular timeslice early. So even though
+ # our sleep timeout was reached, the registered timer may not
+ # technically be at its expiry point. This leads to us going
+ # back around the loop with a crazy 5ms sleep. So when checking
+ # if timeouts are due, we allow a margin of 20ms, to avoid
+ # these pointless repeated tiny sleeps.
+ def run_once(self):
+ sleep = -1
+ next = self.next_timeout()
+ self.debug("Next timeout due at %d" % next)
+ if next > 0:
+ now = int(time.time() * 1000)
+ if now >= next:
+ sleep = 0
+ else:
+ sleep = (next - now) / 1000.0
+
+ self.debug("Poll with a sleep of %d" % sleep)
+ events = self.poll.poll(sleep)
+
+ # Dispatch any file handle events that occurred
+ for (fd, revents) in events:
+ # See if the events was from the self-pipe
+ # telling us to wakup. if so, then discard
+ # the data just continue
+ if fd == self.pipetrick[0]:
+ data = os.read(fd, 1)
+ continue
+
+ h = self.get_handle_by_fd(fd)
+ if h:
+ self.debug("Dispatch fd %d handle %d events %d" % (fd, h.get_id(), revents))
+ h.dispatch(self.events_from_poll(revents))
+
+ now = int(time.time() * 1000)
+ for t in self.timers:
+ interval = t.get_interval()
+ if interval < 0:
+ continue
+
+ want = t.get_last_fired() + interval
+ # Deduct 20ms, since schedular timeslice
+ # means we could be ever so slightly early
+ if now >= (want-20):
+ self.debug("Dispatch timer %d now %s want %s" % (t.get_id(), str(now), str(want)))
+ t.set_last_fired(now)
+ t.dispatch()
+
+
+ # Actually the event loop forever
+ def run_loop(self):
+ self.quit = False
+ while not self.quit:
+ self.run_once()
+
+ def interrupt(self):
+ os.write(self.pipetrick[1], 'c')
+
+
+ # Registers a new file handle 'fd', monitoring for 'events' (libvirt
+ # event constants), firing the callback cb() when an event occurs.
+ # Returns a unique integer identier for this handle, that should be
+ # used to later update/remove it
+ def add_handle(self, fd, events, cb, opaque):
+ handleID = self.nextHandleID + 1
+ self.nextHandleID = self.nextHandleID + 1
+
+ h = self.virEventLoopPureHandle(handleID, fd, events, cb, opaque)
+ self.handles.append(h)
+
+ self.poll.register(fd, self.events_to_poll(events))
+ self.interrupt()
+
+ self.debug("Add handle %d fd %d events %d" % (handleID, fd, events))
+
+ return handleID
+
+ # Registers a new timer with periodic expiry at 'interval' ms,
+ # firing cb() each time the timer expires. If 'interval' is -1,
+ # then the timer is registered, but not enabled
+ # Returns a unique integer identier for this handle, that should be
+ # used to later update/remove it
+ def add_timer(self, interval, cb, opaque):
+ timerID = self.nextTimerID + 1
+ self.nextTimerID = self.nextTimerID + 1
+
+ h = self.virEventLoopPureTimer(timerID, interval, cb, opaque)
+ self.timers.append(h)
+ self.interrupt()
+
+ self.debug("Add timer %d interval %d" % (timerID, interval))
+
+ return timerID
+
+ # Change the set of events to be monitored on the file handle
+ def update_handle(self, handleID, events):
+ h = self.get_handle_by_id(handleID)
+ if h:
+ h.set_events(events)
+ self.poll.unregister(h.get_fd())
+ self.poll.register(h.get_fd(), self.events_to_poll(events))
+ self.interrupt()
+
+ self.debug("Update handle %d fd %d events %d" % (handleID, h.get_fd(), events))
+
+ # Change the periodic frequency of the timer
+ def update_timer(self, timerID, interval):
+ for h in self.timers:
+ if h.get_id() == timerID:
+ h.set_interval(interval);
+ self.interrupt()
+
+ self.debug("Update timer %d interval %d" % (timerID, interval))
+ break
+
+ # Stop monitoring for events on the file handle
+ def remove_handle(self, handleID):
+ handles = []
+ for h in self.handles:
+ if h.get_id() == handleID:
+ self.poll.unregister(h.get_fd())
+ self.debug("Remove handle %d fd %d" % (handleID, h.get_fd()))
+ else:
+ handles.append(h)
+ self.handles = handles
+ self.interrupt()
+
+ # Stop firing the periodic timer
+ def remove_timer(self, timerID):
+ timers = []
+ for h in self.timers:
+ if h.get_id() != timerID:
+ timers.append(h)
+ self.debug("Remove timer %d" % timerID)
+ self.timers = timers
+ self.interrupt()
+
+ # Convert from libvirt event constants, to poll() events constants
+ def events_to_poll(self, events):
+ ret = 0
+ if events & libvirt.VIR_EVENT_HANDLE_READABLE:
+ ret |= select.POLLIN
+ if events & libvirt.VIR_EVENT_HANDLE_WRITABLE:
+ ret |= select.POLLOUT
+ if events & libvirt.VIR_EVENT_HANDLE_ERROR:
+ ret |= select.POLLERR;
+ if events & libvirt.VIR_EVENT_HANDLE_HANGUP:
+ ret |= select.POLLHUP;
+ return ret
+
+ # Convert from poll() event constants, to libvirt events constants
+ def events_from_poll(self, events):
+ ret = 0;
+ if events & select.POLLIN:
+ ret |= libvirt.VIR_EVENT_HANDLE_READABLE;
+ if events & select.POLLOUT:
+ ret |= libvirt.VIR_EVENT_HANDLE_WRITABLE;
+ if events & select.POLLNVAL:
+ ret |= libvirt.VIR_EVENT_HANDLE_ERROR;
+ if events & select.POLLERR:
+ ret |= libvirt.VIR_EVENT_HANDLE_ERROR;
+ if events & select.POLLHUP:
+ ret |= libvirt.VIR_EVENT_HANDLE_HANGUP;
+ return ret;
+
+
+###########################################################################
+# Now glue an instance of the general event loop into libvirt's event loop
+###########################################################################
+
+# This single global instance of the event loop wil be used for
+# monitoring libvirt events
+eventLoop = virEventLoopPure(debug=False)
+
+# This keeps track of what thread is running the event loop,
+# (if it is run in a background thread)
+eventLoopThread = None
+
+
+# These next set of 6 methods are the glue between the official
+# libvirt events API, and our particular impl of the event loop
+#
+# There is no reason why the 'virEventLoopPure' has to be used.
+# An application could easily may these 6 glue methods hook into
+# another event loop such as GLib's, or something like the python
+# Twisted event framework.
+
+def virEventAddHandleImpl(fd, events, cb, opaque):
+ global eventLoop
+ return eventLoop.add_handle(fd, events, cb, opaque)
+
+def virEventUpdateHandleImpl(handleID, events):
+ global eventLoop
+ return eventLoop.update_handle(handleID, events)
+
+def virEventRemoveHandleImpl(handleID):
+ global eventLoop
+ return eventLoop.remove_handle(handleID)
+
+def virEventAddTimerImpl(interval, cb, opaque):
+ global eventLoop
+ return eventLoop.add_timer(interval, cb, opaque)
+
+def virEventUpdateTimerImpl(timerID, interval):
+ global eventLoop
+ return eventLoop.update_timer(timerID, interval)
+
+def virEventRemoveTimerImpl(timerID):
+ global eventLoop
+ return eventLoop.remove_timer(timerID)
+
+# This tells libvirt what event loop implementation it
+# should use
+def virEventLoopPureRegister():
+ libvirt.virEventRegisterImpl(virEventAddHandleImpl,
+ virEventUpdateHandleImpl,
+ virEventRemoveHandleImpl,
+ virEventAddTimerImpl,
+ virEventUpdateTimerImpl,
+ virEventRemoveTimerImpl)
+
+# Directly run the event loop in the current thread
+def virEventLoopPureRun():
+ global eventLoop
+ eventLoop.run_loop()
+
+# Spawn a background thread to run the event loop
+def virEventLoopPureStart():
+ global eventLoopThread
+ virEventLoopPureRegister()
+ eventLoopThread = threading.Thread(target=virEventLoopPureRun, name="libvirtEventLoop")
+ eventLoopThread.setDaemon(True)
+ eventLoopThread.start()
+
+
+##########################################################################
+# Everything that now follows is a simple demo of domain lifecycle events
+##########################################################################
+def eventToString(event):
+ eventStrings = ( "Defined",
+ "Undefined",
+ "Started",
+ "Suspended",
+ "Resumed",
+ "Stopped" );
+ return eventStrings[event];
+
+def detailToString(event, detail):
+ eventStrings = (
+ ( "Added", "Updated" ),
+ ( "Removed" ),
+ ( "Booted", "Migrated", "Restored", "Snapshot" ),
+ ( "Paused", "Migrated", "IOError", "Watchdog" ),
+ ( "Unpaused", "Migrated"),
+ ( "Shutdown", "Destroyed", "Crashed", "Migrated", "Saved", "Failed", "Snapshot")
+ )
+ return eventStrings[event][detail]
+
+def readconsoleandsave (domainname):
+ global testdir
+ filename = testdir+"/guests/"+domainname+"/logs/"+domainname+"_console.log"
+ args = ["/usr/bin/virsh"]
+ args = args + [ "console", "%s" % domainname]
+ console_fd = open(filename, "a+")
+ console_fid = console_fd.fileno()
+ fds[domainname] = console_fd
+ (child, fd) = pty.fork()
+ if not child:
+ os.dup2(console_fid, 1)
+ #time.sleep(1)
+ os.execvp(args[0], args)
+ os._exit(1)
+ else:
+ debugprint("Forked %s %s" % (args[0], args))
+ # os.waitpid(-1, os.WNOHANG)
+
+ #print "child is : %d " % child
+ return child
+
+def myDomainEventCallback1 (conn, dom, event, detail, opaque):
+ #print "name: %s event: %s " % ( dom.name(), eventToString(event) )
+ global pids
+ if eventToString(event) == "Started":
+ debugprint("%s started, will call readconsoleandsave" % dom.name())
+ pids[dom.name()] = readconsoleandsave(dom.name())
+ #print "pids[%s] is %s " % (dom.name(), pids[dom.name()])
+ elif eventToString(event) == "Stopped":
+ if dom.name() in fds:
+ fds[dom.name()].close()
+ if dom.name() in pids:
+ os.kill(pids[dom.name()],signal.SIGKILL)
+
+def usage():
+ print "usage: "+os.path.basename(sys.argv[0])+" [uri]"
+ print " uri will default to qemu:///system"
+
+def debugprint(str):
+ global debugstr
+ if (debugstr):
+ print "%s" % str
+
+
+def main():
+ global testdir
+ #try:
+ # opts, args = getopt.getopt(sys.argv[1:], "h", ["help"] )
+ #except getopt.GetoptError, err:
+ # # print help information and exit:
+ # print str(err) # will print something like "option -a not recognized"
+ # usage()
+ # sys.exit(2)
+ parser = OptionParser(conflict_handler="resolve")
+ parser.add_option("-t" ,"--testdir", dest="testdir", help="Root dir for logs",
+ default="/mnt/tests/distribution/virt/install", metavar="PATH")
+ parser.add_option("-h" ,"--help", dest="help", help="Display help", metavar="HELP")
+ parser.add_option("-d" ,"--debug", dest="debug", help="Print debug",
+ action="store_false", default=False, metavar="DEBUG")
+ (options, args) = parser.parse_args()
+
+ if options.help:
+ usage()
+ sys.exit(0)
+
+ if options.debug:
+ debugstr = 1
+
+ if options.testdir:
+ testdir = options.testdir
+
+ if len(args) > 0:
+ uri = args[0]
+ else:
+ #uri = "qemu:///system"
+ uri = subprocess.Popen(["virsh", "uri"], stdout=subprocess.PIPE).communicate()[0].strip()
+
+ print "Using uri: " + uri
+
+ # Run a background thread with the event loop
+ virEventLoopPureStart()
+
+ vc = libvirt.open(uri)
+
+ # Close connection on exit (to test cleanup paths)
+ old_exitfunc = getattr(sys, 'exitfunc', None)
+ def exit():
+ print "Closing " + str(vc)
+ vc.close()
+ if (old_exitfunc): old_exitfunc()
+ sys.exitfunc = exit
+
+ #Add 2 callbacks to prove this works with more than just one
+ vc.domainEventRegister(myDomainEventCallback1,None)
+
+ # The rest of your app would go here normally, but for sake
+ # of demo we'll just go to sleep. The other option is to
+ # run the event loop in your main thread if your app is
+ # totally event based.
+ while 1:
+ time.sleep(1)
+
+
+if __name__ == "__main__":
+ pids = { }
+ fds = { }
+ path = ""
+ main()
diff --git a/distribution/virt/import/rhts_submit_virt_logs b/distribution/virt/import/rhts_submit_virt_logs
new file mode 100755
index 0000000..7aa567c
--- /dev/null
+++ b/distribution/virt/import/rhts_submit_virt_logs
@@ -0,0 +1,58 @@
+#!/bin/bash
+#
+# This script is a wrap-around rhts_submit_log script to submit virtualization
+# related logs. It submits logfiles based on the type of the hypervisor.
+#
+
+if isxen; then
+ #submit xen logs
+ for xenlog in $(find /var/log/xen/ -type f | tr '\n' "${IFS}")
+ do
+ rhts_submit_log -l ${xenlog}
+ done
+ for xenlog in $(find /var/log/xen/console -type f | tr '\n' "${IFS}")
+ do
+ rhts_submit_log -l ${xenlog}
+ done
+ # submit dmesg
+ dmesg > ./dmesg.txt
+ rhts_submit_log -l ./dmesg.txt
+
+else
+ for qemulog in $(find /var/log/libvirt/qemu -type f | tr '\n' "${IFS}")
+ do
+ rhts_submit_log -l ${qemulog}
+ done
+ # submit dmesg
+ dmesg > ./dmesg.txt
+ rhts_submit_log -l ./dmesg.txt
+
+fi
+
+#submit libvirtd debug log...
+if [ -e /tmp/libvirtd_debug.log ]; then
+rhts_submit_log -l /tmp/libvirtd_debug.log
+# clean the log for the next test
+echo "" > /tmp/libvirtd_debug.log
+fi
+
+if setvirttestargs; then
+
+ if [[ x"${VIRT_TESTARGS}" != "x" ]]; then
+ OLDIFS=${IFS}
+ IFS="|"
+ for guestname in $VIRT_TESTARGS
+ do
+ if [ -d $(pwd)/${guestname}/logs ]; then
+ unset IFS
+ for file in $(ls $(pwd)/${guestname}/logs)
+ do
+ rhts_submit_log -l $(pwd)/${guestname}/logs/${file}
+ done
+ IFS="|"
+ fi
+ done
+ IFS=${OLDIFS}
+ fi
+fi
+
diff --git a/distribution/virt/import/rhts_virt_funcs.sh b/distribution/virt/import/rhts_virt_funcs.sh
new file mode 100755
index 0000000..bfbed1f
--- /dev/null
+++ b/distribution/virt/import/rhts_virt_funcs.sh
@@ -0,0 +1,166 @@
+# This file will contain the general functions that might be useful to various
+# virtualization testings. The file will sit in /usr/local/bin/ directory and will be
+# intended to be sourced in by the tests utilizing this.
+
+# function isxen:
+# return 0 if we're running in a xen hypervisor , return 1 if not
+function isxen {
+ if uname -r | grep -q xen; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+# function iskvm:
+# return 0 if we're running in a kvm hypervisor , return 1 if not
+function iskvm {
+ if lsmod | grep -q kvm; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+# function isparavirt:
+# return 0 if the $guest is paravirt
+function isparavirt {
+
+ if [[ $# != 1 ]]; then
+ echo "Need a guest name as an argument"
+ return 1;
+ fi
+
+ if virsh dumpxml $1 | grep -q '>hvm<'; then
+ return 1
+ else
+ return 0
+ fi
+
+}
+
+# function ishvm
+# return 0 if $guest is hvm, 1 if it's not.
+function ishvm {
+
+ if [[ $# != 1 ]]; then
+ echo "Need a guest name as an argument"
+ return 1;
+ fi
+
+ if virsh dumpxml $1 | grep -q '>hvm<'; then
+ return 0
+ else
+ return 1
+ fi
+
+}
+# function setvirttestargs :
+# most virt testing scripts need to have guestnames to work with. This can be
+# provided either at the workflow, or it can be determined during the testtime.
+# If it's the latter, the test will run on each and every guest in order. The
+# guest arguments will be separated with pipe sign , |
+function setvirttestargs {
+
+ if [[ -z ${VIRT_TESTARGS} ]]; then
+ for dir in /mnt/tests/distribution/virt/install/guests/* ; do
+ guest_name=$(basename $dir)
+ if [[ -z ${VIRT_TESTARGS} ]]; then
+ VIRT_TESTARGS="${guest_name}"
+ else
+ VIRT_TESTARGS="${VIRT_TESTARGS}|${guest_name}"
+ fi
+ done
+ fi
+
+ echo ${VIRT_TESTARGS}
+ export VIRT_TESTARGS="${VIRT_TESTARGS}"
+ return 0
+
+
+}
+
+# function submitvirtlogs
+# submits the logs to rhts based on what hypervisor is running
+function submitvirtlogs {
+
+ if iskvm; then
+ for kvmlog in $(find /var/log/libvirt/qemu/ -type f | tr '\n' "${IFS}")
+ do
+ rhts_submit_log -l ${kvmlog}
+ done
+ else
+ for xenlog in $(find /var/log/xen/ -type f | tr '\n' "${IFS}")
+ do
+ rhts_submit_log -l ${xenlog}
+ done
+ for dumps in $(find /var/lib/xen/dump -type f | tr '\n' "${IFS}")
+ do
+ thefile=$(basename $dumps)
+ if ! scp.exp -u netdump -p netdump -h $DUMPSERVER -t 3600 -f ${thefile} -F /var/crash/${JOBID}_${RECIPEID}_${thefile}; then
+ echo "problem with scp-ing $thefile "
+ else
+ echo "$thefile has been loaded up to netdump server, please investigate"
+ fi
+ done
+ fi
+
+ #submit libvirtd debug log...
+ if [ -e /tmp/libvirtd_debug.log ]; then
+ rhts_submit_log -l /tmp/libvirtd_debug.log
+ # clean the log for the next test
+ echo "" > /tmp/libvirtd_debug.log
+ fi
+
+ if setvirttestargs; then
+
+ if [[ x"${VIRT_TESTARGS}" != "x" ]]; then
+ OLDIFS=${IFS}
+ IFS="|"
+ for guestname in $VIRT_TESTARGS
+ do
+ if [ -d $(pwd)/guests/${guestname}/logs ]; then
+ unset IFS
+ for file in $(ls $(pwd)/guests/${guestname}/logs)
+ do
+ rhts_submit_log -l $(pwd)/guests/${guestname}/logs/${file}
+ done
+ IFS="|"
+ fi
+ done
+ IFS=${OLDIFS}
+ fi
+
+ fi
+ #submit dmesg
+ dmesg > ./dmesg.txt
+ rhts_submit_log -l ./dmesg.txt
+ # Always submit the audit.log
+ rhts_submit_log -l /var/log/audit/audit.log
+
+}
+
+function TurnOnLibvirtdLogging()
+{
+ if alias | grep cp=; then
+ unalias cp
+ fi
+ cp -f /etc/libvirt/libvirtd.conf /etc/libvirt/libvirtd.conf.orig
+ echo 'log_filters="1:libvirt 1:util 1:qemu"' >> /etc/libvirt/libvirtd.conf
+ echo 'log_outputs="1:file:/tmp/libvirtd_debug.log"' >> /etc/libvirt/libvirtd.conf
+
+ if ! service libvirtd restart; then
+ echo "There was a problem restarting libvirtd!!!"
+ fi
+}
+
+
+function TurnOffLibvirtdLogging()
+{
+
+ perl -pi.bak -e 's/^log_.*$//g' /etc/libvirt/libvirtd.conf
+
+ if ! service libvirtd restart; then
+ echo "There was a problem restarting libvirtd!!!"
+ fi
+}
diff --git a/distribution/virt/import/runtest.sh b/distribution/virt/import/runtest.sh
new file mode 100755
index 0000000..6872fd1
--- /dev/null
+++ b/distribution/virt/import/runtest.sh
@@ -0,0 +1,1112 @@
+#!/bin/bash
+
+# Source the common test script helpers
+. /usr/bin/rhts_environment.sh
+. /usr/local/bin/rhts_virt_funcs.sh
+
+result=PASS
+value=0
+kvm_num=0
+home_basedir=0
+# control where to log debug messages to:
+# devnull = 1 : log to /dev/null
+# devnull = 0 : log to file specified in ${DEBUGLOG}
+devnull=0
+mkdir -p /home/virtimages/VirtualMachines
+
+# Create debug log
+DEBUGLOG=`mktemp -p /mnt/testarea -t virtinstall.XXXXXX`
+
+# locking to avoid races
+lck=$OUTPUTDIR/$(basename $0).lck
+
+function TurnOnLibvirtdLogging()
+{
+ if alias | grep cp=; then
+ unalias cp
+ fi
+ cp -f /etc/libvirt/libvirtd.conf /etc/libvirt/libvirtd.conf.orig
+ echo 'log_filters="1:libvirt 1:util 1:qemu"' >> /etc/libvirt/libvirtd.conf
+ echo 'log_outputs="1:file:/tmp/libvirtd_debug.log"' >> /etc/libvirt/libvirtd.conf
+
+ if ! service libvirtd restart; then
+ echo "There was a problem restarting libvirtd!!!"
+ fi
+}
+
+
+function TurnOffLibvirtdLogging()
+{
+
+ perl -pi.bak -e 's/^log_.*$//g' /etc/libvirt/libvirtd.conf
+
+ if ! service libvirtd restart; then
+ echo "There was a problem restarting libvirtd!!!"
+ fi
+}
+
+
+
+# Log a message to the ${DEBUGLOG} or to /dev/null
+function DeBug ()
+{
+ local msg="$1"
+ local timestamp=$(date +%Y%m%d-%H%M%S)
+ if [ "$devnull" = "0" ]; then
+ echo -n "${timestamp}: " >>$DEBUGLOG 2>&1
+ echo "${msg}" >>$DEBUGLOG 2>&1
+ else
+ echo "${msg}" >/dev/null 2>&1
+ fi
+}
+
+function SelectKernel ()
+{
+ DeBug "Enter SelectKernel"
+ VR=$1
+ EXTRA=$2
+ DeBug "VR=$VR EXTRA=$EXTRA"
+
+ # If not version or Extra selected then choose the latest installed version
+ if [ -z "$EXTRA" -a -z "$VR" ]; then
+ DeBug "ERROR: missing args"
+ return 1
+ fi
+
+ # Workaround for broken RT kernel spec file, part 1
+ if [ "$EXTRA" = "rt" ]; then
+ DeBug "EXTRA=$EXTRA"
+ EXTRA=""
+ fi
+
+ # Workaround for broken RT kernel spec file, part 2
+ if [ "$EXTRA" = "rt-vanilla" ]; then
+ DeBug "EXTRA=$EXTRA"
+ EXTRA="vanilla"
+ fi
+
+ # Workaround for broken RT kernel spec file, part 1
+ if [ "$EXTRA" = "up" ]; then
+ DeBug "EXTRA=$EXTRA"
+ EXTRA=""
+ fi
+
+ echo "***** Attempting to switch boot kernel to ($VR$EXTRA) *****"
+ DeBug "Attempting to switch boot kernel to ($VR$EXTRA)"
+
+ grub_file=/boot/grub/grub.conf
+
+ if [ -f $grub_file ]; then
+ DeBug "Using: $grub_file"
+ COUNT=0
+ DEFAULT=undefined
+ for i in $(grep '^title' $grub_file | sed 's/.*(\(.*\)).*/\1/'); do
+ DeBug "COUNT=$COUNT VR=$VR EXTRA=$EXTRA i=$i"
+ if echo $i | egrep -e "${VR}.*${EXTRA}" ; then
+ DEFAULT=$COUNT;
+ fi
+ COUNT=$(expr $COUNT + 1)
+ done
+ if [[ x"${DEFAULT}" != x"undefined" ]]; then
+ DeBug "DEFAULT=$DEFAULT"
+ /bin/ed -s $grub_file <<EOF
+/default/
+d
+i
+default=$DEFAULT
+.
+w
+q
+EOF
+ fi
+ DeBug "$grub_file"
+ cat $grub_file | tee -a $DEBUGLOG
+ fi
+
+ elilo_file=/boot/efi/efi/redhat/elilo.conf
+
+ if [ -f $elilo_file ]; then
+ DeBug "Using: $elilo_file"
+ DEFAULT=$(grep -A 2 "image=vmlinuz-$VR$EXTRA$" $elilo_file | awk -F= '/label=/ {print $2}')
+ DeBug "DEFAULT=$DEFAULT"
+ if [ -n "$DEFAULT" ]; then
+ DeBug "DEFAULT=$DEFAULT"
+ /bin/ed -s $elilo_file <<EOF
+/default/
+d
+i
+default=$DEFAULT
+.
+w
+q
+EOF
+ fi
+ DeBug "$elilo_file"
+ cat $elilo_file | tee -a $DEBUGLOG
+ fi
+
+ yaboot_file=/boot/etc/yaboot.conf
+
+ if [ -f $yaboot_file ] ; then
+ DeBug "Using: $yaboot_file"
+ grep vmlinuz $yaboot_file
+ if [ $? -eq 0 ] ; then
+ VM=z
+ else
+ VM=x
+ fi
+ DEFAULT=$(grep -A 1 "image=/vmlinu$VM-$VR$EXTRA" $yaboot_file | awk -F= '/label=/ {print $2}')
+ DeBug "DEFAULT=$DEFAULT"
+ if [ -n "$DEFAULT" ] ; then
+ sed -i 's/label=linux/label=orig-linux/g' $yaboot_file
+ sed -i 's/label='$DEFAULT'/label=linux/g' $yaboot_file
+ DeBug "DEFAULT=$DEFAULT"
+ grep -q label=linux $yaboot_file
+ if [ $? -ne 0 ] ; then
+ sed -i 's/label=orig-linux/label=linux/g' $yaboot_file
+ DeBug "Reverted back to original kernel"
+ fi
+ fi
+ DeBug "$yaboot_file"
+ cat $yaboot_file | tee -a $DEBUGLOG
+ fi
+
+ zipl_file=/etc/zipl.conf
+
+ if [ -f $zipl_file ] ; then
+ DeBug "Using: $zipl_file"
+ DEFAULT=$(grep "image=/boot/vmlinuz-$VR$EXTRA" $zipl_file | awk -Fvmlinuz- '/vmlinuz/ {printf "%.15s\n",$2}')
+ DeBug "DEFAULT=$DEFAULT"
+ if [ -n "$DEFAULT" ] ; then
+ DeBug "$VR$EXTRA"
+ tag=$(grep "\[$DEFAULT\]" $zipl_file)
+ DeBug "tag=$tag"
+ if [ -z "$tag" ] ; then
+ DeBug "Setting it back to default"
+ DEFAULT=linux
+ fi
+ /bin/ed -s $zipl_file <<EOF
+/default=/
+d
+i
+default=$DEFAULT
+.
+w
+q
+EOF
+ zipl
+ fi
+ DeBug "$zipl_file"
+ cat $zipl_file | tee -a $DEBUGLOG
+ fi
+
+ sync
+ sleep 5
+ DeBug "Exit SelectKernel"
+ return 0
+}
+
+
+
+
+function SubmitLog ()
+{
+ LOG=$1
+ rhts_submit_log -l $LOG
+}
+
+function SubmitVirtLogs ()
+{
+ # submit the relevant logfiles
+ if [[ ${kvm_num} > 0 ]]; then
+ for kvmlog in $(find /var/log/libvirt/qemu/ -type f)
+ do
+ rhts_submit_log -l ${kvmlog}
+ done
+ else
+ for xenlog in $(find /var/log/xen/ -type f)
+ do
+ rhts_submit_log -l ${xenlog}
+ done
+ for dumps in $(find /var/lib/xen/dump -type f)
+ do
+ rhts_submit_log -l ${dumps}
+ done
+ fi
+
+ rhts_submit_log -l ${DEBUGLOG}
+
+ #submit dmesg
+ dmesg > ./dmesg.txt
+ rhts_submit_log -l ./dmesg.txt
+ # Always submit the audit.log
+ rhts_submit_log -l /var/log/audit/audit.log
+ #submit virt-install log file
+ if [ -e ${HOME}/.virtinst/virt-install.log ]; then
+ rhts_submit_log -l ${HOME}/.virtinst/virt-install.log
+ fi
+ #submit libvirtd debug log...
+ if [ -e /tmp/libvirtd_debug.log ]; then
+ rhts_submit_log -l /tmp/libvirtd_debug.log
+ # clean the log for the next test
+ echo "" > /tmp/libvirtd_debug.log
+ fi
+
+}
+
+function setuprhel5consoles()
+{
+ local RESULT="PASS"
+ local FAIL=0
+
+ if ! cp zrhel5_write_consolelogs.initd /etc/init.d/zrhel5_write_consolelogs; then
+ echo "Problem copying zrhel5_write_consolelogs.initd to initd dir"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ if ! cp zrhel5_write_consolelogs.py /usr/local/bin/zrhel5_write_consolelogs; then
+ echo "Problem copying zrhel5_write_consolelogs.py to /usr/local/bin"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+ if ! chmod 755 /usr/local/bin/zrhel5_write_consolelogs; then
+ echo "Problem with chmoding zrhel5_write_consolelogs"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ if ! chkconfig --add zrhel5_write_consolelogs; then
+ echo "problem with chkconfig --add zrhel5_write_consolelogs"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ if ! chkconfig zrhel5_write_consolelogs on; then
+ echo "problem with chkconfig zrhel5_write_consolelogs on"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ iptables -I INPUT -p tcp --dport 8000 -j ACCEPT
+ iptables -I INPUT -p udp --dport 8000 -j ACCEPT
+ sleep 1
+ if ! service iptables save; then
+ echo "Problem with service iptables save"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ if ! cp -f iptables_after_libvirtd.initd /etc/init.d/iptables_after_libvirtd; then
+ echo "Problem with copying iptables_after_libvirtd.initd"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ if ! chkconfig --add iptables_after_libvirtd; then
+ echo "problem with adding iptables_after_libvirtd"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ if ! chkconfig iptables_after_libvirtd on; then
+ echo "problem with chkconfig iptables_after_libvirtd on"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ if ! service iptables_after_libvirtd start; then
+ echo "problem with service iptables_after_libvirtd start"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ echo "Status of iptables: "
+ service iptables status
+ service zrhel5_write_consolelogs start
+ sleep 2
+ echo "Status of rhel5_write_consoles:"
+ service zrhel5_write_consolelogs status
+ echo "Initial log output:"
+ cat /nohup.out
+ if [[ ${FAIL} > 0 ]]; then
+ report_result ${TEST}_zrhel5_write_consolelogs WARN $FAIL
+ return -1
+ else
+ report_result ${TEST}_zrhel5_write_consolelogs PASS 0
+ return 0
+ fi
+
+}
+
+function setupconsolelogs()
+{
+ local RESULT="PASS"
+ local FAIL=0
+ if ! gcc -g -Wall logguestconsoles.c -o logguestconsoles -lssl -lcrypto -lcurl $(xmlrpc-c-config client --libs) $(pkg-config libvirt --libs) $(xml2-config --cflags) $(xml2-config --libs); then
+ echo "Problem with compiling logguestconsoles.c file"
+ echo "Guest console logs won't be available"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ if ! cp logguestconsoles /usr/local/bin; then
+ echo "Problem with copying logguestconsoles to /usr/local/bin"
+ echo "Guest console logs won't be available"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ if ! cp logguestconsoles.initd /etc/init.d/logguestconsoles; then
+ echo "problem with copying init script"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ if ! chkconfig --add logguestconsoles; then
+ echo "problem with chkconfig --add logguestconsoles"
+ RESULT="FAIL"
+ let "FAIL=${FAIL}+1"
+ fi
+
+ if [[ ${FAIL} > 0 ]]; then
+ report_result ${TEST}_consolelogsetup WARN $FAIL
+ return 1
+ else
+ report_result ${TEST}_consolelogsetup PASS 0
+ return 0
+ fi
+}
+
+function setuprhel5_xmlrpcc()
+{
+ local SRCRPM="xmlrpc-c.el5.src.rpm"
+ local FAIL=0
+ local OUTPUTLOG="./setuprhel5_xmlrpcc.log"
+
+ if ! rpm -ivh ${LOOKASIDE}/${SRCRPM}; then
+ echo "problem with rpm -ivh ${LOOKASIDE}/${SRCRPM}"
+ FAIL=$(expr ${FAIL} + 1)
+ fi
+
+ exec 5>&1 6>&2
+ exec >> ${OUTPUTLOG} 2>&1
+
+ if ! rpmbuild -ba /usr/src/redhat/SPECS/xmlrpc-c.spec; then
+ echo "problem with rpmbuild -ba /usr/src/redhat/SPECS/xmlrpc-c.spec"
+ FAIL=$(expr ${FAIL} + 1)
+ fi
+
+ if ! rpm -Uvh /usr/src/redhat/RPMS/$(arch)/xmlrpc-c*.rpm; then
+ echo "problem with rpm -Uvh /usr/src/redhat/RPMS/$(arch)/xmlrpc-c*.rpm"
+ FAIL=$(expr ${FAIL} + 1)
+ fi
+
+ exec 1>&5 5>&-
+ exec 2>&6 6>&-
+
+ if [[ ${FAIL} > 0 ]]; then
+ report_result ${TESTNAME}_setuprhel5_xmlrpcc FAIL 1
+ else
+ report_result ${TESTNAME}_setuprhel5_xmlrpcc PASS 0
+ fi
+ rhts_submit_log ${OUTPUTLOG}
+}
+
+
+
+function rename_current_ifcfg_config()
+{
+ local cfg_file=/etc/sysconfig/network-scripts/ifcfg-$netdev
+ local ret=0
+ if [ -e "$cfg_file" ]; then
+ echo "Found $cfg_file, trying to rename"
+ mv -vf $cfg_file /etc/sysconfig/network-scripts/Xifcfg-${netdev}.orig
+ ret=$?
+ else
+ # Bug 845225 - interface name does not match ifcfg- config file name
+ echo "Could not find $cfg_file, searching for one based on MAC"
+ local ifcfg_files=`grep -l "${mac}" /etc/sysconfig/network-scripts/ifcfg-*`
+ echo "Matching config files: $ifcfg_files"
+ for cfg_file in $ifcfg_files; do
+ bn=`basename $cfg_file`
+ mv -vf $cfg_file /etc/sysconfig/network-scripts/X$bn.orig
+ if [ $? -ne 0 ]; then
+ ret=1
+ fi
+ done
+ fi
+ return $ret
+}
+
+# Bug 901542 - qemu doesn't depend on seabios-bin, resulting in error: "qemu: PC system firmware (pflash) must be a multiple of 0x1000"
+function workaround_bug901542()
+{
+ yum list installed seabios-bin
+ local ret2=$?
+ if [ $ret2 -ne 0 ]; then
+ echo "Bug 901542, seabios/seabios-bin is not installed, trying to install.."
+ yum -y install seabios seabios-bin
+ report_result bug901542 FAIL 1
+ fi
+}
+
+# Bug 958860 - Could not access KVM kernel module: Permission denied
+function workaround_bug958860()
+{
+ local rights=`stat --format=%a /dev/kvm | tail -c 4`
+ if [ "${rights:0:1}" -lt "6" -o "${rights:1:1}" -lt "6" -o "${rights:2:1}" -lt "6" ]; then
+ echo "Bug 958860 - Could not access KVM kernel module, chmod-ing to 0666"
+ chmod 666 /dev/kvm
+ report_result bug958860 FAIL 1
+ fi
+}
+
+# Bug 957897 - service network start won't start network
+function workaround_bug957897()
+{
+ if [ ! -e /etc/sysconfig/network ]; then
+ echo "Bug 957897 - service network start won't start network, touching /etc/sysconfig/network"
+ touch /etc/sysconfig/network
+ restorecon /etc/sysconfig/network
+ report_result bug957897 FAIL 1
+ fi
+}
+
+function ConfirmDefaultNetDevice ()
+{
+ # RHEL5 brings up all the network devices and sets
+ # the last network device to come up as the default.
+ # ConfirmDefaultNetDevice:
+ # Confirms the network installation device
+ # is set as the default network device.
+ # This is required for proper network bridging on guests.
+
+ # Run for RHEL5 only
+ local rhel5_ver="2.6.18"
+ local kernel_ver=`rpm -q --queryformat '%{version}\n' -qf /boot/config-$(uname -r)`
+
+ if [ "$rhel5_ver" == "$kernel_ver" ]; then
+ echo ""
+ echo "***** RHEL5: Verifying the the network installation device is set as the default network device *****"
+
+ if [ ! -e /root/anaconda-ks.cfg ]; then
+ echo ""
+ echo "***** WARN: /root/anaconda-ks.cfg file is missing *****"
+ echo "***** WARN: Unable to confirm system network installation device *****"
+ echo ""
+ report_result ${TEST}_ConfirmDefaultNetDevice WARN
+ else
+ echo ""
+ echo "***** Confirming system network installation device *****"
+ echo "***** Checking /root/anaconda-ks.cfg *****"
+
+ grep "network --device" /root/anaconda-ks.cfg
+ if [ "$?" -ne "0" ]; then
+ echo "***** WARN: Unable to confirm system network installation device *****"
+ report_result ${TEST}_ConfirmDefaultNetDevice WARN
+ else
+ # Get system network installation device
+ local installdev=$(grep -oP "(?<=--device )[^ ]+" /root/anaconda-ks.cfg)
+ if [ "$?" -eq "0" ]; then
+ echo "***** System network installation device = $installdev *****"
+ echo ""
+ echo "***** Confirming system default network device *****"
+ echo "***** Checking route *****"
+ # Get systems current default network device
+ local defaultdev=$(route | grep default | awk '{print $NF}')
+ if [ "$?" -eq "0" ]; then
+ echo "***** Default network device = $defaultdev *****"
+ # Confirm install and default network device are the same device
+ if [ "$installdev" != "$defaultdev" ]; then
+ echo "***** The install and default network devices are not the same device *****"
+ echo "***** As RHEL5 sets the last device that comes up to the default, *****"
+ echo "***** its likely this system has multiple nics up on the same subnet *****"
+ echo ""
+ echo "***** Setting $installdev to default network device *****"
+
+ # This trick will make the $installdev the last network device to come up
+ # Thus, resetting the RHEL5 default network device
+ ifdown "$installdev"
+ sleep 3
+ ifup "$installdev"
+
+ # One last check
+ if [ "$installdev" -ne "$defaultdev" ]; then
+ echo "***** WARN: Unable to set $installdev to default network device *****"
+ report_result ${TEST}_ConfirmDefaultNetDevice WARN
+ else
+ echo "***** $installdev successfully set to default network device *****"
+ echo ""
+ fi
+ else
+ echo "***** System installation and default default network device = $defaultdev *****"
+ echo ""
+ fi
+ else
+ echo "***** WARN: Unable to confirm default network device *****"
+ report_result ${TEST}_ConfirmDefaultNetDevice WARN
+ fi
+ fi
+ fi
+ fi
+ fi
+}
+
+#
+# ---------- Start Test -------------
+#
+
+# workaround RHEL7 issues
+rpm -qa initscripts | grep "\.el7"
+if [ $? -eq 0 ]; then
+ workaround_bug901542
+ workaround_bug958860
+ workaround_bug957897
+fi
+
+chmod 755 *.initd
+
+# turn on libvirtd debugging log.
+TurnOnLibvirtdLogging
+
+## normally this test will run in selinux enforcing mode but sometimes we may
+# want to run it in permissive mode to de bug selinux issues:
+if [[ -n "${PERMISSIVE_MODE}" ]]; then
+ setenforce Permissive
+ if [[ $? != 0 ]] ; then
+ echo "Problem with setting enforcing to permissive"
+ report_result ${TEST}/selinux_permissive FAIL 1
+ exit 0
+ fi
+ perl -pi.bak -e 's/SELINUX=enforcing/SELINUX=permissive/g' /etc/selinux/config
+fi
+
+# Add in a variable to workaround virt-install and selinux brokeness
+# See BZ [Bug 475786] [RHEL5.3] SELinux AVC Denied: while trying to write to
+# /.virtinst/virt-install.log
+if [ -z $HOME ] ; then
+ export HOME=/root
+fi
+
+# starting with rhel 5.4 we'll have both xen and kvm hypervisors shipped with the tree.
+# This test will be used to install guests under both hypervisors hence it needs to
+# identify what it is doing. @virtualization group pulls in packages/kernels for both
+# hypervisors and by default kernel-xen is selected which means any guests installed will
+# be xen guests. To allow testers to install kvm guestsi, --kvm argument in guest_args
+# will be used. It will NOT be allowed to specify both xen or kvm guests. All guests must
+# be of one hypervisor. The flow will be:
+#
+# Check if this is rhel6 installation or not. If it is, then just assume all kvm guests
+# with or without --kvm indicators.
+#
+# If this is rhel5 then, figure what type of guests there are
+# - Make sure that ALL guests are of the same type
+# - if kvm:
+# check the running kernel
+# if baremetal, move on
+# if xen, switch to baremetal kernel and rhts-reboot
+# - if xen:
+# check the running kernel
+# if not kernel-xen error out (it should be the default)
+# if xen, make changes to xend files and rhts-reboot
+# - install guests.
+if ver=$(rpm -q --qf '%{version}\n' --whatprovides redhat-release); then
+ if [[ ${ver} == 6 || ${ver} > 6 ]]; then
+ kvm_num=1
+ else
+ # ensure that they all are either xen or kvm
+ if ! kvm_num=$(./get_guest_info.py --kvm-num) ; then
+ echo "can't mix up kvm and non-kvm guests. They all have to be kvm or nonkvm"
+ report_result ${TEST}_wrongguestsetup FAIL 1
+ submitvirtlogs
+ exit 1
+ fi
+ fi
+fi
+
+# if this will be kvm guests' install make sure that correct kernel is running.
+if [[ ${kvm_num} > 0 ]]; then
+ if uname -r | grep xen ; then
+ xenkern=$(uname -r)
+ basekern=${xenkern%"xen"}
+ # if for whatever reason base kernel isn't installed, install it..
+ if ! rpm -q kernel-${basekern}; then
+ yum -y install kernel-${basekern} kvm
+ fi
+ # make sure that yum installed it..
+ if ! rpm -q kvm; then
+ echo "Can't find/install kvm"
+ report_result ${TEST}_nokvm FAIL 1
+ exit 1
+ fi
+ echo "this test seems to be for kvm guests, booting into vanilla kernel"
+ SelectKernel ${basekern}
+ echo "Rebooting into base kernel since this is kvm guest"
+ report_result rhts-reboot PASS $REBOOTCOUNT
+ rhts-reboot
+ fi
+
+ if [[ ${ver} == 6 || ${ver} > 6 ]]; then
+ # for rhel6 & above we need to set up bridging and get network manager out
+ # of the way...
+ if [[ ${REBOOTCOUNT} == 0 ]]; then
+
+ ## we need to configure/add bridge to establish bridged networking for
+ ## kvm guests
+ def_line=$(ip route list | grep ^default)
+ defnum=$(perl -e 'for ($i=0; $i<$#ARGV; $i++ ) { if ($ARGV[$i] eq "dev" ) { $_ = $ARGV[ $i + 1 ]; if ( /^(\w*)(\d+)/ ) { print "$_ $2"; } } }' ${def_line} )
+ actnum=$(echo ${defnum} | awk '{print $2}')
+ netdev=$(echo ${defnum} | awk '{print $1}')
+ vifnum=${vifnum:-$actnum}
+ if [ -z ${vifnum} ]; then
+ echo "Can't get the interface number "
+ report_result ${TEST}_networksetup FAIL 1
+ exit 1
+ fi
+ brdev="br${vifnum}"
+ pdev="p${netdev}"
+ mac=`ip link show ${netdev} | grep 'link\/ether' | sed -e 's/.*ether \(..:..:..:..:..:..\).*/\1/'`
+ if [ -z ${mac} ]; then
+ echo "Can't find the mac address"
+ report_result ${TEST}_networksetupnomac FAIL 1
+ exit 1
+ fi
+ echo "brdev: ${brdev} netdev: ${netdev} pdev: ${pdev} mac: ${mac} "
+
+ rename_current_ifcfg_config
+ if [[ $? != 0 ]]; then
+ echo "Problem copying network config scripts"
+ report_result ${TEST}_networksetup FAIL 1
+ exit 1
+ fi
+ cat <<EOF > /etc/sysconfig/network-scripts/ifcfg-$netdev
+DEVICE=$netdev
+ONBOOT=yes
+BRIDGE=$brdev
+HWADDR=$mac
+EOF
+
+ cat <<EOF > /etc/sysconfig/network-scripts/ifcfg-$brdev
+DEVICE=$brdev
+BOOTPROTO=dhcp
+ONBOOT=yes
+TYPE=Bridge
+DELAY=0
+EOF
+
+ service NetworkManager stop
+ chkconfig NetworkManager off
+ chkconfig network on
+ service network restart
+ if [[ $? != 0 ]]; then
+ echo "problem restarting network"
+
+ rpm -qa initscripts | grep "\.el7"
+ if [ $? -eq 0 ]; then
+ echo "This is known RHEL7 issue, proceeding anyway.."
+ echo "Bug 886090 - ifcfg- config contains ONBOOT=yes for interface with no link"
+ report_result ${TEST}_networksetup FAIL 1
+ else
+ report_result ${TEST}_networksetup FAIL 1
+ exit 1
+ fi
+ else
+ echo "configured a bridge: $netdev "
+ fi
+
+ echo "Rebooting after configuring the bridge"
+ report_result rhts-reboot PASS $REBOOTCOUNT
+ rhts-reboot
+
+ # when it's already set up and rebooted, then get the bridge to use to
+ # pass it on to virt-install
+ else
+ def_line=$(ip route list | grep ^default)
+ defnum=$(perl -e 'for ($i=0; $i<$#ARGV; $i++ ) { if ($ARGV[$i] eq "dev" ) { $_ = $ARGV[ $i + 1 ]; if ( /^(\w*)(\d+)/ ) { print "$_ $2"; } } }' ${def_line} )
+ actnum=$(echo ${defnum} | awk '{print $2}')
+ netdev=$(echo ${defnum} | awk '{print $1}')
+ vifnum=${vifnum:-$actnum}
+ if [ -z ${vifnum} ]; then
+ echo "Can't get the interface number "
+ report_result ${TEST}_networksetup FAIL 1
+ exit 1
+ fi
+ brdev="br${vifnum}"
+ fi
+
+ else
+
+ ## we need to configure/add bridge to establish bridged networking for
+ ## kvm guests
+
+ # For RHEL5 before we establish a network bridge device
+ # Lets confirm the default network device is correct
+ ConfirmDefaultNetDevice
+
+ def_line=$(ip route list | grep ^default)
+ defnum=$(perl -e 'for ($i=0; $i<$#ARGV; $i++ ) { if ($ARGV[$i] eq "dev" ) { $_ = $ARGV[ $i + 1 ]; if ( /^(\w*)(\d+)/ ) { print "$_ $2"; } } }' ${def_line} )
+ actnum=$(echo ${defnum} | awk '{print $2}')
+ netdev=$(echo ${defnum} | awk '{print $1}')
+ vifnum=${vifnum:-$actnum}
+ if [ -z ${vifnum} ]; then
+ echo "Can't get the interface number "
+ report_result ${TEST}_networksetup FAIL 1
+ exit 1
+ fi
+ brdev="br${vifnum}"
+ pdev="p${netdev}"
+ mac=`ip link show ${netdev} | grep 'link\/ether' | sed -e 's/.*ether \(..:..:..:..:..:..\).*/\1/'`
+ if [ -z ${mac} ]; then
+ echo "Can't find the mac address"
+ report_result ${TEST}_networksetupnomac FAIL 1
+ exit 1
+ fi
+ echo "brdev: ${brdev} netdev: ${netdev} pdev: ${pdev} mac: ${mac} "
+
+ rename_current_ifcfg_config
+ if [[ $? != 0 ]]; then
+ echo "Problem copying network config scripts"
+ report_result ${TEST}_networksetup FAIL 1
+ exit 1
+ fi
+ cat <<EOF > /etc/sysconfig/network-scripts/ifcfg-$netdev
+DEVICE=$netdev
+ONBOOT=yes
+BRIDGE=$brdev
+HWADDR=$mac
+EOF
+
+ cat <<EOF > /etc/sysconfig/network-scripts/ifcfg-$brdev
+DEVICE=$brdev
+BOOTPROTO=dhcp
+ONBOOT=yes
+TYPE=Bridge
+DELAY=0
+EOF
+
+
+ service NetworkManager stop
+ chkconfig NetworkManager off
+ chkconfig network on
+ service network restart
+ if [[ $? != 0 ]]; then
+ echo "problem restarting network"
+ report_result ${TEST}_networksetup FAIL 1
+ exit 1
+ else
+ echo "configured a bridge: $netdev "
+ fi
+
+ fi
+
+ # see BZ# 749611
+ if rpm -q kernel-xen; then
+ yum -y erase kernel-xen
+ fi
+
+else # this is a xen install
+
+ # turn on various logs in xend and restart xend
+ if [[ $REBOOTCOUNT == 0 ]]; then
+ perl -pi.bak -e 's/#*\(enable-dump\s+no\)/(enable-dump yes)/g' /etc/xen/xend-config.sxp
+ perl -pi.bak -e 's/^#*XENCONSOLED_LOG_HYPERVISOR=no/XENCONSOLED_LOG_HYPERVISOR=yes/g;s/^#*XENCONSOLED_LOG_GUESTS=no/XENCONSOLED_LOG_GUESTS=yes/g;s/^#*XENCONSOLED_LOG_DIR=.*$/XENCONSOLED_LOG_DIR=\/var\/log\/xen\/console/g' /etc/sysconfig/xend
+ echo "Reboot needed to enable xen logging"
+ report_result rhts-reboot PASS $REBOOTCOUNT
+ rhts-reboot
+ else
+ echo "Reboot was successful"
+
+ # For RHEL5 before we establish a network bridge device
+ # Lets confirm the default network device is correct
+ ConfirmDefaultNetDevice
+ fi
+
+ # are we running on a Xen kernel in domain 0 ?
+ # (only report if RHTS tried running us on an unsuitable host)
+ #
+ if [ -d /proc -a ! -f /proc/xen/privcmd ] ; then
+ DeBug "Don't think we are on a Dom0"
+ echo "Don't think we are on a Dom0"
+ report_result ${TEST} WARN 10
+ exit 0
+ fi
+fi
+
+## we need libvirt-devel, that might not be installed during installation time
+if ! rpm -q libvirt-devel; then
+ yum -y install libvirt-devel
+ if ! rpm -q libvirt-devel; then
+ echo "can't install libvirt-devel"
+ echo "guest console logs might not work"
+ report_result NO_libvirt-devel WARN 10
+ fi
+fi
+
+echo "***********************"
+echo "* SELinux Status *"
+echo "***********************"
+/usr/sbin/sestatus
+virtinst="virt-install"
+
+if [[ $REBOOTCOUNT > 1 ]]; then
+ echo "Looks like dom0 rebooted during a guest install. Check console logs"
+ report_result ${TEST}_dom0rebooted FAIL 99
+ submitvirtlogs
+ exit 0
+fi
+
+./get_guest_info.py > ./tmp.guests
+echo "Guests info:"
+cat ./tmp.guests
+
+# go thru the guests and set up console sniff/upload
+while read -r guest_recipeid guest_name guest_mac guest_args ; do
+ if ! mkdir -p $(pwd)/guests/${guest_name}/logs; then
+ report_result ${TEST}_cant_create_dirs FAIL 10
+ fi
+ guest_con_logfile="$(pwd)/guests/${guest_name}/logs/${guest_name}_console.log"
+ echo "$guest_con_logfile $guest_recipeid" >> /usr/local/etc/logguestconsoles.conf
+ if [ ! -e $guest_con_logfile ] ; then
+ touch $guest_con_logfile
+ fi
+ chmod a+rw $guest_con_logfile
+done < ./tmp.guests
+
+# setup consolelogging
+# logguestconsoles create files itself, so if qemu has issues with
+# that in future (permissions/selinux), this needs to be started
+# only after qemu creates those files
+#
+# on some rhel5 releases xmlrpc-c package doesn't exist, install it here
+if [ ${ver:0:1} -lt 6 ]; then
+ minor_ver=$(sed 's/.* release 5\.\([0-9]*\) .*/\1/' /etc/redhat-release)
+ if [[ ${minor_ver} < 6 && ${minor_ver} > 3 ]]; then
+ setuprhel5_xmlrpcc
+ fi
+fi
+
+if setupconsolelogs; then
+ service logguestconsoles start
+ sleep 2
+ echo "Status of logguestconsoles:"
+ service logguestconsoles status
+ echo "Initial log output:"
+ cat /var/log/logguestconsoles.*
+fi
+
+while read -r guest_recipeid guest_name guest_mac guest_args ; do
+ DeBug "guest is :
+ guest_recipeid=$guest_recipeid
+ guest_name=$guest_name
+ guest_mac=$guest_mac
+ guest_args=$guest_args"
+ if [ -z "$guest_name" ] ; then
+ echo "get_guest_info.py did not return a guest name"
+ report_result ${TEST}_no_guestname FAIL 10
+ exit 1
+ fi
+ if ! mkdir -p $(pwd)/guests/${guest_name}/logs || ! mkdir -p $(pwd)/guests/${guest_name}/iso; then
+ echo "Problem creating $(pwd)/guests/${guest_name}/{logs,iso} dirs"
+ report_result ${TEST}_cant_create_dirs FAIL 10
+ exit 1
+ fi
+ if ! chmod -R 777 $(pwd)/guests/${guest_name}; then
+ echo "problem with chmod -R 777 $(pwd)/guests/${guest_name}"
+ report_result ${TEST}_chmod_issue FAIL 10
+ exit 1
+ fi
+
+ if [ -n "$*" ]; then
+ guest_args=$*
+ fi
+
+ # decide where to put the guest's image based on availability
+ home_basedir=0
+ var_lib_df=$(df /var/lib)
+ var_lib_free=$(echo ${var_lib_df} | awk '{print $11}')
+ home_df=$(df /home)
+ home_free=$(echo ${home_df} | awk '{print $11}')
+ if [[ ${home_free} -gt ${var_lib_free} ]]; then
+ mkdir -p /home/virtimages/VirtualMachines
+ home_basedir=1
+ fi
+
+ if [[ ${kvm_num} > 0 ]]; then
+ if [[ ${home_basedir} == 0 ]]; then
+ basedir="/var/lib/libvirt/images"
+ else
+ basedir="/home/virtimages/VirtualMachines"
+ fi
+ else
+ if [[ ${home_basedir} == 0 ]]; then
+ basedir="/var/lib/xen/images"
+ else
+ basedir="/home/virtimages/VirtualMachines"
+ fi
+ fi
+
+ # Retrieve guest image from Lookaside
+ if ! wget -O - $LOOKASIDE/virt_images/${guest_name}.img.bz2 | bzip2 -cd > $basedir/${guest_name}.img; then
+ echo "Unable to download/uncompress $LOOKASIDE/virt_images/${guest_name}.img.bz2"
+ report_result ${TEST}/NoImage FAIL 100
+ exit 1
+ fi
+
+ #bridge=$(ip route list | awk '/^default / { print $NF }' | sed 's/^[^0-9]*//')
+ #CMDLINE="-b xenbr${bridge} -n ${guestname} -f ${IMAGE} $args"
+ CMDLINE="--name ${guest_name} --mac ${guest_mac} $guest_args --debug --nographics --noreboot --import"
+ if [[ ${kvm_num} > 0 ]]; then
+ CMDLINE="${CMDLINE}"
+ else
+ ## the first 2 conditions are for fedora releases since they too can support xen guests.
+ if grep -q -i fedora /etc/fedora-release; then
+ CMDLINE="$CMDLINE --serial file,path=$(pwd)/guests/${guest_name}/logs/${guest_name}_console.log --extra-args \"ks=$guest_ks serial console=tty0 console=ttyS0,115200\""
+ fi
+ fi
+
+ CMDLINE="${CMDLINE} --disk path=$basedir/${guest_name}.img,xvda,w"
+
+ # FIXME: Update image to use guest recipe id
+
+ # kvm guest should have --accelerate and --os-variant=virtio26 by default.
+ # --kvm switch shouldn't be passed on to virtinstall.exp
+ if [[ ${kvm_num} > 0 ]]; then
+ #get rid of --kvm
+ CMDLINE=$( echo ${CMDLINE} | awk '{ for (i=1;i<=NF;i++) { if ( $i != "--kvm" ) printf "%s ", $i } }' )
+ #add --accelerate or --os-variant=virtio26 or both
+ echo ${CMDLINE} | awk '{rc=0; for(i=1;i<=NF;i++) { if ( $i ~ /--accelerate*/ ) rc+=1; else if ( $i ~ /--os-variant=*/ ) rc += 2; } exit rc }'
+ rc=$?
+ if [[ $rc == 0 ]]; then
+ CMDLINE="${CMDLINE} --accelerate --os-variant=virtio26 "
+ elif [[ $rc == 1 ]]; then
+ CMDLINE="${CMDLINE} --accelerate "
+ elif [[ $rc == 3 ]]; then
+ CMDLINE="${CMDLINE} --os-variant=virtio26 "
+ fi
+ # make an exception for given --network arg..
+ # see BZ#821984
+ GIVENNW=$(echo $CMDLINE | awk '{ for (i=1;i<=NF;i++) { if ( $i == "--network" ) { i+=1; printf "%s ", $i } else { continue; } } }')
+ if [[ -z "${GIVENNW}" ]]; then
+ CMDLINE="${CMDLINE} --ver6 --network bridge:${brdev} "
+ else
+ CMDLINE=$(echo $CMDLINE | awk '{ for (i=1;i<=NF;i++) { if ( $i == "--network" ) { i+=1; continue; } else printf "%s ", $i } }')
+ NWARG=""
+ for opts in ${GIVENNW}
+ do
+ NWARG="${NWARG} --network bridge:${brdev},${opts}"
+ done
+ CMDLINE="${CMDLINE} --ver6 ${NWARG}"
+
+ fi
+
+ # beginning with rhel6 virt-install can take -serial option. automatically
+ # append serial console args unless one is given already or unless --virttest is given
+ if [[ ${ver} == 6 || ${ver} > 6 ]]; then
+ echo ${CMDLINE} | awk '{rc=0; for(i=1;i<=NF;i++) { if ( $i == "--virttest" || $i ~ /--serial*/) exit 1 } exit 0 }'
+ rc=$?
+ if [[ ${rc} == 0 ]]; then
+ CMDLINE="$CMDLINE --serial file,path=$(pwd)/guests/${guest_name}/logs/${guest_name}_console.log"
+ # workaround for BZ: 731115
+ l_guest_name=$(echo ${guest_name} | tr [:upper:] [:lower:])
+ if [[ x"$guest_name" != x"$l_guest_name" ]]; then
+ ln -sf $(pwd)/guests/${guest_name} $(pwd)/guests/${l_guest_name}
+ if [[ $? != 0 ]]; then
+ echo "error with workaround for bz731115"
+ report_result ${TEST}_logdir_link FAIL 100
+ fi
+ fi
+ # end of workaround for BZ 731115
+ #
+ fi
+ fi
+ fi
+
+ # Tell Beaker the guest recipe has started.
+ ./start_recipe.py $guest_recipeid
+
+ DeBug "CMDLINE == $CMDLINE"
+ echo "CMDLINE == $CMDLINE"
+ DeBug "***** Start $virtinst ${CMDLINE} *****"
+ echo "***** Start $virtinst ${CMDLINE} *****"
+ starttime=$(date +%s)
+ echo "Start time: $starttime"
+ if selinuxenabled; then
+ eval /usr/bin/runcon -t unconfined_t -- $virtinst $CMDLINE 2>&1
+ else
+ eval $virtinst $CMDLINE 2>&1
+ fi
+ value=$?
+ endtime=$(date +%s)
+ echo "End time: $endtime"
+ if [[ $value == 1 ]]; then
+ echo "WARNING: install may or may not have failed. Check the guest"
+ let "fail=${fail}+1"
+ result=FAIL
+ report_result ${TEST}/install_${guest_name} $result $value
+ elif [[ $value == 14 ]]; then
+ echo "Err No 14 workaround"
+ let "fail=${fail}+1"
+ result=WARN
+ report_result ${TEST}/install_${guest_name} $result $value
+ elif [[ $value == 33 ]]; then
+ echo "libvirt error"
+ let "fail=${fail}+1"
+ result=FAIL
+ report_result ${TEST}/install_${guest_name}_libvirterror $result $value
+ elif [[ $value == 37 ]]; then
+ echo "guest crashed"
+ let "fail=${fail}+1"
+ result=FAIL
+ report_result ${TEST}/install_${guest_name}_guestcrash $result $value
+ elif [[ $value == 38 ]]; then
+ echo "no HVM support on the machine"
+ let "fail=${fail}+1"
+ result=FAIL
+ report_result ${TEST}/install_${guest_name}_noHVMsupport $result $value
+ elif [[ $value != 0 ]]; then
+ echo "$virtinst FAILED"
+ let "fail=${fail}+1"
+ result=FAIL
+ report_result ${TEST}/install_${guest_name} $result $value
+ else
+ result=PASS
+ elapsed=$(expr $endtime - $starttime)
+ echo "***** Finished $virtinst ${guest_name} in $elapsed seconds *****"
+ report_result ${TEST}/import_${guest_name} $result $elapsed
+ fi
+ # and for rhel6 and above we should have a console log.
+ if [ -e `pwd`/guests/${guest_name}/logs/${guest_name}_console.log ]; then
+ rhts_submit_log -l `pwd`/guests/${guest_name}/logs/${guest_name}_console.log
+ fi
+ # upload the guest's config file too.
+ if [[ ${kvm_num} < 1 ]]; then
+ rhts_submit_log -l /etc/xen/${guest_name}
+ elif virsh dumpxml ${guest_name}; then
+ virsh dumpxml ${guest_name} > ./guests/${guest_name}/logs/${guest_name}.xml
+ rhts_submit_log -l ./guests/${guest_name}/logs/${guest_name}.xml
+ fi
+ let i="$i+1"
+done < ./tmp.guests
+if [[ $i == 0 ]]; then
+ report_result ${TEST}/noguestinstall PASS 0
+elif [[ ${fail} == 0 ]]; then
+ report_result ${TEST} PASS 0
+else
+ report_result ${TEST} FAIL 1
+fi
+
+# turn on service for rhel5 console writing.
+# This is after the guests are installed so that it won't try to steal console
+# from the installation
+if [ ${ver:0:1} -lt 6 -a ${minor_ver} -gt 3 -a -z "${NORHEL5CONSOLELOGS}" ]; then
+ setuprhel5consoles
+fi
+
+# submit the relevant logfiles
+submitvirtlogs
+if [ -e /nohup.out ]; then
+ rhts_submit_log -l /nohup.out
+fi
+
+exit 0
diff --git a/distribution/virt/import/scp.exp b/distribution/virt/import/scp.exp
new file mode 100755
index 0000000..033aed7
--- /dev/null
+++ b/distribution/virt/import/scp.exp
@@ -0,0 +1,62 @@
+#!/usr/bin/expect
+#
+# Usage: scp.exp <-f File> <-h Remotehost> [-u User] [-p Password] [-t timeout]
+#
+# username, password, timeout are optional.
+
+set USAGE_STR "$argv0 <-f|--file LocalFile> <-h|--host Remotehost> \[-F|--File \
+remotefile\] \[-u|--user User\] \[-p|--password Password\] \[-t|--timeout \
+timeout\] \n"
+set username "root"
+set password "redhat"
+set remotefile "~/."
+if { $argc < 4 || $argc > 12 } {
+ send_user "Usage: ${USAGE_STR} \n"
+ exit 1
+}
+
+for {set i 0} { $i < $argc } { incr i } {
+
+ set cur_arg [lindex $argv $i]
+ if { $cur_arg == "-f" || $cur_arg == "--file" } {
+ set localfile [lindex $argv [incr i] ]
+ } elseif { $cur_arg == "-h" || $cur_arg == "--host" } {
+ set remotehost [lindex $argv [incr i] ]
+ } elseif { $cur_arg == "-F" || $cur_arg == "--File" } {
+ set remotefile [lindex $argv [incr i] ]
+ } elseif { $cur_arg == "-u" || $cur_arg == "--user" } {
+ set username [lindex $argv [incr i] ]
+ } elseif { $cur_arg == "-p" || $cur_arg == "--password" } {
+ set password [lindex $argv [incr i] ]
+ } elseif { $cur_arg == "-t" || $cur_arg == "--timeout" } {
+ set timeout [lindex $argv [incr i] ]
+ } else {
+ send_user "unknown arg: $cur_arg \n"
+ send_user "Usage: ${USAGE_STR} \n"
+ exit 1
+ }
+
+}
+
+if { ![info exists localfile] } {
+ send_user "No file option is given!!!\n";
+ send_user "Usage: ${USAGE_STR} \n"
+ exit 1
+} elseif { ![info exists remotehost] } {
+ send_user "No file option is given!!!\n";
+ send_user "Usage: ${USAGE_STR} \n"
+ exit 1
+}
+
+
+spawn scp $localfile ${username}@${remotehost}:${remotefile}
+set scp_spawn $spawn_id
+expect {
+ -i $scp_spawn "connecting (yes/no)? " { send -i $scp_spawn "yes\n"; exp_continue; }
+ -i $scp_spawn -re "(P|p)assword: " { send -i $scp_spawn "$password\n"; exp_continue; }
+ -i $scp_spawn "No such file or directory" { send_user "No such file or dir\n"; exit 1; }
+ -i $scp_spawn "Permission denied," {send_user "wrong password, exiting.\n"; exit 1; }
+ -i $scp_spawn -exact "100%" { exp_continue }
+ -i $scp_spawn timeout { send_user "expect timeout!\n"; exit 1}
+ -i $scp_spawn eof { exit 0 }
+}
diff --git a/distribution/virt/import/start_recipe.py b/distribution/virt/import/start_recipe.py
new file mode 100755
index 0000000..479e3ed
--- /dev/null
+++ b/distribution/virt/import/start_recipe.py
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+"""
+Marks a guest recipe as started. Pass the recipe ID as the first argument.
+"""
+
+import os
+import sys
+import xmlrpclib
+import xml.dom.minidom
+
+proxy = xmlrpclib.ServerProxy('http://%s:8000/RPC2' % os.environ['LAB_CONTROLLER'])
+recipe_id = int(sys.argv[1])
+recipe_xml = proxy.get_my_recipe(dict(recipe_id=recipe_id))
+doc = xml.dom.minidom.parseString(recipe_xml)
+
+# This is the same logic as when the 'reboot' command goes through for a real
+# system... grab the first task in the recipe and mark it as started. This will
+# set the state of the recipe to Running and start the watchdog with some time
+# on the clock. The watchdog will be extended again once Anaconda hits
+# install_start.
+first_task = doc.getElementsByTagName('task')[0]
+proxy.task_start(first_task.getAttribute('id'))
diff --git a/distribution/virt/import/startguest b/distribution/virt/import/startguest
new file mode 100755
index 0000000..be593ea
--- /dev/null
+++ b/distribution/virt/import/startguest
@@ -0,0 +1,61 @@
+#!/usr/bin/expect
+#
+# utility script to start the guest and make sure that it started is up
+# it has a default of 5 minutes to wait for the guest to start up.
+#
+
+if { $argc < 1 } {
+ send_user "Usage: $argv0 guestname <optional timeout value>"
+ exit 1
+}
+
+# stupid hack for when this is called from a script
+if { $argc == 1 } {
+ set argv_tmp [lindex $argv 0]
+ set argv [split $argv_tmp]
+}
+
+if { [llength $argv] > 1 } {
+ set waittimeout [lindex $argv 1]
+} else {
+ set waittimeout 300
+}
+
+set guest [lindex $argv 0]
+set prompt "(%|#|\\\$) $"
+log_user 0
+
+spawn virsh start $guest
+set start_spawn $spawn_id
+
+expect {
+ -i $start_spawn "Domain $guest started" { exp_continue; }
+ -i $start_spawn timeout {
+ send_user "timeout on virsh start $guest\n";
+ exit 1;
+ }
+ -i $start_spawn eof { }
+}
+
+spawn virsh console $guest
+set con_spawn $spawn_id
+set timeout $waittimeout
+
+send -i $con_spawn "\r"
+send -i $con_spawn "\r"
+send -i $con_spawn "\r"
+expect {
+ -i $con_spawn "login: " { send -i $con_spawn ; exit 0; }
+ -i $con_spawn -re "(P|p)assword:" { send -i $con_spawn ; exit 0; }
+ -i $con_spawn -re $prompt { send -i $con_spawn ; exit 0; }
+ -i $con_spawn timeout {
+ send_user "start timeout\n";
+ exit 1;
+ }
+ -i $con_spawn eof {
+ send_user "start EOF\n";
+ exit 1;
+ }
+}
+
+exit 1;
diff --git a/distribution/virt/import/stopguest b/distribution/virt/import/stopguest
new file mode 100755
index 0000000..b4d20f3
--- /dev/null
+++ b/distribution/virt/import/stopguest
@@ -0,0 +1,56 @@
+#!/usr/bin/expect
+#
+# utility script to stop the guest and make sure that it has shutdown
+# it has a default of 5 minutes to wait for the guest to shut
+#
+
+if { $argc < 1 } {
+ send_user "Usage: $argv0 guestname <optional timeout value>"
+ exit 1
+}
+
+# stupid hack for when this is called from a script
+if { $argc == 1 } {
+ set argv_tmp [lindex $argv 0]
+ set argv [split $argv_tmp]
+}
+
+if { [llength $argv] > 1 } {
+ set waittimeout [lindex $argv 1]
+} else {
+ set waittimeout 300
+}
+
+set guest [lindex $argv 0]
+set prompt "(%|#|\\\$) $"
+log_user 0
+
+spawn virsh shutdown $guest
+set stop_spawn $spawn_id
+
+expect {
+ -i $stop_spawn "Domain $guest is being shutdown" { }
+ -i $stop_spawn timeout {
+ send_user "timeout on virsh shutdown $guest\n";
+ exit 1;
+ }
+ -i $stop_spawn eof {
+ send_user "EOF on virsh shutdown $guest\n";
+ exit 1;
+ }
+}
+
+spawn virsh console $guest
+set con_spawn $spawn_id
+set timeout $waittimeout
+
+expect {
+ -i $con_spawn "System halted." { exit 0; }
+ -i $con_spawn timeout {
+ send_user "start timeout\n";
+ exit 1;
+ }
+ -i $con_spawn eof { exit 0; }
+}
+
+exit 1;
diff --git a/distribution/virt/import/virshstartguest b/distribution/virt/import/virshstartguest
new file mode 100755
index 0000000..4ed37c0
--- /dev/null
+++ b/distribution/virt/import/virshstartguest
@@ -0,0 +1,68 @@
+#!/usr/bin/expect
+#
+# utility script to start the guest and make sure that it started is up
+# it has a default of 5 minutes to wait for the guest to start up.
+#
+
+#exp_internal 1
+
+if { $argc < 1 } {
+ send_user "Usage: $argv0 guestname <optional timeout value>"
+ exit 1
+}
+
+# stupid hack for when this is called from a script
+if { $argc == 1 } {
+ set argv_tmp [lindex $argv 0]
+ set argv [split $argv_tmp]
+}
+
+if { [llength $argv] > 1 } {
+ set waittimeout [lindex $argv 1]
+} else {
+ set waittimeout 300
+}
+
+set guest [lindex $argv 0]
+set prompt "(%|#|\\\$) $"
+log_user 0
+
+spawn virsh start $guest
+set start_spawn $spawn_id
+expect {
+ -i $start_spawn "Domain $guest started" { }
+ -i $start_spawn timeout {
+ send_user "timeout on virsh start $guest\n";
+ exit 1;
+ }
+ -i $start_spawn eof {
+ send_user "Unexpected EOF on starting $guest \n";
+ exit 1;
+ }
+}
+
+sleep 15
+set timeout $waittimeout
+spawn virsh console $guest
+set con_spawn $spawn_id
+send -i $con_spawn "\n"
+send -i $con_spawn "\n"
+send -i $con_spawn "\n"
+expect {
+ -i $con_spawn "Press any key to continue" { send -i $con_spawn "\n"; exp_continue; }
+ -i $con_spawn "Press enter" {sleep 5 ; send -i $con_spawn "\n"; exp_continue; }
+ -i $con_spawn "The highlighted entry will be booted automatically in" { exp_continue; }
+ -i $con_spawn -exact {[ OK ]} { exp_continue; }
+ -i $con_spawn "login:" { send -i $con_spawn "\n";send -i $con_spawn ; exit 0; }
+ -i $con_spawn -re "(P|p)assword:" { send -i $con_spawn "\n";send -i $con_spawn ; exit 0; }
+ -i $con_spawn timeout {
+ send_user "start timeout\n";
+ exit 1;
+ }
+ -i $con_spawn eof {
+ send_user "start EOF\n";
+ exit 1;
+ }
+}
+
+exit 1;
diff --git a/distribution/virt/import/virshstopguest b/distribution/virt/import/virshstopguest
new file mode 100755
index 0000000..b4d20f3
--- /dev/null
+++ b/distribution/virt/import/virshstopguest
@@ -0,0 +1,56 @@
+#!/usr/bin/expect
+#
+# utility script to stop the guest and make sure that it has shutdown
+# it has a default of 5 minutes to wait for the guest to shut
+#
+
+if { $argc < 1 } {
+ send_user "Usage: $argv0 guestname <optional timeout value>"
+ exit 1
+}
+
+# stupid hack for when this is called from a script
+if { $argc == 1 } {
+ set argv_tmp [lindex $argv 0]
+ set argv [split $argv_tmp]
+}
+
+if { [llength $argv] > 1 } {
+ set waittimeout [lindex $argv 1]
+} else {
+ set waittimeout 300
+}
+
+set guest [lindex $argv 0]
+set prompt "(%|#|\\\$) $"
+log_user 0
+
+spawn virsh shutdown $guest
+set stop_spawn $spawn_id
+
+expect {
+ -i $stop_spawn "Domain $guest is being shutdown" { }
+ -i $stop_spawn timeout {
+ send_user "timeout on virsh shutdown $guest\n";
+ exit 1;
+ }
+ -i $stop_spawn eof {
+ send_user "EOF on virsh shutdown $guest\n";
+ exit 1;
+ }
+}
+
+spawn virsh console $guest
+set con_spawn $spawn_id
+set timeout $waittimeout
+
+expect {
+ -i $con_spawn "System halted." { exit 0; }
+ -i $con_spawn timeout {
+ send_user "start timeout\n";
+ exit 1;
+ }
+ -i $con_spawn eof { exit 0; }
+}
+
+exit 1;
diff --git a/distribution/virt/import/wait4guest b/distribution/virt/import/wait4guest
new file mode 100755
index 0000000..00af187
--- /dev/null
+++ b/distribution/virt/import/wait4guest
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+
+import xmltramp
+import xmlrpclib
+import time
+import sys
+from beah import config
+from datetime import datetime, timedelta
+
+lab_controller = None
+
+USAGE_TEXT = """
+Usage: wait4guest guestrecipeid <timeout>
+
+timeout is optional and is in seconds
+"""
+
+def configure():
+ config.backend_conf(filename='beah_beaker.conf')
+
+def loop(recipeid, timeout):
+ conf = config.get_conf('beah-backend')
+ lab_controller = conf.get('DEFAULT','LAB_CONTROLLER')
+ server = xmlrpclib.ServerProxy(lab_controller)
+ while True:
+ myxml = server.get_my_recipe(dict(recipe_id=recipeid))
+ status = xmltramp.parse(myxml).recipeSet.guestrecipe()['status']
+ if status in ['Running']:
+ sys.exit(0)
+ if timeout and timeout < datetime.now():
+ print "Timeout Exceeded"
+ sys.exit(1)
+ time.sleep(60)
+
+def usage():
+ sys.stderr.write(USAGE_TEXT)
+ sys.exit(-1)
+
+def main():
+ if len(sys.argv) < 2:
+ usage()
+ recipeid = sys.argv[1]
+ if len(sys.argv) == 3:
+ timeout = datetime.now() + timedelta(seconds = int(sys.argv[2]))
+ else:
+ timeout = None
+ configure()
+ loop(recipeid, timeout)
+
+if __name__ == '__main__':
+ main()
diff --git a/distribution/virt/import/wait4guesttasks b/distribution/virt/import/wait4guesttasks
new file mode 100755
index 0000000..91484f7
--- /dev/null
+++ b/distribution/virt/import/wait4guesttasks
@@ -0,0 +1,201 @@
+#!/usr/bin/python
+#
+
+# wait4guesttasks
+# usage: wait4guesttasks <--guestname name | --guestrecipe recipe> \
+# [--timeout NN[s|m|h|d]] [--interval NN[s|m|h|d]] \
+# [/task/name /task2/name ...]
+# this script will take a name or recipeid of the guest and list of task to be
+# waited to for completion and a timeout value to wait for all tasks to be
+# completed. If the tasks inside the guest complete with the optional statuses,
+# it'll exit with exit code of 0. If anything goes wrong, it'll exit with an
+# exit code other than zero. timeout argument can take an optional and
+# case-insensitive suffix of s,m,h,d corresponding to seconds,minutes,hours,days
+# respectively.
+# Everything but either guestname or guestrecipeid is optional. Default values
+# follows:
+# timeout: none. ie. it'll run forever until the tasks are completed.
+# interval: set to 120 seconds.
+# tasks: default is all tasks inside the guest to be completed.
+# Additionally, a desired result for any task can be given. For that the syntax
+# is :
+# taskname=result for example /distribution/install=Pass
+# If no desired status are given, then the script will just wait until the task
+# is completed. For example the following syntax:
+# .waitforguesttasks.py --guestname myguest /distribution/install=Pass\
+# /my/test
+# will wait until /distribution/install test inside myguest has completed and
+# Passed AND the test /my/test has completed. If /distribution/install has a
+# result other than Pass, it'll exit with an error code. The results are
+# case-insensitive, so /distribution/install=Pass|pass|PASS are all same.
+#
+import os
+import sys
+import xmlrpclib
+import xml.dom.minidom
+import time
+from optparse import OptionParser
+
+# findguestrecipe
+# looks up the recipeid of the guest.
+# should be called from the hostmachine where the hypervisor's recipeid will be
+# available. assumes that a connection to global proxy object is established
+# already.
+def findguestrecipe(name):
+ recipe_xml = proxy.get_my_recipe(dict(recipe_id=os.environ['RECIPEID']))
+ doc = xml.dom.minidom.parseString(recipe_xml)
+
+ for guestrecipe in doc.getElementsByTagName('guestrecipe'):
+ if guestrecipe.getAttribute('guestname') == name:
+ return guestrecipe.getAttribute('id')
+
+ #if we didn't find it, return None
+ return None
+
+tasks_list = None
+tasks_dict = { }
+total_sleep = 0
+timeout = -1
+interval = 20
+errors = []
+parser = OptionParser()
+
+proxy = xmlrpclib.ServerProxy('http://%s:8000/RPC2' % os.environ['LAB_CONTROLLER'])
+
+# add options to parser all the options...
+parser.add_option("--guestname", dest="guestname", help="Name of guest",
+ metavar="GUESTNAME")
+parser.add_option("--guestrecipe", dest="guestrecipe", help="Recipeid of guest",
+ metavar="GUESTRECIPE")
+parser.add_option("--timeout", dest="timeout", help="Time to wait until the \
+ tasks are completed", metavar="TIMEOUT")
+parser.add_option("--interval", dest="interval", help="How often to query the \
+ lab controller to check on the tasks", metavar="INTERVAL")
+
+(options, args) = parser.parse_args()
+
+if not options.guestname and not options.guestrecipe:
+ print "no guestname or guestrecipe"
+ print "either one of them must be supplied"
+ sys.exit(1)
+
+if options.guestrecipe:
+ guestrecipe = options.guestrecipe
+else:
+ guestrecipe = findguestrecipe(options.guestname)
+ if not guestrecipe:
+ print "Can't find recipeid for %s", (options.guestname)
+ sys.exit(3)
+
+if options.timeout:
+ timeout = options.timeout
+ if timeout[-1] == 's' or timeout[-1] == 'S':
+ timeout = int(timeout[:-1])
+ elif timeout[-1] == 'm' or timeout[-1] == 'M':
+ timeout = int(timeout[:-1])
+ timeout = timeout * 60
+ elif timeout[-1] == 'h' or timeout[-1] == 'H':
+ timeout = int(timeout[:-1])
+ timeout = timeout * 3600
+ elif timeout[-1] == 'd' or timeout[-1] == 'D':
+ timeout = int(timeout[:-1])
+ timeout = timeout * 86400
+ timeout = int(timeout)
+
+if options.interval:
+ interval = options.interval
+ if interval[-1] == 's' or interval[-1] == 'S':
+ interval = int(interval[:-1])
+ elif interval[-1] == 'm' or interval[-1] == 'M':
+ interval = int(interval[:-1])
+ interval = interval * 60
+ elif interval[-1] == 'h' or interval[-1] == 'H':
+ interval = int(interval[:-1])
+ interval = interval * 3600
+ elif interval[-1] == 'd' or interval[-1] == 'D':
+ interval = int(interval[:-1])
+ interval = interval * 86400
+ interval = int(interval)
+
+# if there are any args, they'll be the tasks.
+tasks_list = args
+# default tasks_list is all
+if not tasks_list:
+ tasks_list = []
+ # find all the tasks inside the guest
+ recipe_xml = proxy.get_my_recipe(dict(recipe_id=guestrecipe))
+ doc = xml.dom.minidom.parseString(recipe_xml)
+ for xmltasks in doc.getElementsByTagName('task'):
+ taskname = xmltasks.getAttribute('name')
+ tasks_list.append(taskname)
+
+
+print "guestrecipe that'll be used is: " + guestrecipe
+
+# walk thru tasks and see what (if any) results that needs to be completed
+for task in tasks_list:
+ idv_task = task.split('=')
+ if len(idv_task) > 1:
+ tasks_dict[idv_task[0]] = idv_task[1]
+ else:
+ tasks_dict[idv_task[0]] = None
+
+print "waiting for below tasks to be completed in %s :" % (guestrecipe)
+print "Task name Result "
+for key,value in tasks_dict.iteritems():
+ print "%s %s" % (key,value)
+
+while len(tasks_dict) > 0:
+ if timeout > -1:
+ if total_sleep > timeout:
+ print "Timeout waiting for the tasks to complete."
+ if len(errors) > 0:
+ print "Also these tasks didn't have the expected result: "
+ print errors
+ sys.exit(5)
+
+ recipe_xml = proxy.get_my_recipe(dict(recipe_id=os.environ['RECIPEID']))
+ doc = xml.dom.minidom.parseString(recipe_xml)
+
+ for xmltasks in doc.getElementsByTagName('task'):
+ taskname = xmltasks.getAttribute('name')
+ taskstatus = xmltasks.getAttribute('status')
+ taskresult = xmltasks.getAttribute('result')
+ if taskstatus != 'Completed':
+ continue
+
+ if taskname in tasks_dict:
+ # check if we need to be aware of the result
+ if tasks_dict[taskname] is not None:
+ if taskresult.lower() != tasks_dict[taskname].lower():
+ print "For %s looking for %s but got %s" \
+ % (taskname,tasks_dict[taskname],taskresult)
+ errors.append(taskname)
+ del tasks_dict[taskname]
+ else:
+ print "Got %s done" % (taskname)
+ del tasks_dict[taskname]
+ if len(tasks_dict) > 0:
+ print "Now waiting for : "
+ for key,value in tasks_dict.iteritems():
+ print "%s %s" % (key,value)
+
+ else:
+ print "Got %s done" % (taskname)
+ del tasks_dict[taskname]
+ if len(tasks_dict) > 0:
+ print "Now waiting for : "
+ for key,value in tasks_dict.iteritems():
+ print "%s %s" % (key,value)
+
+ if len(tasks_dict) == 0:
+ break
+ total_sleep = total_sleep + interval
+ time.sleep(interval)
+
+if len(errors) > 0:
+ print "These tasks didn't have the expected result: "
+ print errors
+ sys.exit(2)
+
+sys.exit(0)
diff --git a/distribution/virt/import/wait4login b/distribution/virt/import/wait4login
new file mode 100755
index 0000000..349d21b
--- /dev/null
+++ b/distribution/virt/import/wait4login
@@ -0,0 +1,48 @@
+#!/usr/bin/expect
+
+if { $argc != 1 && $argc != 2 } {
+ send_user "Usage: $argv0 guestname <timeout>\n";
+ send_user "timeout is optional. \n"
+ exit 1;
+}
+set timeout -1
+set guestname [lindex $argv 0]
+set prompt "(%|#|\\\$) $"
+
+if { $argc == 2 } {
+ if { ![string is integer [lindex $argv 1]] } {
+ send_user "Timeout value must be an integer.\n";
+ exit 1;
+ }
+ set timeout [lindex $argv 1]
+}
+
+# spawn whatever the guest is configured with
+if { [catch {exec isfileconsole $guestname} errStr] } {
+ spawn virsh console $guestname
+ sleep 1
+ send "\r\n"
+ sleep 1
+ send "\r\n"
+} else {
+ if { [ catch { set console_file [exec getconsolefile $guestname] } ] } {
+ send_user "error with set console_file \[getconsolefile $guestname\] \n"
+ exit 1;
+ }
+ spawn tail -f $console_file
+}
+expect {
+ -exact "login: " { exit 0; }
+ timeout {
+ send_user "didn't get prompt in $timeout seconds \n";
+ exit 1;
+ }
+ eof {
+ send_user "Unexpected EOF ..\n";
+ exit 1;
+ }
+}
+
+#should never come here ...
+exit 1;
+
diff --git a/distribution/virt/import/wait4shutdown b/distribution/virt/import/wait4shutdown
new file mode 100755
index 0000000..1a0e2d9
--- /dev/null
+++ b/distribution/virt/import/wait4shutdown
@@ -0,0 +1,41 @@
+#!/usr/bin/expect
+
+if { $argc != 1 && $argc != 2 } {
+ send_user "Usage: $argv0 guestname <timeout>\n";
+ send_user "timeout is optional. \n"
+ exit 1;
+}
+set timeout -1
+set guestname [lindex $argv 0]
+set prompt "(%|#|\\\$) $"
+
+if { $argc == 2 } {
+ if { ![string is integer [lindex $argv 1]] } {
+ send_user "Timeout value must be an integer.\n";
+ exit 1;
+ }
+ set timeout [lindex $argv 1]
+}
+
+# spawn whatever the guest is configured with
+if { [catch {exec isfileconsole $guestname} errStr] } {
+ spawn virsh console $guestname
+} else {
+ if { [ catch { set console_file [exec getconsolefile $guestname] } ] } {
+ send_user "error with set console_file \[getconsolefile $guestname\] \n"
+ exit 1;
+ }
+ spawn tail -f $console_file
+}
+expect {
+ "Power down." { exit 0; }
+ timeout {
+ send_user "didn't get prompt in $timeout seconds \n";
+ exit 1;
+ }
+ eof { exit 0; }
+}
+
+#should never come here ...
+exit 1;
+
diff --git a/distribution/virt/import/xmltramp.py b/distribution/virt/import/xmltramp.py
new file mode 100755
index 0000000..25dd0d0
--- /dev/null
+++ b/distribution/virt/import/xmltramp.py
@@ -0,0 +1,361 @@
+"""xmltramp: Make XML documents easily accessible."""
+
+__version__ = "2.17"
+__author__ = "Aaron Swartz"
+__credits__ = "Many thanks to pjz, bitsko, and DanC."
+__copyright__ = "(C) 2003-2006 Aaron Swartz. GNU GPL 2."
+
+if not hasattr(__builtins__, 'True'): True, False = 1, 0
+def isstr(f): return isinstance(f, type('')) or isinstance(f, type(u''))
+def islst(f): return isinstance(f, type(())) or isinstance(f, type([]))
+
+empty = {'http://www.w3.org/1999/xhtml': ['img', 'br', 'hr', 'meta', 'link', 'base', 'param', 'input', 'col', 'area']}
+
+def quote(x, elt=True):
+ if elt and '<' in x and len(x) > 24 and x.find(']]>') == -1: return "<![CDATA["+x+"]]>"
+ else: x = x.replace('&', '&amp;').replace('<', '&lt;').replace(']]>', ']]&gt;')
+ if not elt: x = x.replace('"', '&quot;')
+ return x
+
+class Element:
+ def __init__(self, name, attrs=None, children=None, prefixes=None):
+ if islst(name) and name[0] == None: name = name[1]
+ if attrs:
+ na = {}
+ for k in attrs.keys():
+ if islst(k) and k[0] == None: na[k[1]] = attrs[k]
+ else: na[k] = attrs[k]
+ attrs = na
+
+ self._name = name
+ self._attrs = attrs or {}
+ self._dir = children or []
+
+ prefixes = prefixes or {}
+ self._prefixes = dict(zip(prefixes.values(), prefixes.keys()))
+
+ if prefixes: self._dNS = prefixes.get(None, None)
+ else: self._dNS = None
+
+ def __repr__(self, recursive=0, multiline=0, inprefixes=None):
+ def qname(name, inprefixes):
+ if islst(name):
+ if inprefixes[name[0]] is not None:
+ return inprefixes[name[0]]+':'+name[1]
+ else:
+ return name[1]
+ else:
+ return name
+
+ def arep(a, inprefixes, addns=1):
+ out = ''
+
+ for p in self._prefixes.keys():
+ if not p in inprefixes.keys():
+ if addns: out += ' xmlns'
+ if addns and self._prefixes[p]: out += ':'+self._prefixes[p]
+ if addns: out += '="'+quote(p, False)+'"'
+ inprefixes[p] = self._prefixes[p]
+
+ for k in a.keys():
+ out += ' ' + qname(k, inprefixes)+ '="' + quote(a[k], False) + '"'
+
+ return out
+
+ inprefixes = inprefixes or {u'http://www.w3.org/XML/1998/namespace':'xml'}
+
+ # need to call first to set inprefixes:
+ attributes = arep(self._attrs, inprefixes, recursive)
+ out = '<' + qname(self._name, inprefixes) + attributes
+
+ if not self._dir and (self._name[0] in empty.keys()
+ and self._name[1] in empty[self._name[0]]):
+ out += ' />'
+ return out
+
+ out += '>'
+
+ if recursive:
+ content = 0
+ for x in self._dir:
+ if isinstance(x, Element): content = 1
+
+ pad = '\n' + ('\t' * recursive)
+ for x in self._dir:
+ if multiline and content: out += pad
+ if isstr(x): out += quote(x)
+ elif isinstance(x, Element):
+ out += x.__repr__(recursive+1, multiline, inprefixes.copy())
+ else:
+ raise TypeError, "I wasn't expecting "+`x`+"."
+ if multiline and content: out += '\n' + ('\t' * (recursive-1))
+ else:
+ if self._dir: out += '...'
+
+ out += '</'+qname(self._name, inprefixes)+'>'
+
+ return out
+
+ def __unicode__(self):
+ text = ''
+ for x in self._dir:
+ text += unicode(x)
+ return ' '.join(text.split())
+
+ def __str__(self):
+ return self.__unicode__().encode('utf-8')
+
+ def __getattr__(self, n):
+ if n[0] == '_': raise AttributeError, "Use foo['"+n+"'] to access the child element."
+ if self._dNS: n = (self._dNS, n)
+ for x in self._dir:
+ if isinstance(x, Element) and x._name == n: return x
+ raise AttributeError, 'No child element named %s' % repr(n)
+
+ def __hasattr__(self, n):
+ for x in self._dir:
+ if isinstance(x, Element) and x._name == n: return True
+ return False
+
+ def __setattr__(self, n, v):
+ if n[0] == '_': self.__dict__[n] = v
+ else: self[n] = v
+
+
+ def __getitem__(self, n):
+ if isinstance(n, type(0)): # d[1] == d._dir[1]
+ return self._dir[n]
+ elif isinstance(n, slice(0).__class__):
+ # numerical slices
+ if isinstance(n.start, type(0)): return self._dir[n.start:n.stop]
+
+ # d['foo':] == all <foo>s
+ n = n.start
+ if self._dNS and not islst(n): n = (self._dNS, n)
+ out = []
+ for x in self._dir:
+ if isinstance(x, Element) and x._name == n: out.append(x)
+ return out
+ else: # d['foo'] == first <foo>
+ if self._dNS and not islst(n): n = (self._dNS, n)
+ for x in self._dir:
+ if isinstance(x, Element) and x._name == n: return x
+ raise KeyError
+
+ def __setitem__(self, n, v):
+ if isinstance(n, type(0)): # d[1]
+ self._dir[n] = v
+ elif isinstance(n, slice(0).__class__):
+ # d['foo':] adds a new foo
+ n = n.start
+ if self._dNS and not islst(n): n = (self._dNS, n)
+
+ nv = Element(n)
+ self._dir.append(nv)
+
+ else: # d["foo"] replaces first <foo> and dels rest
+ if self._dNS and not islst(n): n = (self._dNS, n)
+
+ nv = Element(n); nv._dir.append(v)
+ replaced = False
+
+ todel = []
+ for i in range(len(self)):
+ if self[i]._name == n:
+ if replaced:
+ todel.append(i)
+ else:
+ self[i] = nv
+ replaced = True
+ if not replaced: self._dir.append(nv)
+ for i in todel: del self[i]
+
+ def __delitem__(self, n):
+ if isinstance(n, type(0)): del self._dir[n]
+ elif isinstance(n, slice(0).__class__):
+ # delete all <foo>s
+ n = n.start
+ if self._dNS and not islst(n): n = (self._dNS, n)
+
+ for i in range(len(self)):
+ if self[i]._name == n: del self[i]
+ else:
+ # delete first foo
+ for i in range(len(self)):
+ if self[i]._name == n: del self[i]
+ break
+
+ def __call__(self, *_pos, **_set):
+ if _set:
+ for k in _set.keys(): self._attrs[k] = _set[k]
+ if len(_pos) > 1:
+ for i in range(0, len(_pos), 2):
+ self._attrs[_pos[i]] = _pos[i+1]
+ if len(_pos) == 1 is not None:
+ return self._attrs[_pos[0]]
+ if len(_pos) == 0:
+ return self._attrs
+
+ def __len__(self): return len(self._dir)
+
+class Namespace:
+ def __init__(self, uri): self.__uri = uri
+ def __getattr__(self, n): return (self.__uri, n)
+ def __getitem__(self, n): return (self.__uri, n)
+
+from xml.sax.handler import EntityResolver, DTDHandler, ContentHandler, ErrorHandler
+
+class Seeder(EntityResolver, DTDHandler, ContentHandler, ErrorHandler):
+ def __init__(self):
+ self.stack = []
+ self.ch = ''
+ self.prefixes = {}
+ ContentHandler.__init__(self)
+
+ def startPrefixMapping(self, prefix, uri):
+ if not self.prefixes.has_key(prefix): self.prefixes[prefix] = []
+ self.prefixes[prefix].append(uri)
+ def endPrefixMapping(self, prefix):
+ self.prefixes[prefix].pop()
+
+ def startElementNS(self, name, qname, attrs):
+ ch = self.ch; self.ch = ''
+ if ch and not ch.isspace(): self.stack[-1]._dir.append(ch)
+
+ attrs = dict(attrs)
+ newprefixes = {}
+ for k in self.prefixes.keys(): newprefixes[k] = self.prefixes[k][-1]
+
+ self.stack.append(Element(name, attrs, prefixes=newprefixes.copy()))
+
+ def characters(self, ch):
+ self.ch += ch
+
+ def endElementNS(self, name, qname):
+ ch = self.ch; self.ch = ''
+ if ch and not ch.isspace(): self.stack[-1]._dir.append(ch)
+
+ element = self.stack.pop()
+ if self.stack:
+ self.stack[-1]._dir.append(element)
+ else:
+ self.result = element
+
+from xml.sax import make_parser
+from xml.sax.handler import feature_namespaces
+
+def seed(fileobj):
+ seeder = Seeder()
+ parser = make_parser()
+ parser.setFeature(feature_namespaces, 1)
+ parser.setContentHandler(seeder)
+ parser.parse(fileobj)
+ return seeder.result
+
+def parse(text):
+ from StringIO import StringIO
+ return seed(StringIO(text))
+
+def load(url):
+ import urllib
+ return seed(urllib.urlopen(url))
+
+def unittest():
+ parse('<doc>a<baz>f<b>o</b>ob<b>a</b>r</baz>a</doc>').__repr__(1,1) == \
+ '<doc>\n\ta<baz>\n\t\tf<b>o</b>ob<b>a</b>r\n\t</baz>a\n</doc>'
+
+ assert str(parse("<doc />")) == ""
+ assert str(parse("<doc>I <b>love</b> you.</doc>")) == "I love you."
+ assert parse("<doc>\nmom\nwow\n</doc>")[0].strip() == "mom\nwow"
+ assert str(parse('<bing> <bang> <bong>center</bong> </bang> </bing>')) == "center"
+ assert str(parse('<doc>\xcf\x80</doc>')) == '\xcf\x80'
+
+ d = Element('foo', attrs={'foo':'bar'}, children=['hit with a', Element('bar'), Element('bar')])
+
+ try:
+ d._doesnotexist
+ raise "ExpectedError", "but found success. Damn."
+ except AttributeError: pass
+ assert d.bar._name == 'bar'
+ try:
+ d.doesnotexist
+ raise "ExpectedError", "but found success. Damn."
+ except AttributeError: pass
+
+ assert hasattr(d, 'bar') == True
+
+ assert d('foo') == 'bar'
+ d(silly='yes')
+ assert d('silly') == 'yes'
+ assert d() == d._attrs
+
+ assert d[0] == 'hit with a'
+ d[0] = 'ice cream'
+ assert d[0] == 'ice cream'
+ del d[0]
+ assert d[0]._name == "bar"
+ assert len(d[:]) == len(d._dir)
+ assert len(d[1:]) == len(d._dir) - 1
+ assert len(d['bar':]) == 2
+ d['bar':] = 'baz'
+ assert len(d['bar':]) == 3
+ assert d['bar']._name == 'bar'
+
+ d = Element('foo')
+
+ doc = Namespace("http://example.org/bar")
+ bbc = Namespace("http://example.org/bbc")
+ dc = Namespace("http://purl.org/dc/elements/1.1/")
+ d = parse("""<doc version="2.7182818284590451"
+ xmlns="http://example.org/bar"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:bbc="http://example.org/bbc">
+ <author>John Polk and John Palfrey</author>
+ <dc:creator>John Polk</dc:creator>
+ <dc:creator>John Palfrey</dc:creator>
+ <bbc:show bbc:station="4">Buffy</bbc:show>
+ </doc>""")
+
+ assert repr(d) == '<doc version="2.7182818284590451">...</doc>'
+ assert d.__repr__(1) == '<doc xmlns:bbc="http://example.org/bbc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://example.org/bar" version="2.7182818284590451"><author>John Polk and John Palfrey</author><dc:creator>John Polk</dc:creator><dc:creator>John Palfrey</dc:creator><bbc:show bbc:station="4">Buffy</bbc:show></doc>'
+ assert d.__repr__(1,1) == '<doc xmlns:bbc="http://example.org/bbc" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://example.org/bar" version="2.7182818284590451">\n\t<author>John Polk and John Palfrey</author>\n\t<dc:creator>John Polk</dc:creator>\n\t<dc:creator>John Palfrey</dc:creator>\n\t<bbc:show bbc:station="4">Buffy</bbc:show>\n</doc>'
+
+ assert repr(parse("<doc xml:lang='en' />")) == '<doc xml:lang="en"></doc>'
+
+ assert str(d.author) == str(d['author']) == "John Polk and John Palfrey"
+ assert d.author._name == doc.author
+ assert str(d[dc.creator]) == "John Polk"
+ assert d[dc.creator]._name == dc.creator
+ assert str(d[dc.creator:][1]) == "John Palfrey"
+ d[dc.creator] = "Me!!!"
+ assert str(d[dc.creator]) == "Me!!!"
+ assert len(d[dc.creator:]) == 1
+ d[dc.creator:] = "You!!!"
+ assert len(d[dc.creator:]) == 2
+
+ assert d[bbc.show](bbc.station) == "4"
+ d[bbc.show](bbc.station, "5")
+ assert d[bbc.show](bbc.station) == "5"
+
+ e = Element('e')
+ e.c = '<img src="foo">'
+ assert e.__repr__(1) == '<e><c>&lt;img src="foo"></c></e>'
+ e.c = '2 > 4'
+ assert e.__repr__(1) == '<e><c>2 > 4</c></e>'
+ e.c = 'CDATA sections are <em>closed</em> with ]]>.'
+ assert e.__repr__(1) == '<e><c>CDATA sections are &lt;em>closed&lt;/em> with ]]&gt;.</c></e>'
+ e.c = parse('<div xmlns="http://www.w3.org/1999/xhtml">i<br /><span></span>love<br />you</div>')
+ assert e.__repr__(1) == '<e><c><div xmlns="http://www.w3.org/1999/xhtml">i<br /><span></span>love<br />you</div></c></e>'
+
+ e = Element('e')
+ e('c', 'that "sucks"')
+ assert e.__repr__(1) == '<e c="that &quot;sucks&quot;"></e>'
+
+
+ assert quote("]]>") == "]]&gt;"
+ assert quote('< dkdkdsd dkd sksdksdfsd fsdfdsf]]> kfdfkg >') == '&lt; dkdkdsd dkd sksdksdfsd fsdfdsf]]&gt; kfdfkg >'
+
+ assert parse('<x a="&lt;"></x>').__repr__(1) == '<x a="&lt;"></x>'
+ assert parse('<a xmlns="http://a"><b xmlns="http://b"/></a>').__repr__(1) == '<a xmlns="http://a"><b xmlns="http://b"></b></a>'
+
+if __name__ == '__main__': unittest()
diff --git a/distribution/virt/import/xmstartguest b/distribution/virt/import/xmstartguest
new file mode 100755
index 0000000..a982435
--- /dev/null
+++ b/distribution/virt/import/xmstartguest
@@ -0,0 +1,61 @@
+#!/usr/bin/expect
+#
+# utility script to start the guest and make sure that it started is up
+# it has a default of 5 minutes to wait for the guest to start up.
+#
+
+if { $argc < 1 } {
+ send_user "Usage: $argv0 guestname <optional timeout value>"
+ exit 1
+}
+
+# stupid hack for when this is called from a script
+if { $argc == 1 } {
+ set argv_tmp [lindex $argv 0]
+ set argv [split $argv_tmp]
+}
+
+if { [llength $argv] > 1 } {
+ set waittimeout [lindex $argv 1]
+} else {
+ set waittimeout 300
+}
+
+set guest [lindex $argv 0]
+set prompt "(%|#|\\\$) $"
+log_user 0
+
+spawn xm create $guest
+set start_spawn $spawn_id
+
+expect {
+ -i $start_spawn "Domain $guest started" { exp_continue; }
+ -i $start_spawn timeout {
+ send_user "timeout on xm start $guest\n";
+ exit 1;
+ }
+ -i $start_spawn eof { }
+}
+
+spawn xm console $guest
+set con_spawn $spawn_id
+set timeout $waittimeout
+
+send -i $con_spawn "\r"
+send -i $con_spawn "\r"
+send -i $con_spawn "\r"
+expect {
+ -i $con_spawn "login: " { send -i $con_spawn ; exit 0; }
+ -i $con_spawn -re "(P|p)assword:" { send -i $con_spawn ; exit 0; }
+ -i $con_spawn -re $prompt { send -i $con_spawn ; exit 0; }
+ -i $con_spawn timeout {
+ send_user "start timeout\n";
+ exit 1;
+ }
+ -i $con_spawn eof {
+ send_user "start EOF\n";
+ exit 1;
+ }
+}
+
+exit 1;
diff --git a/distribution/virt/import/xmstopguest b/distribution/virt/import/xmstopguest
new file mode 100755
index 0000000..cd1730a
--- /dev/null
+++ b/distribution/virt/import/xmstopguest
@@ -0,0 +1,52 @@
+#!/usr/bin/expect
+#
+# utility script to stop the guest and make sure that it has shutdown
+# it has a default of 5 minutes to wait for the guest to shut
+#
+
+if { $argc < 1 } {
+ send_user "Usage: $argv0 guestname <optional timeout value>"
+ exit 1
+}
+
+# stupid hack for when this is called from a script
+if { $argc == 1 } {
+ set argv_tmp [lindex $argv 0]
+ set argv [split $argv_tmp]
+}
+
+if { [llength $argv] > 1 } {
+ set waittimeout [lindex $argv 1]
+} else {
+ set waittimeout 300
+}
+
+set guest [lindex $argv 0]
+set prompt "(%|#|\\\$) $"
+log_user 0
+
+spawn xm shutdown $guest
+set stop_spawn $spawn_id
+
+expect {
+ -i $stop_spawn timeout {
+ send_user "timeout on xm shutdown $guest\n";
+ exit 1;
+ }
+ -i $stop_spawn eof { }
+}
+
+spawn xm console $guest
+set con_spawn $spawn_id
+set timeout $waittimeout
+
+expect {
+ -i $con_spawn "System halted." { exit 0; }
+ -i $con_spawn timeout {
+ send_user "stop timeout\n";
+ exit 1;
+ }
+ -i $con_spawn eof { exit 0; }
+}
+
+exit 1;
diff --git a/distribution/virt/import/zrhel5_write_consolelogs.initd b/distribution/virt/import/zrhel5_write_consolelogs.initd
new file mode 100755
index 0000000..1ec2c8e
--- /dev/null
+++ b/distribution/virt/import/zrhel5_write_consolelogs.initd
@@ -0,0 +1,111 @@
+#!/bin/sh
+#
+# zrhel5_write_consolelogs Agent for reporting virtual guest IDs to subscription-manager
+#
+# chkconfig: 2345 98 99
+# description: Agent for reporting virtual guest IDs to subscription-manager
+
+### BEGIN INIT INFO
+# Provides: zrhel5_write_consolelogs
+# Required-Start: $network libvirtd
+# Required-Stop:
+# Should-Start:
+# Should-Stop:
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: start and stop zrhel5_write_consolelogs
+# Description: Agent for reporting virtual guest IDs to subscription-manager
+### END INIT INFO
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+exec="/usr/local/bin/zrhel5_write_consolelogs"
+prog="zrhel5_write_consolelogs"
+#config="/usr/local/etc/zrhel5_write_consolelogs.conf"
+pidfile="/var/run/$prog.pid"
+args=""
+
+# Export all variables in /etc/sysconfig/zrhel5_write_consolelogs
+set -a
+[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
+# below is where we'll get the LAB_CONTROLLER
+[ -e /etc/profile.d/rh-env.sh ] && . /etc/profile.d/rh-env.sh
+set +a
+
+lockfile=/var/lock/subsys/zrhel5_write_consolelogs
+
+start() {
+ [ -x $exec ] || exit 5
+ echo -n $"Starting $prog: "
+ #daemon --pidfile $pidfile nohup $exec &
+ nohup $exec $args &
+ childpid=$!
+ if kill -0 ${childpid}; then
+ echo
+ touch $lockfile
+ echo "${childpid}" > ${pidfile}
+ return 0
+ else
+ return -1
+ fi
+}
+
+stop() {
+ echo -n $"Stopping $prog: "
+ killproc -p $pidfile $prog
+ retval=$?
+ echo
+ [ $retval -eq 0 ] && rm -f $lockfile
+ return $retval
+}
+
+restart() {
+ stop
+ start
+}
+
+force_reload() {
+ restart
+}
+
+rh_status() {
+ status -p $pidfile $prog
+}
+
+rh_status_q() {
+ rh_status >/dev/null 2>&1
+}
+
+
+case "$1" in
+ start)
+ rh_status_q && exit 0
+ $1
+ ;;
+ stop)
+ rh_status_q || exit 0
+ $1
+ ;;
+ restart)
+ $1
+ ;;
+ reload)
+ rh_status_q || exit 7
+ $1
+ ;;
+ force-reload)
+ force_reload
+ ;;
+ status)
+ rh_status
+ ;;
+ condrestart|try-restart)
+ rh_status_q || exit 0
+ restart
+ ;;
+ *)
+ echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
+ exit 2
+esac
+exit $?
diff --git a/distribution/virt/import/zrhel5_write_consolelogs.py b/distribution/virt/import/zrhel5_write_consolelogs.py
new file mode 100755
index 0000000..6d069f5
--- /dev/null
+++ b/distribution/virt/import/zrhel5_write_consolelogs.py
@@ -0,0 +1,537 @@
+#!/usr/bin/python -u
+#
+#
+#
+#################################################################################
+# Start off by implementing a general purpose event loop for anyones use
+#################################################################################
+
+import sys
+import getopt
+import os
+import libvirt
+import select
+import errno
+import time
+import threading
+import subprocess
+import signal
+import pty
+import fcntl
+from xml.dom import minidom
+from optparse import OptionParser
+
+debugstr = 0
+
+#
+# This general purpose event loop will support waiting for file handle
+# I/O and errors events, as well as scheduling repeatable timers with
+# a fixed interval.
+#
+# It is a pure python implementation based around the poll() API
+#
+class virEventLoopPure:
+ # This class contains the data we need to track for a
+ # single file handle
+ class virEventLoopPureHandle:
+ def __init__(self, handle, fd, events, cb, opaque):
+ self.handle = handle
+ self.fd = fd
+ self.events = events
+ self.cb = cb
+ self.opaque = opaque
+
+ def get_id(self):
+ return self.handle
+
+ def get_fd(self):
+ return self.fd
+
+ def get_events(self):
+ return self.events
+
+ def set_events(self, events):
+ self.events = events
+
+ def dispatch(self, events):
+ self.cb(self.handle,
+ self.fd,
+ events,
+ self.opaque[0],
+ self.opaque[1])
+
+ # This class contains the data we need to track for a
+ # single periodic timer
+ class virEventLoopPureTimer:
+ def __init__(self, timer, interval, cb, opaque):
+ self.timer = timer
+ self.interval = interval
+ self.cb = cb
+ self.opaque = opaque
+ self.lastfired = 0
+
+ def get_id(self):
+ return self.timer
+
+ def get_interval(self):
+ return self.interval
+
+ def set_interval(self, interval):
+ self.interval = interval
+
+ def get_last_fired(self):
+ return self.lastfired
+
+ def set_last_fired(self, now):
+ self.lastfired = now
+
+ def dispatch(self):
+ self.cb(self.timer,
+ self.opaque[0],
+ self.opaque[1])
+
+
+ def __init__(self, debug=False):
+ self.debugOn = debug
+ self.poll = select.poll()
+ self.pipetrick = os.pipe()
+ self.nextHandleID = 1
+ self.nextTimerID = 1
+ self.handles = []
+ self.timers = []
+ self.quit = False
+
+ # The event loop can be used from multiple threads at once.
+ # Specifically while the main thread is sleeping in poll()
+ # waiting for events to occur, another thread may come along
+ # and add/update/remove a file handle, or timer. When this
+ # happens we need to interrupt the poll() sleep in the other
+ # thread, so that it'll see the file handle / timer changes.
+ #
+ # Using OS level signals for this is very unreliable and
+ # hard to implement correctly. Thus we use the real classic
+ # "self pipe" trick. A anonymous pipe, with one end registered
+ # with the event loop for input events. When we need to force
+ # the main thread out of a poll() sleep, we simple write a
+ # single byte of data to the other end of the pipe.
+ self.debug("Self pipe watch %d write %d" %(self.pipetrick[0], self.pipetrick[1]))
+ self.poll.register(self.pipetrick[0], select.POLLIN)
+
+ def debug(self, msg):
+ if self.debugOn:
+ print msg
+
+
+ # Calculate when the next timeout is due to occurr, returning
+ # the absolute timestamp for the next timeout, or 0 if there is
+ # no timeout due
+ def next_timeout(self):
+ next = 0
+ for t in self.timers:
+ last = t.get_last_fired()
+ interval = t.get_interval()
+ if interval < 0:
+ continue
+ if next == 0 or (last + interval) < next:
+ next = last + interval
+
+ return next
+
+ # Lookup a virEventLoopPureHandle object based on file descriptor
+ def get_handle_by_fd(self, fd):
+ for h in self.handles:
+ if h.get_fd() == fd:
+ return h
+ return None
+
+ # Lookup a virEventLoopPureHandle object based on its event loop ID
+ def get_handle_by_id(self, handleID):
+ for h in self.handles:
+ if h.get_id() == handleID:
+ return h
+ return None
+
+
+ # This is the heart of the event loop, performing one single
+ # iteration. It asks when the next timeout is due, and then
+ # calcuates the maximum amount of time it is able to sleep
+ # for in poll() pending file handle events.
+ #
+ # It then goes into the poll() sleep.
+ #
+ # When poll() returns, there will zero or more file handle
+ # events which need to be dispatched to registered callbacks
+ # It may also be time to fire some periodic timers.
+ #
+ # Due to the coarse granularity of schedular timeslices, if
+ # we ask for a sleep of 500ms in order to satisfy a timer, we
+ # may return upto 1 schedular timeslice early. So even though
+ # our sleep timeout was reached, the registered timer may not
+ # technically be at its expiry point. This leads to us going
+ # back around the loop with a crazy 5ms sleep. So when checking
+ # if timeouts are due, we allow a margin of 20ms, to avoid
+ # these pointless repeated tiny sleeps.
+ def run_once(self):
+ sleep = -1
+ next = self.next_timeout()
+ self.debug("Next timeout due at %d" % next)
+ if next > 0:
+ now = int(time.time() * 1000)
+ if now >= next:
+ sleep = 0
+ else:
+ sleep = (next - now) / 1000.0
+
+ self.debug("Poll with a sleep of %d" % sleep)
+ events = self.poll.poll(sleep)
+
+ # Dispatch any file handle events that occurred
+ for (fd, revents) in events:
+ # See if the events was from the self-pipe
+ # telling us to wakup. if so, then discard
+ # the data just continue
+ if fd == self.pipetrick[0]:
+ data = os.read(fd, 1)
+ continue
+
+ h = self.get_handle_by_fd(fd)
+ if h:
+ self.debug("Dispatch fd %d handle %d events %d" % (fd, h.get_id(), revents))
+ h.dispatch(self.events_from_poll(revents))
+
+ now = int(time.time() * 1000)
+ for t in self.timers:
+ interval = t.get_interval()
+ if interval < 0:
+ continue
+
+ want = t.get_last_fired() + interval
+ # Deduct 20ms, since schedular timeslice
+ # means we could be ever so slightly early
+ if now >= (want-20):
+ self.debug("Dispatch timer %d now %s want %s" % (t.get_id(), str(now), str(want)))
+ t.set_last_fired(now)
+ t.dispatch()
+
+
+ # Actually the event loop forever
+ def run_loop(self):
+ self.quit = False
+ while not self.quit:
+ self.run_once()
+
+ def interrupt(self):
+ os.write(self.pipetrick[1], 'c')
+
+
+ # Registers a new file handle 'fd', monitoring for 'events' (libvirt
+ # event constants), firing the callback cb() when an event occurs.
+ # Returns a unique integer identier for this handle, that should be
+ # used to later update/remove it
+ def add_handle(self, fd, events, cb, opaque):
+ handleID = self.nextHandleID + 1
+ self.nextHandleID = self.nextHandleID + 1
+
+ h = self.virEventLoopPureHandle(handleID, fd, events, cb, opaque)
+ self.handles.append(h)
+
+ self.poll.register(fd, self.events_to_poll(events))
+ self.interrupt()
+
+ self.debug("Add handle %d fd %d events %d" % (handleID, fd, events))
+
+ return handleID
+
+ # Registers a new timer with periodic expiry at 'interval' ms,
+ # firing cb() each time the timer expires. If 'interval' is -1,
+ # then the timer is registered, but not enabled
+ # Returns a unique integer identier for this handle, that should be
+ # used to later update/remove it
+ def add_timer(self, interval, cb, opaque):
+ timerID = self.nextTimerID + 1
+ self.nextTimerID = self.nextTimerID + 1
+
+ h = self.virEventLoopPureTimer(timerID, interval, cb, opaque)
+ self.timers.append(h)
+ self.interrupt()
+
+ self.debug("Add timer %d interval %d" % (timerID, interval))
+
+ return timerID
+
+ # Change the set of events to be monitored on the file handle
+ def update_handle(self, handleID, events):
+ h = self.get_handle_by_id(handleID)
+ if h:
+ h.set_events(events)
+ self.poll.unregister(h.get_fd())
+ self.poll.register(h.get_fd(), self.events_to_poll(events))
+ self.interrupt()
+
+ self.debug("Update handle %d fd %d events %d" % (handleID, h.get_fd(), events))
+
+ # Change the periodic frequency of the timer
+ def update_timer(self, timerID, interval):
+ for h in self.timers:
+ if h.get_id() == timerID:
+ h.set_interval(interval);
+ self.interrupt()
+
+ self.debug("Update timer %d interval %d" % (timerID, interval))
+ break
+
+ # Stop monitoring for events on the file handle
+ def remove_handle(self, handleID):
+ handles = []
+ for h in self.handles:
+ if h.get_id() == handleID:
+ self.poll.unregister(h.get_fd())
+ self.debug("Remove handle %d fd %d" % (handleID, h.get_fd()))
+ else:
+ handles.append(h)
+ self.handles = handles
+ self.interrupt()
+
+ # Stop firing the periodic timer
+ def remove_timer(self, timerID):
+ timers = []
+ for h in self.timers:
+ if h.get_id() != timerID:
+ timers.append(h)
+ self.debug("Remove timer %d" % timerID)
+ self.timers = timers
+ self.interrupt()
+
+ # Convert from libvirt event constants, to poll() events constants
+ def events_to_poll(self, events):
+ ret = 0
+ if events & libvirt.VIR_EVENT_HANDLE_READABLE:
+ ret |= select.POLLIN
+ if events & libvirt.VIR_EVENT_HANDLE_WRITABLE:
+ ret |= select.POLLOUT
+ if events & libvirt.VIR_EVENT_HANDLE_ERROR:
+ ret |= select.POLLERR;
+ if events & libvirt.VIR_EVENT_HANDLE_HANGUP:
+ ret |= select.POLLHUP;
+ return ret
+
+ # Convert from poll() event constants, to libvirt events constants
+ def events_from_poll(self, events):
+ ret = 0;
+ if events & select.POLLIN:
+ ret |= libvirt.VIR_EVENT_HANDLE_READABLE;
+ if events & select.POLLOUT:
+ ret |= libvirt.VIR_EVENT_HANDLE_WRITABLE;
+ if events & select.POLLNVAL:
+ ret |= libvirt.VIR_EVENT_HANDLE_ERROR;
+ if events & select.POLLERR:
+ ret |= libvirt.VIR_EVENT_HANDLE_ERROR;
+ if events & select.POLLHUP:
+ ret |= libvirt.VIR_EVENT_HANDLE_HANGUP;
+ return ret;
+
+
+###########################################################################
+# Now glue an instance of the general event loop into libvirt's event loop
+###########################################################################
+
+# This single global instance of the event loop wil be used for
+# monitoring libvirt events
+eventLoop = virEventLoopPure(debug=False)
+
+# This keeps track of what thread is running the event loop,
+# (if it is run in a background thread)
+eventLoopThread = None
+
+
+# These next set of 6 methods are the glue between the official
+# libvirt events API, and our particular impl of the event loop
+#
+# There is no reason why the 'virEventLoopPure' has to be used.
+# An application could easily may these 6 glue methods hook into
+# another event loop such as GLib's, or something like the python
+# Twisted event framework.
+
+def virEventAddHandleImpl(fd, events, cb, opaque):
+ global eventLoop
+ return eventLoop.add_handle(fd, events, cb, opaque)
+
+def virEventUpdateHandleImpl(handleID, events):
+ global eventLoop
+ return eventLoop.update_handle(handleID, events)
+
+def virEventRemoveHandleImpl(handleID):
+ global eventLoop
+ return eventLoop.remove_handle(handleID)
+
+def virEventAddTimerImpl(interval, cb, opaque):
+ global eventLoop
+ return eventLoop.add_timer(interval, cb, opaque)
+
+def virEventUpdateTimerImpl(timerID, interval):
+ global eventLoop
+ return eventLoop.update_timer(timerID, interval)
+
+def virEventRemoveTimerImpl(timerID):
+ global eventLoop
+ return eventLoop.remove_timer(timerID)
+
+# This tells libvirt what event loop implementation it
+# should use
+def virEventLoopPureRegister():
+ libvirt.virEventRegisterImpl(virEventAddHandleImpl,
+ virEventUpdateHandleImpl,
+ virEventRemoveHandleImpl,
+ virEventAddTimerImpl,
+ virEventUpdateTimerImpl,
+ virEventRemoveTimerImpl)
+
+# Directly run the event loop in the current thread
+def virEventLoopPureRun():
+ global eventLoop
+ eventLoop.run_loop()
+
+# Spawn a background thread to run the event loop
+def virEventLoopPureStart():
+ global eventLoopThread
+ virEventLoopPureRegister()
+ eventLoopThread = threading.Thread(target=virEventLoopPureRun, name="libvirtEventLoop")
+ eventLoopThread.setDaemon(True)
+ eventLoopThread.start()
+
+
+##########################################################################
+# Everything that now follows is a simple demo of domain lifecycle events
+##########################################################################
+def eventToString(event):
+ eventStrings = ( "Defined",
+ "Undefined",
+ "Started",
+ "Suspended",
+ "Resumed",
+ "Stopped" );
+ return eventStrings[event];
+
+def detailToString(event, detail):
+ eventStrings = (
+ ( "Added", "Updated" ),
+ ( "Removed" ),
+ ( "Booted", "Migrated", "Restored", "Snapshot" ),
+ ( "Paused", "Migrated", "IOError", "Watchdog" ),
+ ( "Unpaused", "Migrated"),
+ ( "Shutdown", "Destroyed", "Crashed", "Migrated", "Saved", "Failed", "Snapshot")
+ )
+ return eventStrings[event][detail]
+
+def readconsoleandsave (domainname):
+ global testdir
+ filename = testdir+"/guests/"+domainname+"/logs/"+domainname+"_console.log"
+ args = ["/usr/bin/virsh"]
+ args = args + [ "console", "%s" % domainname]
+ console_fd = open(filename, "a+")
+ console_fid = console_fd.fileno()
+ fds[domainname] = console_fd
+ (child, fd) = pty.fork()
+ if child:
+ try:
+ flags = fcntl.fcntl(fd, fcntl.F_GETFD)
+ fcntl.fcntl(fd, fcntl.F_SETFD, flags | fcntl.FD_CLOEXEC)
+ except Exception, e:
+ print e
+ debugprint("Forked %s %s" % (args[0], args))
+ else:
+ os.dup2(console_fid, 1)
+ time.sleep(1)
+ os.execvp(args[0], args)
+ os._exit(1)
+
+ #print "child is : %d " % child
+ return child
+
+def myDomainEventCallback1 (conn, dom, event, detail, opaque):
+ #print "name: %s event: %s " % ( dom.name(), eventToString(event) )
+ global pids
+ if eventToString(event) == "Started":
+ debugprint("%s started, will call readconsoleandsave" % dom.name())
+ pids[dom.name()] = readconsoleandsave(dom.name())
+ #print "pids[%s] is %s " % (dom.name(), pids[dom.name()])
+ elif eventToString(event) == "Stopped":
+ if dom.name() in fds:
+ fds[dom.name()].close()
+ if dom.name() in pids:
+ os.kill(pids[dom.name()],signal.SIGKILL)
+
+def usage():
+ print "usage: "+os.path.basename(sys.argv[0])+" [uri]"
+ print " uri will default to qemu:///system"
+
+def debugprint(str):
+ global debugstr
+ if (debugstr):
+ print "%s" % str
+
+
+def main():
+ global testdir
+ #try:
+ # opts, args = getopt.getopt(sys.argv[1:], "h", ["help"] )
+ #except getopt.GetoptError, err:
+ # # print help information and exit:
+ # print str(err) # will print something like "option -a not recognized"
+ # usage()
+ # sys.exit(2)
+ parser = OptionParser(conflict_handler="resolve")
+ parser.add_option("-t" ,"--testdir", dest="testdir", help="Root dir for logs",
+ default="/mnt/tests/distribution/virt/install", metavar="PATH")
+ parser.add_option("-h" ,"--help", dest="help", help="Display help", metavar="HELP")
+ parser.add_option("-d" ,"--debug", dest="debug", help="Print debug",
+ action="store_false", default=False, metavar="DEBUG")
+ (options, args) = parser.parse_args()
+
+ if options.help:
+ usage()
+ sys.exit(0)
+
+ if options.debug:
+ debugstr = 1
+
+ if options.testdir:
+ testdir = options.testdir
+
+ if len(args) > 0:
+ uri = args[0]
+ else:
+ #uri = "qemu:///system"
+ uri = subprocess.Popen(["virsh", "uri"], stdout=subprocess.PIPE).communicate()[0].strip()
+
+ print "Using uri: " + uri
+
+ # Run a background thread with the event loop
+ virEventLoopPureStart()
+
+ vc = libvirt.open(uri)
+
+ # Close connection on exit (to test cleanup paths)
+ old_exitfunc = getattr(sys, 'exitfunc', None)
+ def exit():
+ print "Closing " + str(vc)
+ vc.close()
+ if (old_exitfunc): old_exitfunc()
+ sys.exitfunc = exit
+
+ #Add 2 callbacks to prove this works with more than just one
+ vc.domainEventRegister(myDomainEventCallback1,None)
+
+ # The rest of your app would go here normally, but for sake
+ # of demo we'll just go to sleep. The other option is to
+ # run the event loop in your main thread if your app is
+ # totally event based.
+ while 1:
+ time.sleep(1)
+
+
+if __name__ == "__main__":
+ pids = { }
+ fds = { }
+ path = ""
+ main()