On 06/28/2011 01:54 PM, Guannan.ren wrote:
---
repos/domain/migrate.py | 335 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 335 insertions(+), 0 deletions(-)
create mode 100644 repos/domain/migrate.py
diff --git a/repos/domain/migrate.py b/repos/domain/migrate.py
new file mode 100644
index 0000000..3f80bd0
--- /dev/null
+++ b/repos/domain/migrate.py
@@ -0,0 +1,335 @@
+#!/usr/bin/env python
+"""this script is for migration testing
+ domain:migrate
+ target_machine
+ 10.66.5.5
+ username
+ root
+ password
+ redhat
+ guestname
+ rhel6
+ prestate
+ running
+ poststate
+ running
+ presrcconfig
+ false
+ postsrcconfig
+ false
+ predstconfig
+ false
+ postdstconfig
+ false
+ flags
+ 0|live
+
+prestate and poststate is the domain state:<running|paused>
+presrconfig, postsrconfig, predstconfig, postdstconfig is<true|false>
+flags is the migration flags combination<0|peer2peer|tunnelled|live|paused \
+ |persist_dest|undefine_source|>
+
+"""
+__author__ = 'Guannan Ren: gren(a)redhat.com'
+__date__ = 'Sun June 26, 2011'
+__version__ = '0.1.0'
+__credits__ = 'Copyright (C) 2011 Red Hat, Inc.'
+__all__ = ['usage', 'migrate']
+
+import os
+import re
+import sys
+import pexpect
+import string
+import commands
+
+def append_path(path):
+ """Append root path of package"""
+ if path in sys.path:
+ pass
+ else:
+ sys.path.append(path)
+
+pwd = os.getcwd()
+result = re.search('(.*)libvirt-test-API', pwd)
+append_path(result.group(0))
+
+from lib import connectAPI
+from lib.domainAPI import *
+from utils.Python import utils
+from utils.Python import xmlbuilder
+from exception import LibvirtAPI
+
+SSH_KEYGEN = "ssh-keygen -t rsa"
+SSH_COPY_ID = "ssh-copy-id"
+GUEST_XML = "/etc/libvirt/qemu/%s.xml"
+
+def exec_command(logger, command, flag):
+ """execute shell command
+ """
+ status, ret = commands.getstatusoutput(command)
+ if not flag and status:
+ logger.error("executing "+ "\"" + command +
"\"" + " failed")
+ logger.error(ret)
+ return status, ret
+
+
+def env_clean(src, dst, srcdom, dstdom, target_machine, guestname, logger):
+
+ logger.info("destroy and undefine %s on both side if it exsits",
guestname)
+ exec_command(logger, "virsh destroy %s" % guestname, 1)
+ exec_command(logger, "virsh undefine %s" % guestname, 1)
+ REMOTE_DESTROY = "ssh %s \"virsh destroy %s\"" %
(target_machine, guestname)
+ exec_command(logger, REMOTE_DESTROY, 1)
+ REMOTE_UNDEFINE = "ssh %s \"virsh undefine %s\"" %
(target_machine, guestname)
+ exec_command(logger, REMOTE_UNDEFINE, 1)
+
+ src.close()
+ logger.info("close local hypervisor connection")
+ dst.close()
+ logger.info("close remote hypervisor connection")
+
+ REMOVE_SSH = "ssh %s \"rm -rf /root/.ssh/*\"" %
(target_machine)
+ logger.info("remove ssh key on remote machine")
+ status, ret = exec_command(logger, REMOVE_SSH, 0)
+ if status:
+ logger.error("failed to remove ssh key")
+
+ REMOVE_LOCAL_SSH = "rm -rf /root/.ssh/*"
+ logger.info("remove local ssh key")
+ status, ret = exec_command(logger, REMOVE_LOCAL_SSH, 0)
+ if status:
+ logger.error("failed to remove local ssh key")
+
+
+def check_params(params):
+ """check out the arguments requried for migration"""
+ logger = params['logger']
+ keys = ['target_machine', 'username', 'password',
'guestname', 'flags']
+ for key in keys:
+ if key not in params:
+ logger.error("Argument %s is required" % key)
+ return 1
+ return 0
+
+def ssh_keygen(logger):
+ """using pexpect to generate RSA"""
+ logger.info("generate ssh RSA \"%s\"" % SSH_KEYGEN)
+ child = pexpect.spawn(SSH_KEYGEN)
+ while True:
+ index = child.expect(['Enter file in which to save the key ',
+ 'Enter passphrase ',
+ 'Enter same passphrase again: ',
+ pexpect.EOF,
+ pexpect.TIMEOUT])
+ if index == 0:
+ child.sendline("\r")
+ elif index == 1:
+ child.sendline("\r")
+ elif index == 2:
+ child.sendline("\r")
+ elif index == 3:
+ logger.debug(string.strip(child.before))
+ child.close()
+ return 0
+ elif index == 4:
+ logger.error("ssh_keygen timeout")
+ logger.debug(string.strip(child.before))
+ child.close()
+ return 1
+
+ return 0
+
+def ssh_tunnel(hostname, username, password, logger):
+ """setup a tunnel to a give host"""
+ logger.info("setup ssh tunnel with host %s" % hostname)
+ user_host = "%s@%s" % (username, hostname)
+ child = pexpect.spawn(SSH_COPY_ID, [ user_host])
+ while True:
+ index = child.expect(['yes\/no', 'password: ',
+ pexpect.EOF,
+ pexpect.TIMEOUT])
+ if index == 0:
+ child.sendline("yes")
+ elif index == 1:
+ child.sendline(password)
+ elif index == 2:
+ logger.debug(string.strip(child.before))
+ child.close()
+ return 0
+ elif index == 3:
+ logger.error("setup tunnel timeout")
+ logger.debug(string.strip(child.before))
+ child.close()
+ return 1
+
+ return 0
+
+def remote_guest_define(target_machine, username, guestname, logger):
+ """copy guest xml description to target machine and define
it"""
+ xml_file = GUEST_XML % guestname
+
+ if not os.path.exists(xml_file):
+ logger.error("guest %s xml file doesn't exsits" % guestname)
+ return 1
+
+ SCP_CMD = "scp %s %s@%s:/tmp" %(xml_file, username, target_machine)
+ status, ret = exec_command(logger, SCP_CMD, 0)
+ if status:
+ logger.error("copy guest file failed")
+ return 1
+
+ VIRSH_DEFINE = "ssh %s \"virsh define /tmp/%s.xml\"" %
(target_machine, guestname)
+ status, ret = exec_command(logger, VIRSH_DEFINE, 0)
+ if status:
+ logger.error("faied to define guest on target machine")
+ return 1
+
+ return 0
+
+def migrate(params):
+ """ migrate a guest back and forth between two
machines"""
+ logger = params['logger']
+ params_check_result = check_params(params)
+ if params_check_result:
+ return 1
+
+ target_machine = params['target_machine']
+ username = params['username']
+ password = params['password']
+ guestname = params['guestname']
+ poststate = params['poststate']
+ presrcconfig = params['presrcconfig']
+ postsrcconfig = params['postsrcconfig']
+ predstconfig = params['predstconfig']
+ postdstconfig = params['postdstconfig']
+ flags = params['flags']
+
+
+ logger.info("the flags is %s" % flags)
+ flags_string = flags.split("|")
+
+ migflags = 0
+ for flag in flags_string:
+ if flag == '0':
+ migflags |= 0
+ elif flag == 'peer2peer':
+ migflags |= VIR_MIGRATE_PEER2PEER
+ elif flag == 'tunnelled':
+ migflags |= VIR_MIGRATE_TUNNELLED
+ elif flag == 'live':
+ migflags |= VIR_MIGRATE_LIVE
+ elif flag == 'persist_dest':
+ migflags |= VIR_MIGRATE_PERSIST_DEST
+ elif flag == 'undefine_source':
+ migflags |= VIR_MIGRATE_UNDEFINE_SOURCE
+ elif flag == 'paused':
+ migflags |= VIR_MIGRATE_PAUSED
+ else:
+ logger.error("unknown flag")
+ return 1
+
+ #generate ssh key pair
+ ret = ssh_keygen(logger)
+ if ret:
+ logger.error("failed to generate RSA key")
+ return 1
+ #setup ssh tunnel with target machine
+ ret = ssh_tunnel(target_machine, username, password, logger)
+ if ret:
+ logger.error("faild to setup ssh tunnel with target machine %s" %
target_machine)
+ return 1
+
+ commands.getstatusoutput("ssh-add")
+
+ srcuri = "qemu:///system"
+ dsturi = "qemu+ssh://%s/system" % target_machine
+
+ # Connect to local hypervisor connection URI
+ util = utils.Utils()
+ conn = connectAPI.ConnectAPI()
+ src = conn.open(srcuri)
+ dst = conn.open(dsturi)
+
+ srcdom = DomainAPI(src)
+ dstdom = DomainAPI(dst)
+
+ if predstconfig == "true":
+ ret = remote_guest_define(target_machine, username, guestname, logger)
+ if ret:
+ env_clean(src, dst, srcdom, dstdom, target_machine, guestname, logger)
+ return 1
+
+
+ try:
+ if(migflags& VIR_MIGRATE_PEER2PEER):
+ logger.info("use migrate_to_uri() API to migrate")
+ srcdom.migrate_to_uri(guestname, dsturi, migflags)
+ else:
+ logger.info("use migrate() to migrate")
+ srcdom.migrate(guestname, dst, migflags)
+ except LibvirtAPI, e:
+ logger.error("API error message: %s, error code is %s" % \
+ (e.response()['message'], e.response()['code']))
+ logger.error("Migration Failed")
+ env_clean(src, dst, srcdom, dstdom, target_machine, guestname, logger)
+ return 1
+
+ if postsrcconfig == "true":
+ if srcdom.is_active(guestname):
+ logger.error("Source VM is still active")
+ env_clean(src, dst, srcdom, dstdom, target_machine, guestname, logger)
+ return 1
+ if not srcdom.is_persistent(guestname):
+ logger.error("Source VM missing config")
+ env_clean(src, dst, srcdom, dstdom, target_machine, guestname, logger)
+ return 1
+ else:
+ guest_names = srcdom.get_list()
+ guest_names += srcdom.get_defined_list()
+ if guestname in guest_names:
+ logger.error("Source VM still exists")
+ env_clean(src, dst, srcdom, dstdom, target_machine, guestname, logger)
+ return 1
+
+ if not dstdom.is_active(guestname):
+ logger.error("Dst VM is not active")
+ env_clean(src, dst, srcdom, dstdom, target_machine, guestname, logger)
+ return 1
+
+ if postdstconfig == "true":
+ if not dstdom.is_persistent(guestname):
+ logger.error("Dst VM missing config")
+ env_clean(src, dst, srcdom, dstdom, target_machine, guestname, logger)
+ return 1
+
+ dstdom_state = dstdom.get_state(guestname)
+ if dstdom_state != poststate:
+ logger.error("Dst VM wrong state %s, should be %s", dstdom_state,
poststate)
+ env_clean(src, dst, srcdom, dstdom, target_machine, guestname, logger)
+ return 1
+
+ logger.info("Migration PASS")
+ env_clean(src, dst, srcdom, dstdom, target_machine, guestname, logger)
+ return 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ACK, but too many redundant blank lines at the end of file.
- nzhang