Hi Chris: I got all the comments, thanks. :) As you said, it is a really complicate patch. Fortunately, most of the points are not big ones because it won't raise problem for now. So if you don't mind, would you please apply it first ? So that I don't need to do the whole patch again.:) After that, I will send a much more simple patch to fix all that you said.
Thanks. :) On 07/03/2012 10:04 PM, Chris Evich wrote: > > Cool, okay, taking a fresh look by trying to pretend I've never seen this > code before... > > On 06/29/2012 03:56 AM, tangchen wrote: >> Signed-off-by: Tang Chen<tanc...@cn.fujitsu.com> >> --- >> client/tests/libvirt/tests/virsh_migrate.py | 267 >> ++++++++++++++++++++++----- >> 1 files changed, 223 insertions(+), 44 deletions(-) >> >> diff --git a/client/tests/libvirt/tests/virsh_migrate.py >> b/client/tests/libvirt/tests/virsh_migrate.py >> index b342987..16fd298 100644 >> --- a/client/tests/libvirt/tests/virsh_migrate.py >> +++ b/client/tests/libvirt/tests/virsh_migrate.py >> @@ -1,5 +1,6 @@ >> -import logging, time >> -from autotest.client.shared import error >> +import logging, os, re, time, shutil, codecs >> +from autotest.client.shared import utils, error >> +from autotest.client.virt import libvirt_vm, virt_utils, virt_test_utils >> >> def run_virsh_migrate(test, params, env): >> """ >> @@ -16,51 +17,69 @@ def run_virsh_migrate(test, params, env): >> else: >> return False >> >> - def cleanup_dest(vm, src_uri = ""): >> + def cleanup_dest(vm, src_uri=""): > > Good, better style. > >> """ >> Clean up the destination host environment >> when doing the uni-direction migration. >> """ >> - vm_state = vm.state() >> - if vm_state == "running": >> - vm.destroy() >> - elif vm_state == "paused": >> - vm.resume() >> - vm.destroy() >> + logging.info("Cleaning up VMs on %s" % vm.connect_uri) >> + try: >> + if libvirt_vm.virsh_domain_exists(vm.name, vm.connect_uri): >> + vm_state = vm.state() >> + if vm_state == "paused": >> + vm.resume() >> + elif vm_state == "shut off": >> + vm.start() >> + vm.destroy() >> + >> + if vm.is_persistent(): >> + vm.undefine() >> >> - if vm.is_persistent(): >> - vm.undefine() >> + except Exception, detail: >> + logging.error("Cleaning up destination failed.\n%s" % detail) >> >> - vm.connect_uri = src_uri >> + if src_uri: >> + vm.connect_uri = src_uri > > > Okay, looking at this fresh, I like the new way. I was concerned for a > moment throwing exception but not raising another would cause problem. > However, assuming cleanup_dest() is only even done for uni-directional > testing, I think it's okay. If there's a problem, the next attempt to > migrate should fail, and we'll have logs to see what happened. Good. > >> >> - def do_migration(dly, vm, dest_uri, options, extra): >> - logging.info("Sleeping %d seconds before migration" % dly) >> - time.sleep(dly) >> + def do_migration(delay, vm, dest_uri, options, extra): >> + logging.info("Sleeping %d seconds before migration" % delay) >> + time.sleep(delay) >> # Migrate the guest. >> - successful = vm.migrate(dest_uri, options, extra) >> - if not successful: >> - raise error.TestFail("Migration failed for %s." % vm_name) >> + successful = vm.migrate(dest_uri, options, extra, True, >> True).exit_status >> + logging.info("successful: %d", successful) >> + if int(successful) != 0: >> + logging.error("Migration failed for %s." % vm_name) >> + return False >> + >> + if options.count("dname") or extra.count("dname"): >> + vm.name = extra.split()[1].strip() >> > > I'm sorry if I recommended this, I think I suggested a mistake :S This will > only work if --dname is always the first option/value. However, I'm inclined > to accept it since all the test configurations are setup this way. Something > we can examine further later, let's not worry about it for now. > >> if vm.is_alive(): # vm.connect_uri was updated >> logging.info("Alive guest found on destination %s." % dest_uri) >> else: >> - raise error.TestFail("VM not running on destination %s" % >> dest_uri) >> - >> - # Migration may fail, but VM is alive on destination. >> - dest_state = params.get("virsh_migrate_dest_state") >> - ret = check_vm_state(vm, dest_state) >> - logging.info("Supposed state: %s" % dest_state) >> - logging.info("Actual state: %s" % vm.state()) >> - if not ret: >> - raise error.TestFail("VM is not in the supposed state.") >> + logging.error("VM not alive on destination %s" % dest_uri) >> + return False >> >> # FIXME: This needs to be tested, but won't work currently >> # vm.verify_kernel_crash() >> + logging.debug("vm.verify_kernel_crash() needs to be tested, " >> + "but won't work currently.") > > Bah, right, this is still broken for libvirt. Thanks for the reminder :D > >> + return True >> + >> >> vm_name = params.get("main_vm") >> vm = env.get_vm(params["main_vm"]) >> vm.verify_alive() >> >> + # For safety reasons, we'd better back up xmlfile. >> + vm_xmlfile_bak = vm.backup_xml() >> + if not vm_xmlfile_bak: >> + logging.error("Backing up xmlfile failed.") >> + >> + vm.connect_uri = params.get("connect_uri", "default") >> + if vm.connect_uri == 'default': >> + vm.connect_uri = libvirt_vm.virsh_uri() >> + > > Looks good. > >> src_uri = vm.connect_uri >> dest_uri = params.get("virsh_migrate_desturi") >> # Identify easy config. mistakes early >> @@ -75,24 +94,184 @@ def run_virsh_migrate(test, params, env): >> if dest_uri.count('///') or dest_uri.count('EXAMPLE'): >> logging.warning(warning_text % ('destination', dest_uri)) >> >> + vm_ref = params.get("vm_ref", vm.name) >> options = params.get("virsh_migrate_options") >> extra = params.get("virsh_migrate_extra") >> - dly = int(params.get("virsh_migrate_delay", 10)) >> - >> - >> - do_migration(dly, vm, dest_uri, options, extra) >> - # Repeat the migration with a recursive call and guaranteed exit >> - if params.get("virsh_migrate_back", "no") == 'yes': >> - back_dest_uri = params.get("virsh_migrate_back_desturi", 'default') >> - back_options = params.get("virsh_migrate_back_options", 'default') >> - back_extra = params.get("virsh_migrate_back_extra", 'default') >> - if back_dest_uri == 'default': >> - back_dest_uri = src_uri >> - if back_options == 'default': >> - back_options = options >> - if back_extra == 'default': >> - back_extra = extra >> - do_migration(dly, vm, back_dest_uri, back_options, back_extra) >> - # Do the uni-direction migration here. >> + delay = int(params.get("virsh_migrate_delay", 10)) >> + status_error = params.get("status_error", 'no') >> + libvirtd_state = params.get("virsh_migrate_libvirtd_state", 'on') >> + src_state = params.get("virsh_migrate_src_state", "running") >> + new_nic_mac = "ff:ff:ff:ff:ff:ff" >> + dest_xmlfile = "" >> + >> + exception = False >> + try: >> + # Confirm VM can be accessed through network. >> + time.sleep(delay) >> + vm_ip = vm.get_address() >> + s_ping, o_ping = virt_test_utils.ping(vm_ip, count=2, timeout=delay) >> + logging.info(o_ping) >> + if s_ping != 0: >> + raise error.TestError("%s did not respond after %d sec." % >> (vm.name, delay)) >> + >> + # Prepare for --xml. >> + logging.debug("Preparing new xml file for --xml option.") >> + if options.count("xml") or extra.count("xml"): >> + dest_xmlfile = params.get("virsh_migrate_xml", "") >> + if dest_xmlfile: >> + ret_attach = vm.attach_interface("--type bridge --source >> virbr0 --mac %s" % new_nic_mac, True, True) >> + if not ret_attach: >> + exception = True >> + raise error.TestError("Attaching nic to %s failed." % >> vm.name) >> + vm_xml_new = vm.get_xml() >> + logging.debug("Xml file on source: %s" % vm_xml_new) >> + f = codecs.open(dest_xmlfile, 'wb', encoding='utf-8') >> + f.write(vm_xml_new) >> + f.close() >> + if not os.path.exists(dest_xmlfile): >> + exception = True >> + raise error.TestError("Creating %s failed." % >> dest_xmlfile) >> + >> + # Turn VM into certain state. >> + logging.debug("Turning %s into certain state." % vm.name) >> + if src_state == "paused": >> + if vm.is_alive(): >> + vm.pause() >> + elif src_state == "shut off": >> + if vm.is_alive(): >> + if not vm.shutdown(): >> + vm.destroy() > > For future reference, I think you can do vm.destroy(gracefully=True), but > this is fine for now. > >> + >> + # Turn libvirtd into certain state. >> + logging.debug("Turning libvirtd into certain status.") >> + if libvirtd_state == "off": >> + libvirt_vm.libvirtd_stop() >> + >> + # Test uni-direction migration. >> + logging.debug("Doing migration test.") >> + if vm_ref != vm_name: >> + vm.name = vm_ref # For vm name error testing. > > Ahh, I was just wondering what vm_ref was for. Good putting comment here. > >> + ret_migrate = do_migration(delay, vm, dest_uri, options, extra) >> + if vm_ref != vm_name: >> + vm.name = vm_name >> + >> + # Recover libvirtd state. >> + logging.debug("Recovering libvirtd status.") >> + if libvirtd_state == "off": >> + libvirt_vm.libvirtd_start() >> + >> + # Check vm state on destination. >> + logging.debug("Checking %s state on %s." % (vm.name, >> vm.connect_uri)) >> + if options.count("dname") or extra.count("dname"): >> + vm.name = extra.split()[1].strip() > > For future reference, if you have to code the same thing twice, it's a "clue" > that you maybe need to just define a function/method. However, as noted > above, it would be "nice" to have a more robust option-checker here, but this > is okay for now. > >> + check_dest_state = True >> + dest_state = params.get("virsh_migrate_dest_state", "running") >> + check_dest_state = check_vm_state(vm, dest_state) >> + logging.info("Supposed state: %s" % dest_state) >> + logging.info("Actual state: %s" % vm.state()) >> + >> + # Recover VM state. >> + logging.debug("Recovering %s state." % vm.name) >> + if src_state == "paused": >> + vm.resume() >> + elif src_state == "shut off": >> + vm.start() >> + >> + # Checking for --persistent. >> + logging.debug("Checking for --persistent option.") >> + check_dest_persistent = True >> + if options.count("persistent") or extra.count("persistent"): >> + if not vm.is_persistent(): >> + check_dest_persistent = False >> + >> + # Checking for --undefinesource. >> + logging.debug("Checking for --undefinesource option.") >> + check_src_undefine = True >> + if options.count("undefinesource") or extra.count("undefinesource"): >> + logging.info("Verifying<virsh domstate> DOES return an error." >> + "%s should not exist on %s." % (vm_name, src_uri)) >> + if libvirt_vm.virsh_domain_exists(vm_name, src_uri): >> + check_src_undefine = False >> + >> + # Checking for --dname. >> + logging.debug("Checking for --dname option.") >> + check_dest_dname = True >> + if options.count("dname") or extra.count("dname"): >> + dname = extra.split()[1].strip() >> + if not libvirt_vm.virsh_domain_exists(dname, dest_uri): >> + check_dest_dname = False >> + >> + # Checking for --xml. >> + logging.debug("Checking for --xml option.") >> + check_dest_xml = True >> + if options.count("xml") or extra.count("xml"): >> + if dest_xmlfile: >> + vm_dest_xml = vm.get_xml() >> + logging.info("Xml file on destination: %s" % vm_dest_xml) >> + if not re.search(new_nic_mac, vm_dest_xml): >> + check_dest_xml = False >> + >> + # Repeat the migration from destination to source. >> + if params.get("virsh_migrate_back", "no") == 'yes': >> + back_dest_uri = params.get("virsh_migrate_back_desturi", >> 'default') >> + back_options = params.get("virsh_migrate_back_options", >> 'default') >> + back_extra = params.get("virsh_migrate_back_extra", 'default') >> + if back_dest_uri == 'default': >> + back_dest_uri = src_uri >> + if back_options == 'default': >> + back_options = options >> + if back_extra == 'default': >> + back_extra = extra >> + ret_migrate = do_migration(delay, vm, back_dest_uri, >> back_options, back_extra) >> + >> + except Exception, detail: >> + exception = True >> + logging.error("%s: %s" % (detail.__class__, detail)) >> + >> + >> + # Whatever error occurs, we have to clean up all environment. >> + # Make sure vm.connect_uri is the destination uri. >> + vm.connect_uri = dest_uri >> + if options.count("dname") or extra.count("dname"): >> + # Use the VM object to remove >> + vm.name = extra.split()[1].strip() > > Ahh, three times, see it would help to have a function to do this :) It's > okay for now though since it's such a simplistic check. > >> + cleanup_dest(vm, src_uri) >> + vm.name = vm_name >> else: >> cleanup_dest(vm, src_uri) >> + >> + # Recover source (just in case). >> + # vm.connect_uri has been set back to src_uri in cleanup_dest(). > > Good to put this comment here, guarantee no confusion. > >> + if not libvirt_vm.virsh_domain_exists(vm_name, src_uri): >> + vm.define(vm_xmlfile_bak) > > This we can improve with my new xml_utils class, it will automatically take > backup. What you have here is fine for now since my code is still > experimental. > >> + else: >> + #if not vm.shutdown(): >> + vm.destroy() >> + >> + # Cleanup source. >> + if os.path.exists(vm_xmlfile_bak): >> + os.remove(vm_xmlfile_bak) >> + logging.info("%s removed." % vm_xmlfile_bak) >> + if os.path.exists(dest_xmlfile): >> + os.remove(dest_xmlfile) >> + >> + if exception: >> + raise error.TestError("Error occurred. \n%s: %s" % >> (detail.__class__, detail)) >> + >> + # Check test result. >> + if status_error == 'yes': >> + if ret_migrate: >> + raise error.TestFail("Migration finished with unexpected >> status.") >> + else: >> + if not ret_migrate: >> + raise error.TestFail("Migration finished with unexpected >> status.") >> + if not check_dest_state: >> + raise error.TestFail("Wrong VM state on destination.") >> + if not check_dest_persistent: >> + raise error.TestFail("VM is not persistent on destination.") >> + if not check_src_undefine: >> + raise error.TestFail("VM is not undefined on source.") >> + if not check_dest_dname: >> + raise error.TestFail("Wrong VM name %s on destination." % dname) >> + if not check_dest_xml: >> + raise error.TestFail("Wrong xml configuration on destination.") > > This is becomming very complicated test module, but the code reads very > clean, style looks good, and it's well commented. There are a few areas for > improvement (nothing is ever perfect :) but overall it's very good. I'll > tripple check it through pylint and then see about getting it committed to > "next" branch for integration testing. Nice job, thanks for following > through. > -- Best Regards, Tang chen _______________________________________________ Autotest mailing list Autotest@test.kernel.org http://test.kernel.org/cgi-bin/mailman/listinfo/autotest