From bae2632658bce8d09a8b5f777e8a3d1b6c960467 Mon Sep 17 00:00:00 2001 From: Alessandro Pilotti Date: Mon, 13 Aug 2012 17:24:16 +0300 Subject: Adds Hyper-V support in nova-compute (with new network_info model), including unit tests blueprint hyper-v-revival Features included in ths commit: Spawn (including CoW image option support) Destroy Info List Pause Unpause Suspend Resume Reboot Power On Power Off Snapshot Volume Attach Volume Detach Boot from Volume Live Migration Supported platforms: Windows Server / Hyper-V Server 2008 R2 Windows Server / Hyper-V Server 2012 Unit tests: Unit tests for all the listed features are included. Tests can be execute on Linux as well. nova.conf relevant flags: Compute driver: compute_driver=nova.virt.hyperv.driver.HyperVDriver External vswitch to be used: vswitch_name=an_external_vswitch Path where the VHDs are going to be stored instances_path=C:\Hyper-V\instances Live migration support for hosts with etherogeneus CPUs limit_cpu_features=true Change-Id: Ic40adcd2d78b0ca6792d77940810f5a44de8cc37 --- nova/tests/hyperv/__init__.py | 0 nova/tests/hyperv/basetestcase.py | 96 +++ nova/tests/hyperv/db_fakes.py | 166 ++++++ nova/tests/hyperv/hypervutils.py | 245 ++++++++ nova/tests/hyperv/mockproxy.py | 234 ++++++++ nova/tests/hyperv/stubs/README.rst | 2 + ...pi.HyperVAPITestCase.test_attach_volume_os.p.gz | Bin 0 -> 670 bytes ...VAPITestCase.test_attach_volume_subprocess.p.gz | Bin 0 -> 2768 bytes ....HyperVAPITestCase.test_attach_volume_time.p.gz | Bin 0 -> 257 bytes ....HyperVAPITestCase.test_attach_volume_uuid.p.gz | Bin 0 -> 660 bytes ...h_volume_with_target_connection_failure_os.p.gz | Bin 0 -> 702 bytes ..._with_target_connection_failure_subprocess.p.gz | Bin 0 -> 571 bytes ...volume_with_target_connection_failure_time.p.gz | Bin 0 -> 277 bytes ...volume_with_target_connection_failure_uuid.p.gz | Bin 0 -> 652 bytes ..._volume_with_target_connection_failure_wmi.p.gz | Bin 0 -> 23220 bytes ...i.HyperVAPITestCase.test_attach_volume_wmi.p.gz | Bin 0 -> 28631 bytes ...ITestCase.test_boot_from_volume_subprocess.p.gz | Bin 0 -> 385 bytes ...perVAPITestCase.test_boot_from_volume_time.p.gz | Bin 0 -> 260 bytes ...perVAPITestCase.test_boot_from_volume_uuid.p.gz | Bin 0 -> 578 bytes ...yperVAPITestCase.test_boot_from_volume_wmi.p.gz | Bin 0 -> 20274 bytes ...pi.HyperVAPITestCase.test_detach_volume_os.p.gz | Bin 0 -> 725 bytes ...VAPITestCase.test_detach_volume_subprocess.p.gz | Bin 0 -> 426 bytes ....HyperVAPITestCase.test_detach_volume_time.p.gz | Bin 0 -> 257 bytes ....HyperVAPITestCase.test_detach_volume_uuid.p.gz | Bin 0 -> 660 bytes ...i.HyperVAPITestCase.test_detach_volume_wmi.p.gz | Bin 0 -> 31833 bytes ...i.HyperVAPITestCase.test_live_migration_os.p.gz | Bin 0 -> 726 bytes ...HyperVAPITestCase.test_live_migration_time.p.gz | Bin 0 -> 250 bytes ...HyperVAPITestCase.test_live_migration_uuid.p.gz | Bin 0 -> 621 bytes ...test_live_migration_with_target_failure_os.p.gz | Bin 0 -> 744 bytes ...st_live_migration_with_target_failure_time.p.gz | Bin 0 -> 267 bytes ...st_live_migration_with_target_failure_uuid.p.gz | Bin 0 -> 640 bytes ...est_live_migration_with_target_failure_wmi.p.gz | Bin 0 -> 25238 bytes ....HyperVAPITestCase.test_live_migration_wmi.p.gz | Bin 0 -> 29404 bytes ...pi.HyperVAPITestCase.test_attach_volume_os.p.gz | Bin 0 -> 722 bytes ...yperVAPITestCase.test_attach_volume_shutil.p.gz | Bin 0 -> 289 bytes ...VAPITestCase.test_attach_volume_subprocess.p.gz | Bin 0 -> 2797 bytes ....HyperVAPITestCase.test_attach_volume_time.p.gz | Bin 0 -> 276 bytes ....HyperVAPITestCase.test_attach_volume_uuid.p.gz | Bin 0 -> 674 bytes ...h_volume_with_target_connection_failure_os.p.gz | Bin 0 -> 755 bytes ...lume_with_target_connection_failure_shutil.p.gz | Bin 0 -> 320 bytes ..._with_target_connection_failure_subprocess.p.gz | Bin 0 -> 591 bytes ...volume_with_target_connection_failure_time.p.gz | Bin 0 -> 290 bytes ...volume_with_target_connection_failure_uuid.p.gz | Bin 0 -> 658 bytes ..._volume_with_target_connection_failure_wmi.p.gz | Bin 0 -> 22780 bytes ...i.HyperVAPITestCase.test_attach_volume_wmi.p.gz | Bin 0 -> 28844 bytes ...rVAPITestCase.test_boot_from_volume_shutil.p.gz | Bin 0 -> 292 bytes ...ITestCase.test_boot_from_volume_subprocess.p.gz | Bin 0 -> 2800 bytes ...perVAPITestCase.test_boot_from_volume_time.p.gz | Bin 0 -> 275 bytes ...perVAPITestCase.test_boot_from_volume_uuid.p.gz | Bin 0 -> 592 bytes ...yperVAPITestCase.test_boot_from_volume_wmi.p.gz | Bin 0 -> 19845 bytes ...ypervapi.HyperVAPITestCase.test_destroy_os.p.gz | Bin 0 -> 748 bytes ...vapi.HyperVAPITestCase.test_destroy_shutil.p.gz | Bin 0 -> 283 bytes ...ervapi.HyperVAPITestCase.test_destroy_time.p.gz | Bin 0 -> 253 bytes ...ervapi.HyperVAPITestCase.test_destroy_uuid.p.gz | Bin 0 -> 627 bytes ...pervapi.HyperVAPITestCase.test_destroy_wmi.p.gz | Bin 0 -> 24040 bytes ...pi.HyperVAPITestCase.test_detach_volume_os.p.gz | Bin 0 -> 723 bytes ...yperVAPITestCase.test_detach_volume_shutil.p.gz | Bin 0 -> 289 bytes ...VAPITestCase.test_detach_volume_subprocess.p.gz | Bin 0 -> 2798 bytes ....HyperVAPITestCase.test_detach_volume_time.p.gz | Bin 0 -> 275 bytes ....HyperVAPITestCase.test_detach_volume_uuid.p.gz | Bin 0 -> 671 bytes ...i.HyperVAPITestCase.test_detach_volume_wmi.p.gz | Bin 0 -> 29537 bytes ...pervapi.HyperVAPITestCase.test_get_info_os.p.gz | Bin 0 -> 717 bytes ...api.HyperVAPITestCase.test_get_info_shutil.p.gz | Bin 0 -> 284 bytes ...rvapi.HyperVAPITestCase.test_get_info_time.p.gz | Bin 0 -> 254 bytes ...rvapi.HyperVAPITestCase.test_get_info_uuid.p.gz | Bin 0 -> 626 bytes ...ervapi.HyperVAPITestCase.test_get_info_wmi.p.gz | Bin 0 -> 23400 bytes ...TestCase.test_list_instances_detail_shutil.p.gz | Bin 0 -> 277 bytes ...APITestCase.test_list_instances_detail_wmi.p.gz | Bin 0 -> 7893 bytes ...perVAPITestCase.test_list_instances_shutil.p.gz | Bin 0 -> 290 bytes ....HyperVAPITestCase.test_list_instances_wmi.p.gz | Bin 0 -> 1300 bytes ...i.HyperVAPITestCase.test_live_migration_os.p.gz | Bin 0 -> 603 bytes ...perVAPITestCase.test_live_migration_shutil.p.gz | Bin 0 -> 290 bytes ...HyperVAPITestCase.test_live_migration_time.p.gz | Bin 0 -> 260 bytes ...HyperVAPITestCase.test_live_migration_uuid.p.gz | Bin 0 -> 631 bytes ...test_live_migration_with_target_failure_os.p.gz | Bin 0 -> 621 bytes ..._live_migration_with_target_failure_shutil.p.gz | Bin 0 -> 310 bytes ...st_live_migration_with_target_failure_time.p.gz | Bin 0 -> 280 bytes ...st_live_migration_with_target_failure_uuid.p.gz | Bin 0 -> 649 bytes ...est_live_migration_with_target_failure_wmi.p.gz | Bin 0 -> 23876 bytes ....HyperVAPITestCase.test_live_migration_wmi.p.gz | Bin 0 -> 26172 bytes ...rVAPITestCase.test_pause_already_paused_os.p.gz | Bin 0 -> 728 bytes ...ITestCase.test_pause_already_paused_shutil.p.gz | Bin 0 -> 296 bytes ...APITestCase.test_pause_already_paused_time.p.gz | Bin 0 -> 266 bytes ...APITestCase.test_pause_already_paused_uuid.p.gz | Bin 0 -> 638 bytes ...VAPITestCase.test_pause_already_paused_wmi.p.gz | Bin 0 -> 23490 bytes ..._hypervapi.HyperVAPITestCase.test_pause_os.p.gz | Bin 0 -> 716 bytes ...ervapi.HyperVAPITestCase.test_pause_shutil.p.gz | Bin 0 -> 281 bytes ...ypervapi.HyperVAPITestCase.test_pause_time.p.gz | Bin 0 -> 251 bytes ...ypervapi.HyperVAPITestCase.test_pause_uuid.p.gz | Bin 0 -> 624 bytes ...hypervapi.HyperVAPITestCase.test_pause_wmi.p.gz | Bin 0 -> 23350 bytes ...Case.test_power_off_already_powered_off_os.p.gz | Bin 0 -> 740 bytes ....test_power_off_already_powered_off_shutil.p.gz | Bin 0 -> 305 bytes ...se.test_power_off_already_powered_off_time.p.gz | Bin 0 -> 275 bytes ...se.test_power_off_already_powered_off_uuid.p.gz | Bin 0 -> 646 bytes ...ase.test_power_off_already_powered_off_wmi.p.gz | Bin 0 -> 23323 bytes ...ervapi.HyperVAPITestCase.test_power_off_os.p.gz | Bin 0 -> 719 bytes ...pi.HyperVAPITestCase.test_power_off_shutil.p.gz | Bin 0 -> 285 bytes ...vapi.HyperVAPITestCase.test_power_off_time.p.gz | Bin 0 -> 255 bytes ...vapi.HyperVAPITestCase.test_power_off_uuid.p.gz | Bin 0 -> 625 bytes ...rvapi.HyperVAPITestCase.test_power_off_wmi.p.gz | Bin 0 -> 23258 bytes ...ITestCase.test_power_on_already_running_os.p.gz | Bin 0 -> 734 bytes ...tCase.test_power_on_already_running_shutil.p.gz | Bin 0 -> 300 bytes ...estCase.test_power_on_already_running_time.p.gz | Bin 0 -> 270 bytes ...estCase.test_power_on_already_running_uuid.p.gz | Bin 0 -> 640 bytes ...TestCase.test_power_on_already_running_wmi.p.gz | Bin 0 -> 23305 bytes ...pervapi.HyperVAPITestCase.test_power_on_os.p.gz | Bin 0 -> 718 bytes ...api.HyperVAPITestCase.test_power_on_shutil.p.gz | Bin 0 -> 284 bytes ...rvapi.HyperVAPITestCase.test_power_on_time.p.gz | Bin 0 -> 254 bytes ...rvapi.HyperVAPITestCase.test_power_on_uuid.p.gz | Bin 0 -> 626 bytes ...ervapi.HyperVAPITestCase.test_power_on_wmi.p.gz | Bin 0 -> 23962 bytes ...tCase.test_pre_live_migration_cow_image_os.p.gz | Bin 0 -> 536 bytes ...e.test_pre_live_migration_cow_image_shutil.p.gz | Bin 0 -> 304 bytes ...ase.test_pre_live_migration_cow_image_time.p.gz | Bin 0 -> 273 bytes ...ase.test_pre_live_migration_cow_image_uuid.p.gz | Bin 0 -> 335 bytes ...Case.test_pre_live_migration_cow_image_wmi.p.gz | Bin 0 -> 1382 bytes ...est_pre_live_migration_no_cow_image_shutil.p.gz | Bin 0 -> 307 bytes ....test_pre_live_migration_no_cow_image_uuid.p.gz | Bin 0 -> 337 bytes ...e.test_pre_live_migration_no_cow_image_wmi.p.gz | Bin 0 -> 849 bytes ...hypervapi.HyperVAPITestCase.test_reboot_os.p.gz | Bin 0 -> 717 bytes ...rvapi.HyperVAPITestCase.test_reboot_shutil.p.gz | Bin 0 -> 282 bytes ...pervapi.HyperVAPITestCase.test_reboot_time.p.gz | Bin 0 -> 252 bytes ...pervapi.HyperVAPITestCase.test_reboot_uuid.p.gz | Bin 0 -> 623 bytes ...ypervapi.HyperVAPITestCase.test_reboot_wmi.p.gz | Bin 0 -> 23931 bytes ...APITestCase.test_resume_already_running_os.p.gz | Bin 0 -> 733 bytes ...estCase.test_resume_already_running_shutil.p.gz | Bin 0 -> 298 bytes ...ITestCase.test_resume_already_running_time.p.gz | Bin 0 -> 268 bytes ...ITestCase.test_resume_already_running_uuid.p.gz | Bin 0 -> 640 bytes ...PITestCase.test_resume_already_running_wmi.p.gz | Bin 0 -> 23341 bytes ...hypervapi.HyperVAPITestCase.test_resume_os.p.gz | Bin 0 -> 716 bytes ...rvapi.HyperVAPITestCase.test_resume_shutil.p.gz | Bin 0 -> 282 bytes ...pervapi.HyperVAPITestCase.test_resume_time.p.gz | Bin 0 -> 252 bytes ...pervapi.HyperVAPITestCase.test_resume_uuid.p.gz | Bin 0 -> 623 bytes ...ypervapi.HyperVAPITestCase.test_resume_wmi.p.gz | Bin 0 -> 24291 bytes ...pervapi.HyperVAPITestCase.test_snapshot_os.p.gz | Bin 0 -> 1012 bytes ...api.HyperVAPITestCase.test_snapshot_shutil.p.gz | Bin 0 -> 416 bytes ...rvapi.HyperVAPITestCase.test_snapshot_time.p.gz | Bin 0 -> 254 bytes ...rvapi.HyperVAPITestCase.test_snapshot_uuid.p.gz | Bin 0 -> 667 bytes ...tCase.test_snapshot_with_update_failure_os.p.gz | Bin 0 -> 1033 bytes ...e.test_snapshot_with_update_failure_shutil.p.gz | Bin 0 -> 437 bytes ...ase.test_snapshot_with_update_failure_time.p.gz | Bin 0 -> 274 bytes ...ase.test_snapshot_with_update_failure_uuid.p.gz | Bin 0 -> 688 bytes ...Case.test_snapshot_with_update_failure_wmi.p.gz | Bin 0 -> 24794 bytes ...ervapi.HyperVAPITestCase.test_snapshot_wmi.p.gz | Bin 0 -> 24505 bytes ....HyperVAPITestCase.test_spawn_cow_image_os.p.gz | Bin 0 -> 724 bytes ...erVAPITestCase.test_spawn_cow_image_shutil.p.gz | Bin 0 -> 291 bytes ...yperVAPITestCase.test_spawn_cow_image_time.p.gz | Bin 0 -> 261 bytes ...yperVAPITestCase.test_spawn_cow_image_uuid.p.gz | Bin 0 -> 631 bytes ...HyperVAPITestCase.test_spawn_cow_image_wmi.p.gz | Bin 0 -> 24716 bytes ...perVAPITestCase.test_spawn_no_cow_image_os.p.gz | Bin 0 -> 607 bytes ...APITestCase.test_spawn_no_cow_image_shutil.p.gz | Bin 0 -> 294 bytes ...rVAPITestCase.test_spawn_no_cow_image_time.p.gz | Bin 0 -> 264 bytes ...rVAPITestCase.test_spawn_no_cow_image_uuid.p.gz | Bin 0 -> 635 bytes ...erVAPITestCase.test_spawn_no_cow_image_wmi.p.gz | Bin 0 -> 24420 bytes ...estCase.test_spawn_no_vswitch_exception_os.p.gz | Bin 0 -> 737 bytes ...ase.test_spawn_no_vswitch_exception_shutil.p.gz | Bin 0 -> 302 bytes ...tCase.test_spawn_no_vswitch_exception_time.p.gz | Bin 0 -> 271 bytes ...tCase.test_spawn_no_vswitch_exception_uuid.p.gz | Bin 0 -> 558 bytes ...stCase.test_spawn_no_vswitch_exception_wmi.p.gz | Bin 0 -> 17307 bytes ...TestCase.test_suspend_already_suspended_os.p.gz | Bin 0 -> 734 bytes ...Case.test_suspend_already_suspended_shutil.p.gz | Bin 0 -> 301 bytes ...stCase.test_suspend_already_suspended_time.p.gz | Bin 0 -> 271 bytes ...stCase.test_suspend_already_suspended_uuid.p.gz | Bin 0 -> 643 bytes ...estCase.test_suspend_already_suspended_wmi.p.gz | Bin 0 -> 24133 bytes ...ypervapi.HyperVAPITestCase.test_suspend_os.p.gz | Bin 0 -> 717 bytes ...vapi.HyperVAPITestCase.test_suspend_shutil.p.gz | Bin 0 -> 283 bytes ...ervapi.HyperVAPITestCase.test_suspend_time.p.gz | Bin 0 -> 253 bytes ...ervapi.HyperVAPITestCase.test_suspend_uuid.p.gz | Bin 0 -> 623 bytes ...pervapi.HyperVAPITestCase.test_suspend_wmi.p.gz | Bin 0 -> 23864 bytes ...PITestCase.test_unpause_already_running_os.p.gz | Bin 0 -> 735 bytes ...stCase.test_unpause_already_running_shutil.p.gz | Bin 0 -> 299 bytes ...TestCase.test_unpause_already_running_time.p.gz | Bin 0 -> 269 bytes ...TestCase.test_unpause_already_running_uuid.p.gz | Bin 0 -> 640 bytes ...ITestCase.test_unpause_already_running_wmi.p.gz | Bin 0 -> 23690 bytes ...ypervapi.HyperVAPITestCase.test_unpause_os.p.gz | Bin 0 -> 717 bytes ...vapi.HyperVAPITestCase.test_unpause_shutil.p.gz | Bin 0 -> 283 bytes ...ervapi.HyperVAPITestCase.test_unpause_time.p.gz | Bin 0 -> 253 bytes ...ervapi.HyperVAPITestCase.test_unpause_uuid.p.gz | Bin 0 -> 626 bytes ...pervapi.HyperVAPITestCase.test_unpause_wmi.p.gz | Bin 0 -> 24099 bytes nova/tests/test_hypervapi.py | 463 +++++++++++++++ nova/virt/hyperv/README.rst | 44 ++ nova/virt/hyperv/__init__.py | 0 nova/virt/hyperv/baseops.py | 61 ++ nova/virt/hyperv/constants.py | 54 ++ nova/virt/hyperv/driver.py | 226 +++++++ nova/virt/hyperv/ioutils.py | 26 + nova/virt/hyperv/livemigrationops.py | 162 +++++ nova/virt/hyperv/snapshotops.py | 187 ++++++ nova/virt/hyperv/vmops.py | 650 +++++++++++++++++++++ nova/virt/hyperv/vmutils.py | 146 +++++ nova/virt/hyperv/volumeops.py | 297 ++++++++++ nova/virt/hyperv/volumeutils.py | 122 ++++ 191 files changed, 3181 insertions(+) create mode 100644 nova/tests/hyperv/__init__.py create mode 100644 nova/tests/hyperv/basetestcase.py create mode 100644 nova/tests/hyperv/db_fakes.py create mode 100644 nova/tests/hyperv/hypervutils.py create mode 100644 nova/tests/hyperv/mockproxy.py create mode 100644 nova/tests/hyperv/stubs/README.rst create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_wmi.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_os.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_shutil.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_time.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_uuid.p.gz create mode 100644 nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_wmi.p.gz create mode 100644 nova/tests/test_hypervapi.py create mode 100644 nova/virt/hyperv/README.rst create mode 100644 nova/virt/hyperv/__init__.py create mode 100644 nova/virt/hyperv/baseops.py create mode 100644 nova/virt/hyperv/constants.py create mode 100644 nova/virt/hyperv/driver.py create mode 100644 nova/virt/hyperv/ioutils.py create mode 100644 nova/virt/hyperv/livemigrationops.py create mode 100644 nova/virt/hyperv/snapshotops.py create mode 100644 nova/virt/hyperv/vmops.py create mode 100644 nova/virt/hyperv/vmutils.py create mode 100644 nova/virt/hyperv/volumeops.py create mode 100644 nova/virt/hyperv/volumeutils.py diff --git a/nova/tests/hyperv/__init__.py b/nova/tests/hyperv/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/nova/tests/hyperv/basetestcase.py b/nova/tests/hyperv/basetestcase.py new file mode 100644 index 000000000..318cf2e28 --- /dev/null +++ b/nova/tests/hyperv/basetestcase.py @@ -0,0 +1,96 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +TestCase for MockProxy based tests and related classes. +""" + +import gzip +import os +import pickle + +from nova import test +from nova.tests.hyperv import mockproxy + +gen_test_mocks_key = 'NOVA_GENERATE_TEST_MOCKS' + + +class BaseTestCase(test.TestCase): + """TestCase for MockProxy based tests.""" + + def run(self, result=None): + self._currentResult = result + super(BaseTestCase, self).run(result) + + def setUp(self): + super(BaseTestCase, self).setUp() + self._mps = {} + + def tearDown(self): + super(BaseTestCase, self).tearDown() + + has_errors = len([test for (test, msgs) in self._currentResult.errors + if test.id() == self.id()]) > 0 + failed = len([test for (test, msgs) in self._currentResult.failures + if test.id() == self.id()]) > 0 + + if not has_errors and not failed: + self._save_mock_proxies() + + def _save_mock(self, name, mock): + path = self._get_stub_file_path(self.id(), name) + pickle.dump(mock, gzip.open(path, 'wb')) + + def _get_stub_file_path(self, test_name, mock_name): + # test naming differs between platforms + prefix = 'nova.tests.' + if test_name.startswith(prefix): + test_name = test_name[len(prefix):] + file_name = '{0}_{1}.p.gz'.format(test_name, mock_name) + return os.path.join(os.path.dirname(mockproxy.__file__), + "stubs", file_name) + + def _load_mock(self, name): + path = self._get_stub_file_path(self.id(), name) + if os.path.exists(path): + return pickle.load(gzip.open(path, 'rb')) + else: + return None + + def _load_mock_or_create_proxy(self, module_name): + m = None + if not gen_test_mocks_key in os.environ or \ + os.environ[gen_test_mocks_key].lower() \ + not in ['true', 'yes', '1']: + m = self._load_mock(module_name) + else: + module = __import__(module_name) + m = mockproxy.MockProxy(module) + self._mps[module_name] = m + return m + + def _inject_mocks_in_modules(self, objects_to_mock, modules_to_test): + for module_name in objects_to_mock: + mp = self._load_mock_or_create_proxy(module_name) + for mt in modules_to_test: + module_local_name = module_name.split('.')[-1] + setattr(mt, module_local_name, mp) + + def _save_mock_proxies(self): + for name, mp in self._mps.items(): + m = mp.get_mock() + if m.has_values(): + self._save_mock(name, m) diff --git a/nova/tests/hyperv/db_fakes.py b/nova/tests/hyperv/db_fakes.py new file mode 100644 index 000000000..9f5572fd1 --- /dev/null +++ b/nova/tests/hyperv/db_fakes.py @@ -0,0 +1,166 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Stubouts, mocks and fixtures for the test suite +""" + +import time + +from nova.compute import task_states +from nova.compute import vm_states +from nova import db +from nova import utils + + +def get_fake_instance_data(name, project_id, user_id): + return {'name': name, + 'id': 1, + 'uuid': utils.gen_uuid(), + 'project_id': project_id, + 'user_id': user_id, + 'image_ref': "1", + 'kernel_id': "1", + 'ramdisk_id': "1", + 'mac_address': "de:ad:be:ef:be:ef", + 'instance_type': 'm1.tiny', + } + + +def get_fake_image_data(project_id, user_id): + return {'name': 'image1', + 'id': 1, + 'project_id': project_id, + 'user_id': user_id, + 'image_ref': "1", + 'kernel_id': "1", + 'ramdisk_id': "1", + 'mac_address': "de:ad:be:ef:be:ef", + 'instance_type': 'm1.tiny', + } + + +def get_fake_volume_info_data(target_portal, volume_id): + return { + 'driver_volume_type': 'iscsi', + 'data': { + 'volume_id': 1, + 'target_iqn': 'iqn.2010-10.org.openstack:volume-' + volume_id, + 'target_portal': target_portal, + 'target_lun': 1, + 'auth_method': 'CHAP', + 'auth_method': 'fake', + 'auth_method': 'fake', + } +} + + +def get_fake_block_device_info(target_portal, volume_id): + return { + 'block_device_mapping': [{'connection_info': { + 'driver_volume_type': 'iscsi', + 'data': {'target_lun': 1, + 'volume_id': volume_id, + 'target_iqn': 'iqn.2010-10.org.openstack:volume-' + + volume_id, + 'target_portal': target_portal, + 'target_discovered': False}}, + 'mount_device': 'vda', + 'delete_on_termination': False}], + 'root_device_name': None, + 'ephemerals': [], + 'swap': None + } + + +def stub_out_db_instance_api(stubs): + """Stubs out the db API for creating Instances.""" + + INSTANCE_TYPES = { + 'm1.tiny': dict(memory_mb=512, vcpus=1, root_gb=0, flavorid=1), + 'm1.small': dict(memory_mb=2048, vcpus=1, root_gb=20, flavorid=2), + 'm1.medium': + dict(memory_mb=4096, vcpus=2, root_gb=40, flavorid=3), + 'm1.large': dict(memory_mb=8192, vcpus=4, root_gb=80, flavorid=4), + 'm1.xlarge': + dict(memory_mb=16384, vcpus=8, root_gb=160, flavorid=5)} + + class FakeModel(object): + """Stubs out for model.""" + + def __init__(self, values): + self.values = values + + def __getattr__(self, name): + return self.values[name] + + def __getitem__(self, key): + if key in self.values: + return self.values[key] + else: + raise NotImplementedError() + + def fake_instance_create(context, values): + """Stubs out the db.instance_create method.""" + + if 'instance_type' not in values: + return + + type_data = INSTANCE_TYPES[values['instance_type']] + + base_options = { + 'name': values['name'], + 'id': values['id'], + 'uuid': utils.gen_uuid(), + 'reservation_id': utils.generate_uid('r'), + 'image_ref': values['image_ref'], + 'kernel_id': values['kernel_id'], + 'ramdisk_id': values['ramdisk_id'], + 'vm_state': vm_states.BUILDING, + 'task_state': task_states.SCHEDULING, + 'user_id': values['user_id'], + 'project_id': values['project_id'], + 'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()), + 'instance_type': values['instance_type'], + 'memory_mb': type_data['memory_mb'], + 'vcpus': type_data['vcpus'], + 'mac_addresses': [{'address': values['mac_address']}], + 'root_gb': type_data['root_gb'], + } + return FakeModel(base_options) + + def fake_network_get_by_instance(context, instance_id): + """Stubs out the db.network_get_by_instance method.""" + + fields = { + 'bridge': 'vmnet0', + 'netmask': '255.255.255.0', + 'gateway': '10.10.10.1', + 'broadcast': '10.10.10.255', + 'dns1': 'fake', + 'vlan': 100} + return FakeModel(fields) + + def fake_instance_type_get_all(context, inactive=0, filters=None): + return INSTANCE_TYPES.values() + + def fake_instance_type_get_by_name(context, name): + return INSTANCE_TYPES[name] + + stubs.Set(db, 'instance_create', fake_instance_create) + stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) + stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all) + stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name) diff --git a/nova/tests/hyperv/hypervutils.py b/nova/tests/hyperv/hypervutils.py new file mode 100644 index 000000000..7cf9f32fe --- /dev/null +++ b/nova/tests/hyperv/hypervutils.py @@ -0,0 +1,245 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Hyper-V classes to be used in testing. +""" + +import sys +import time + +from nova import exception +from nova.virt.hyperv import constants +from nova.virt.hyperv import volumeutils +from xml.etree import ElementTree + +# Check needed for unit testing on Unix +if sys.platform == 'win32': + import wmi + + +class HyperVUtils(object): + def __init__(self): + self.__conn = None + self.__conn_v2 = None + self.__conn_cimv2 = None + self.__conn_wmi = None + self._volumeutils = volumeutils.VolumeUtils() + + @property + def _conn(self): + if self.__conn is None: + self.__conn = wmi.WMI(moniker='//./root/virtualization') + return self.__conn + + @property + def _conn_v2(self): + if self.__conn_v2 is None: + self.__conn_v2 = wmi.WMI(moniker='//./root/virtualization/v2') + return self.__conn_v2 + + @property + def _conn_cimv2(self): + if self.__conn_cimv2 is None: + self.__conn_cimv2 = wmi.WMI(moniker='//./root/cimv2') + return self.__conn_cimv2 + + @property + def _conn_wmi(self): + if self.__conn_wmi is None: + self.__conn_wmi = wmi.WMI(moniker='//./root/wmi') + return self.__conn_wmi + + def create_vhd(self, path): + image_service = self._conn.query( + "Select * from Msvm_ImageManagementService")[0] + (job, ret_val) = image_service.CreateDynamicVirtualHardDisk( + Path=path, MaxInternalSize=3 * 1024 * 1024) + + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._check_job_status(job) + else: + success = (ret_val == 0) + if not success: + raise Exception('Failed to create Dynamic disk %s with error %d' + % (path, ret_val)) + + def _check_job_status(self, jobpath): + """Poll WMI job state for completion""" + job_wmi_path = jobpath.replace('\\', '/') + job = wmi.WMI(moniker=job_wmi_path) + + while job.JobState == constants.WMI_JOB_STATE_RUNNING: + time.sleep(0.1) + job = wmi.WMI(moniker=job_wmi_path) + return job.JobState == constants.WMI_JOB_STATE_COMPLETED + + def _get_vm(self, vm_name, conn=None): + if conn is None: + conn = self._conn + vml = conn.Msvm_ComputerSystem(ElementName=vm_name) + if not len(vml): + raise exception.InstanceNotFound(instance=vm_name) + return vml[0] + + def remote_vm_exists(self, server, vm_name): + conn = wmi.WMI(moniker='//' + server + '/root/virtualization') + return self._vm_exists(conn, vm_name) + + def vm_exists(self, vm_name): + return self._vm_exists(self._conn, vm_name) + + def _vm_exists(self, conn, vm_name): + return len(conn.Msvm_ComputerSystem(ElementName=vm_name)) > 0 + + def _get_vm_summary(self, vm_name): + vm = self._get_vm(vm_name) + vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] + vmsettings = vm.associators( + wmi_association_class='Msvm_SettingsDefineState', + wmi_result_class='Msvm_VirtualSystemSettingData') + settings_paths = [v.path_() for v in vmsettings] + return vs_man_svc.GetSummaryInformation([100, 105], + settings_paths)[1][0] + + def get_vm_uptime(self, vm_name): + return self._get_vm_summary(vm_name).UpTime + + def get_vm_state(self, vm_name): + return self._get_vm_summary(vm_name).EnabledState + + def set_vm_state(self, vm_name, req_state): + self._set_vm_state(self._conn, vm_name, req_state) + + def _set_vm_state(self, conn, vm_name, req_state): + vm = self._get_vm(vm_name, conn) + (job, ret_val) = vm.RequestStateChange(req_state) + + success = False + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._check_job_status(job) + elif ret_val == 0: + success = True + elif ret_val == 32775: + #Invalid state for current operation. Typically means it is + #already in the state requested + success = True + if not success: + raise Exception(_("Failed to change vm state of %(vm_name)s" + " to %(req_state)s") % locals()) + + def get_vm_disks(self, vm_name): + return self._get_vm_disks(self._conn, vm_name) + + def _get_vm_disks(self, conn, vm_name): + vm = self._get_vm(vm_name, conn) + vmsettings = vm.associators( + wmi_result_class='Msvm_VirtualSystemSettingData') + rasds = vmsettings[0].associators( + wmi_result_class='MSVM_ResourceAllocationSettingData') + + disks = [r for r in rasds + if r.ResourceSubType == 'Microsoft Virtual Hard Disk'] + disk_files = [] + for disk in disks: + disk_files.extend([c for c in disk.Connection]) + + volumes = [r for r in rasds + if r.ResourceSubType == 'Microsoft Physical Disk Drive'] + volume_drives = [] + for volume in volumes: + hostResources = volume.HostResource + drive_path = hostResources[0] + volume_drives.append(drive_path) + + return (disk_files, volume_drives) + + def remove_remote_vm(self, server, vm_name): + conn = wmi.WMI(moniker='//' + server + '/root/virtualization') + conn_cimv2 = wmi.WMI(moniker='//' + server + '/root/cimv2') + self._remove_vm(vm_name, conn, conn_cimv2) + + def remove_vm(self, vm_name): + self._remove_vm(vm_name, self._conn, self._conn_cimv2) + + def _remove_vm(self, vm_name, conn, conn_cimv2): + vm = self._get_vm(vm_name, conn) + vs_man_svc = conn.Msvm_VirtualSystemManagementService()[0] + #Stop the VM first. + self._set_vm_state(conn, vm_name, 3) + + (disk_files, volume_drives) = self._get_vm_disks(conn, vm_name) + + (job, ret_val) = vs_man_svc.DestroyVirtualSystem(vm.path_()) + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._check_job_status(job) + elif ret_val == 0: + success = True + if not success: + raise Exception(_('Failed to destroy vm %s') % vm_name) + + #Delete associated vhd disk files. + for disk in disk_files: + vhd_file = conn_cimv2.query( + "Select * from CIM_DataFile where Name = '" + + disk.replace("'", "''") + "'")[0] + vhd_file.Delete() + + def _get_target_iqn(self, volume_id): + return 'iqn.2010-10.org.openstack:volume-' + volume_id + + def logout_iscsi_volume_sessions(self, volume_id): + target_iqn = self._get_target_iqn(volume_id) + self._volumeutils.logout_storage_target(self._conn_wmi, target_iqn) + + def iscsi_volume_sessions_exist(self, volume_id): + target_iqn = self._get_target_iqn(volume_id) + return len(self._conn_wmi.query( + "SELECT * FROM MSiSCSIInitiator_SessionClass \ + WHERE TargetName='" + target_iqn + "'")) > 0 + + def get_vm_count(self): + return len(self._conn.query( + "Select * from Msvm_ComputerSystem where Description " + "<> 'Microsoft Hosting Computer System'")) + + def get_vm_snapshots_count(self, vm_name): + return len(self._conn.query( + "Select * from Msvm_VirtualSystemSettingData where \ + SettingType = 5 and SystemName = '" + vm_name + "'")) + + def get_vhd_parent_path(self, vhd_path): + + image_man_svc = self._conn.Msvm_ImageManagementService()[0] + + (vhd_info, job_path, ret_val) = \ + image_man_svc.GetVirtualHardDiskInfo(vhd_path) + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._check_job_status(job_path) + else: + success = (ret_val == 0) + if not success: + raise Exception(_("Failed to get info for disk %s") % + (vhd_path)) + + base_disk_path = None + et = ElementTree.fromstring(vhd_info) + for item in et.findall("PROPERTY"): + if item.attrib["NAME"] == "ParentPath": + base_disk_path = item.find("VALUE").text + break + + return base_disk_path diff --git a/nova/tests/hyperv/mockproxy.py b/nova/tests/hyperv/mockproxy.py new file mode 100644 index 000000000..ff04ea709 --- /dev/null +++ b/nova/tests/hyperv/mockproxy.py @@ -0,0 +1,234 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations + +""" +Classes for dynamic generation of mock objects. +""" + +import inspect + + +def serialize_obj(obj): + if isinstance(obj, float): + val = str(round(obj, 10)) + elif isinstance(obj, dict): + d = {} + for k1, v1 in obj.items(): + d[k1] = serialize_obj(v1) + val = str(d) + elif isinstance(obj, list): + l1 = [] + for i1 in obj: + l1.append(serialize_obj(i1)) + val = str(l1) + elif isinstance(obj, tuple): + l1 = () + for i1 in obj: + l1 = l1 + (serialize_obj(i1),) + val = str(l1) + else: + val = str(obj) + return val + + +def serialize_args(*args, **kwargs): + """Workaround for float string conversion issues in Python 2.6""" + return serialize_obj((args, kwargs)) + + +class Mock(object): + def _get_next_value(self, name): + c = self._access_count.get(name) + if c is None: + c = 0 + else: + c = c + 1 + self._access_count[name] = c + return self._values[name][c] + + def _get_next_ret_value(self, name, params): + d = self._access_count.get(name) + if d is None: + d = {} + self._access_count[name] = d + c = d.get(params) + if c is None: + c = 0 + else: + c = c + 1 + d[params] = c + return self._values[name][params][c] + + def __init__(self, values): + self._values = values + self._access_count = {} + + def has_values(self): + return len(self._values) > 0 + + def __getattr__(self, name): + if name.startswith('__') and name.endswith('__'): + return object.__getattribute__(self, name) + else: + if isinstance(self._values[name], dict): + def newfunc(*args, **kwargs): + params = serialize_args(args, kwargs) + return self._get_next_ret_value(name, params) + return newfunc + else: + return self._get_next_value(name) + + def __str__(self): + return self._get_next_value('__str__') + + def __iter__(self): + return getattr(self._get_next_value('__iter__'), '__iter__')() + + def __len__(self): + return self._get_next_value('__len__') + + def __getitem__(self, key): + return self._get_next_ret_value('__getitem__', str(key)) + + def __call__(self, *args, **kwargs): + params = serialize_args(args, kwargs) + return self._get_next_ret_value('__call__', params) + + +class MockProxy(object): + def __init__(self, wrapped): + self._wrapped = wrapped + self._recorded_values = {} + + def _get_proxy_object(self, obj): + if hasattr(obj, '__dict__') or isinstance(obj, tuple) or \ + isinstance(obj, list) or isinstance(obj, dict): + p = MockProxy(obj) + else: + p = obj + return p + + def __getattr__(self, name): + if name in ['_wrapped']: + return object.__getattribute__(self, name) + else: + attr = getattr(self._wrapped, name) + if inspect.isfunction(attr) or inspect.ismethod(attr) or \ + inspect.isbuiltin(attr): + def newfunc(*args, **kwargs): + result = attr(*args, **kwargs) + p = self._get_proxy_object(result) + params = serialize_args(args, kwargs) + self._add_recorded_ret_value(name, params, p) + return p + return newfunc + elif hasattr(attr, '__dict__') or (hasattr(attr, '__getitem__') + and not (isinstance(attr, str) or isinstance(attr, unicode))): + p = MockProxy(attr) + else: + p = attr + self._add_recorded_value(name, p) + return p + + def __setattr__(self, name, value): + if name in ['_wrapped', '_recorded_values']: + object.__setattr__(self, name, value) + else: + setattr(self._wrapped, name, value) + + def _add_recorded_ret_value(self, name, params, val): + d = self._recorded_values.get(name) + if d is None: + d = {} + self._recorded_values[name] = d + l = d.get(params) + if l is None: + l = [] + d[params] = l + l.append(val) + + def _add_recorded_value(self, name, val): + if not name in self._recorded_values: + self._recorded_values[name] = [] + self._recorded_values[name].append(val) + + def get_mock(self): + values = {} + for k, v in self._recorded_values.items(): + if isinstance(v, dict): + d = {} + values[k] = d + for k1, v1 in v.items(): + l = [] + d[k1] = l + for i1 in v1: + if isinstance(i1, MockProxy): + l.append(i1.get_mock()) + else: + l.append(i1) + else: + l = [] + values[k] = l + for i in v: + if isinstance(i, MockProxy): + l.append(i.get_mock()) + elif isinstance(i, dict): + d = {} + for k1, v1 in v.items(): + if isinstance(v1, MockProxy): + d[k1] = v1.get_mock() + else: + d[k1] = v1 + l.append(d) + elif isinstance(i, list): + l1 = [] + for i1 in i: + if isinstance(i1, MockProxy): + l1.append(i1.get_mock()) + else: + l1.append(i1) + l.append(l1) + else: + l.append(i) + return Mock(values) + + def __str__(self): + s = str(self._wrapped) + self._add_recorded_value('__str__', s) + return s + + def __len__(self): + l = len(self._wrapped) + self._add_recorded_value('__len__', l) + return l + + def __iter__(self): + it = [] + for i in self._wrapped: + it.append(self._get_proxy_object(i)) + self._add_recorded_value('__iter__', it) + return iter(it) + + def __getitem__(self, key): + p = self._get_proxy_object(self._wrapped[key]) + self._add_recorded_ret_value('__getitem__', str(key), p) + return p + + def __call__(self, *args, **kwargs): + c = self._wrapped(*args, **kwargs) + p = self._get_proxy_object(c) + params = serialize_args(args, kwargs) + self._add_recorded_ret_value('__call__', params, p) + return p diff --git a/nova/tests/hyperv/stubs/README.rst b/nova/tests/hyperv/stubs/README.rst new file mode 100644 index 000000000..150fd3ad1 --- /dev/null +++ b/nova/tests/hyperv/stubs/README.rst @@ -0,0 +1,2 @@ +Files with extension p.gz are compressed pickle files containing serialized +mocks used during unit testing diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gz new file mode 100644 index 000000000..c65832c57 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gz new file mode 100644 index 000000000..7076c4868 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gz new file mode 100644 index 000000000..c251f9d6c Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gz new file mode 100644 index 000000000..cac08e3d0 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gz new file mode 100644 index 000000000..d6e624bb0 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gz new file mode 100644 index 000000000..bb18f7453 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gz new file mode 100644 index 000000000..a5f592a74 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gz new file mode 100644 index 000000000..4bebe0e72 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gz new file mode 100644 index 000000000..29a610f36 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gz new file mode 100644 index 000000000..ca92ece00 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gz new file mode 100644 index 000000000..58269455d Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gz new file mode 100644 index 000000000..97cd7e62b Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gz new file mode 100644 index 000000000..708197430 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gz new file mode 100644 index 000000000..d5eb4d746 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gz new file mode 100644 index 000000000..d8c63d8ad Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gz new file mode 100644 index 000000000..d0b27d201 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gz new file mode 100644 index 000000000..657379cec Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gz new file mode 100644 index 000000000..8bf58ef5c Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gz new file mode 100644 index 000000000..c20281811 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gz new file mode 100644 index 000000000..a198af844 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gz new file mode 100644 index 000000000..749eabe40 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gz new file mode 100644 index 000000000..c40e6f995 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gz new file mode 100644 index 000000000..c67dc9271 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gz new file mode 100644 index 000000000..0d671fc18 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gz new file mode 100644 index 000000000..66583beb1 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gz new file mode 100644 index 000000000..efdef819f Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gz b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gz new file mode 100644 index 000000000..5edd6f147 Binary files /dev/null and b/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gz new file mode 100644 index 000000000..009a2d45d Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_shutil.p.gz new file mode 100644 index 000000000..cb7818abb Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gz new file mode 100644 index 000000000..d4005b336 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gz new file mode 100644 index 000000000..041d7314a Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gz new file mode 100644 index 000000000..cab9cd580 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gz new file mode 100644 index 000000000..0dfe439ca Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_shutil.p.gz new file mode 100644 index 000000000..17f83545d Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gz new file mode 100644 index 000000000..1ecf26961 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gz new file mode 100644 index 000000000..1c68ad11e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gz new file mode 100644 index 000000000..7d4bae7a9 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gz new file mode 100644 index 000000000..c1d101887 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gz new file mode 100644 index 000000000..2f30402a9 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_shutil.p.gz new file mode 100644 index 000000000..578b33da7 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gz new file mode 100644 index 000000000..1da1b4dd0 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gz new file mode 100644 index 000000000..67798704f Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gz new file mode 100644 index 000000000..54585f18c Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gz new file mode 100644 index 000000000..61ca098cb Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_os.p.gz new file mode 100644 index 000000000..5f5a6c383 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_shutil.p.gz new file mode 100644 index 000000000..61c59ea1f Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_time.p.gz new file mode 100644 index 000000000..91252758c Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_uuid.p.gz new file mode 100644 index 000000000..b06fd1371 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_wmi.p.gz new file mode 100644 index 000000000..c6e9722c2 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gz new file mode 100644 index 000000000..809332508 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_shutil.p.gz new file mode 100644 index 000000000..d4b9d8921 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gz new file mode 100644 index 000000000..c6124e1e0 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gz new file mode 100644 index 000000000..7b7110e06 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gz new file mode 100644 index 000000000..6c254032c Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gz new file mode 100644 index 000000000..595510cff Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_os.p.gz new file mode 100644 index 000000000..a292ad56e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_shutil.p.gz new file mode 100644 index 000000000..bc29985bd Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_time.p.gz new file mode 100644 index 000000000..21812b0fa Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_uuid.p.gz new file mode 100644 index 000000000..13f51b856 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_wmi.p.gz new file mode 100644 index 000000000..fca5d9328 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_shutil.p.gz new file mode 100644 index 000000000..3ab35a29f Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_wmi.p.gz new file mode 100644 index 000000000..411c0ed07 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_shutil.p.gz new file mode 100644 index 000000000..b082714cd Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_wmi.p.gz new file mode 100644 index 000000000..103f00b84 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gz new file mode 100644 index 000000000..3ab274719 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_shutil.p.gz new file mode 100644 index 000000000..9d89a627d Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gz new file mode 100644 index 000000000..2c6fa5a22 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gz new file mode 100644 index 000000000..9a54bbb62 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gz new file mode 100644 index 000000000..0b6aff86d Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_shutil.p.gz new file mode 100644 index 000000000..51331083e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gz new file mode 100644 index 000000000..fb5e35662 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gz new file mode 100644 index 000000000..d8c75ba3c Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gz new file mode 100644 index 000000000..92bbed9da Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gz new file mode 100644 index 000000000..bb4535336 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_os.p.gz new file mode 100644 index 000000000..b2af3e865 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_shutil.p.gz new file mode 100644 index 000000000..293c9b3cf Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_time.p.gz new file mode 100644 index 000000000..b43ba2897 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_uuid.p.gz new file mode 100644 index 000000000..a1b757b60 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_wmi.p.gz new file mode 100644 index 000000000..f988eca93 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_os.p.gz new file mode 100644 index 000000000..4d53ded9b Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_shutil.p.gz new file mode 100644 index 000000000..42e3d8f98 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_time.p.gz new file mode 100644 index 000000000..e7728c515 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_uuid.p.gz new file mode 100644 index 000000000..a970cc189 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_wmi.p.gz new file mode 100644 index 000000000..6b3414f25 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_os.p.gz new file mode 100644 index 000000000..11910aa8f Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_shutil.p.gz new file mode 100644 index 000000000..a128ac167 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_time.p.gz new file mode 100644 index 000000000..b56c849ea Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_uuid.p.gz new file mode 100644 index 000000000..adf7b4648 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_wmi.p.gz new file mode 100644 index 000000000..907cf2e25 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_os.p.gz new file mode 100644 index 000000000..81877dd6e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_shutil.p.gz new file mode 100644 index 000000000..33a72e90e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_time.p.gz new file mode 100644 index 000000000..ff56a9287 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_uuid.p.gz new file mode 100644 index 000000000..682dd6d40 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_wmi.p.gz new file mode 100644 index 000000000..fba91bfff Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_os.p.gz new file mode 100644 index 000000000..1578751ee Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_shutil.p.gz new file mode 100644 index 000000000..987eeb6da Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_time.p.gz new file mode 100644 index 000000000..27495c884 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_uuid.p.gz new file mode 100644 index 000000000..80d62a9a4 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_wmi.p.gz new file mode 100644 index 000000000..1ad1d60dc Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_os.p.gz new file mode 100644 index 000000000..3855ac0dd Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_shutil.p.gz new file mode 100644 index 000000000..8f1d273f2 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_time.p.gz new file mode 100644 index 000000000..927204978 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_uuid.p.gz new file mode 100644 index 000000000..849fd1c8c Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_wmi.p.gz new file mode 100644 index 000000000..41aa8ccfb Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_os.p.gz new file mode 100644 index 000000000..e69a69a20 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_shutil.p.gz new file mode 100644 index 000000000..d5aa712ac Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_time.p.gz new file mode 100644 index 000000000..db090ad4d Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_uuid.p.gz new file mode 100644 index 000000000..ae76e5693 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_wmi.p.gz new file mode 100644 index 000000000..8e4e9bd65 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_shutil.p.gz new file mode 100644 index 000000000..991858501 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_uuid.p.gz new file mode 100644 index 000000000..f4a514e5c Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_wmi.p.gz new file mode 100644 index 000000000..3916fc0fb Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_os.p.gz new file mode 100644 index 000000000..de1f831de Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_shutil.p.gz new file mode 100644 index 000000000..751668b6f Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_time.p.gz new file mode 100644 index 000000000..922fce900 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_uuid.p.gz new file mode 100644 index 000000000..c79c72334 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_wmi.p.gz new file mode 100644 index 000000000..3cedfe1ba Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_os.p.gz new file mode 100644 index 000000000..626398469 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_shutil.p.gz new file mode 100644 index 000000000..15a83ac0c Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_time.p.gz new file mode 100644 index 000000000..755cf2e08 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_uuid.p.gz new file mode 100644 index 000000000..d14db9b2f Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_wmi.p.gz new file mode 100644 index 000000000..679287e3a Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_os.p.gz new file mode 100644 index 000000000..ed654b90e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_shutil.p.gz new file mode 100644 index 000000000..5b7ff554d Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_time.p.gz new file mode 100644 index 000000000..d89b52377 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_uuid.p.gz new file mode 100644 index 000000000..764e6c45e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_wmi.p.gz new file mode 100644 index 000000000..a63f4881a Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_os.p.gz new file mode 100644 index 000000000..607047b38 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_shutil.p.gz new file mode 100644 index 000000000..4f8b93282 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_time.p.gz new file mode 100644 index 000000000..429a96d7e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_uuid.p.gz new file mode 100644 index 000000000..ac9c25734 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_os.p.gz new file mode 100644 index 000000000..82b3a6185 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_shutil.p.gz new file mode 100644 index 000000000..741f28905 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_time.p.gz new file mode 100644 index 000000000..5c633dc73 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_uuid.p.gz new file mode 100644 index 000000000..da8c02d81 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_wmi.p.gz new file mode 100644 index 000000000..9e0baf1cd Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_wmi.p.gz new file mode 100644 index 000000000..f647f9516 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_os.p.gz new file mode 100644 index 000000000..cd1356e9e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_shutil.p.gz new file mode 100644 index 000000000..8add1aafc Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_time.p.gz new file mode 100644 index 000000000..c889f9472 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_uuid.p.gz new file mode 100644 index 000000000..20a8cad07 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_wmi.p.gz new file mode 100644 index 000000000..9fec601ab Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_os.p.gz new file mode 100644 index 000000000..4587a6fda Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_shutil.p.gz new file mode 100644 index 000000000..48cb908c1 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_time.p.gz new file mode 100644 index 000000000..0d15a012e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_uuid.p.gz new file mode 100644 index 000000000..b0b49c932 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_wmi.p.gz new file mode 100644 index 000000000..574ce071e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_os.p.gz new file mode 100644 index 000000000..c19b6e25e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_shutil.p.gz new file mode 100644 index 000000000..1d655bb02 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_time.p.gz new file mode 100644 index 000000000..678b4cd10 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_uuid.p.gz new file mode 100644 index 000000000..0884a350b Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_wmi.p.gz new file mode 100644 index 000000000..128b20ac5 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_os.p.gz new file mode 100644 index 000000000..bc4d4b99d Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_shutil.p.gz new file mode 100644 index 000000000..8de7c4e71 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_time.p.gz new file mode 100644 index 000000000..ee94dd6c2 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_uuid.p.gz new file mode 100644 index 000000000..313bcfa06 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_wmi.p.gz new file mode 100644 index 000000000..de8064431 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_os.p.gz new file mode 100644 index 000000000..e852140a1 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_shutil.p.gz new file mode 100644 index 000000000..f89c63faf Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_time.p.gz new file mode 100644 index 000000000..12cda7550 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_uuid.p.gz new file mode 100644 index 000000000..07445af3e Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_wmi.p.gz new file mode 100644 index 000000000..8e21428f2 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_os.p.gz new file mode 100644 index 000000000..794d9a09d Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_shutil.p.gz new file mode 100644 index 000000000..775f8232c Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_time.p.gz new file mode 100644 index 000000000..d0c0306f2 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_uuid.p.gz new file mode 100644 index 000000000..3cb6c4b7f Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_wmi.p.gz new file mode 100644 index 000000000..a48d4aa9b Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_wmi.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_os.p.gz new file mode 100644 index 000000000..5578f64f8 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_os.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_shutil.p.gz new file mode 100644 index 000000000..224ba464f Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_shutil.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_time.p.gz new file mode 100644 index 000000000..29c15fe82 Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_time.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_uuid.p.gz new file mode 100644 index 000000000..9ac16ec7d Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_uuid.p.gz differ diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_wmi.p.gz new file mode 100644 index 000000000..d6244c3fc Binary files /dev/null and b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_wmi.p.gz differ diff --git a/nova/tests/test_hypervapi.py b/nova/tests/test_hypervapi.py new file mode 100644 index 000000000..8c4296dd3 --- /dev/null +++ b/nova/tests/test_hypervapi.py @@ -0,0 +1,463 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Test suite for the Hyper-V driver and related APIs. +""" + +import os +import shutil +import sys +import uuid + +from nova.compute import power_state +from nova import context +from nova import db +from nova import flags +from nova.image import glance +from nova.tests import fake_network +from nova.tests.hyperv import basetestcase +from nova.tests.hyperv import db_fakes +from nova.tests.hyperv import hypervutils +from nova.tests.hyperv import mockproxy +import nova.tests.image.fake as fake_image +from nova.virt.hyperv import constants +from nova.virt.hyperv import driver as driver_hyperv +from nova.virt.hyperv import vmutils +from nova.virt import images + + +class HyperVAPITestCase(basetestcase.BaseTestCase): + """Unit tests for Hyper-V driver calls.""" + + def setUp(self): + super(HyperVAPITestCase, self).setUp() + + self._user_id = 'fake' + self._project_id = 'fake' + self._instance_data = None + self._image_metadata = None + self._dest_server = None + self._fetched_image = None + self._update_image_raise_exception = False + self._post_method_called = False + self._recover_method_called = False + self._volume_target_portal = '192.168.1.112:3260' + self._volume_id = '10958016-e196-42e3-9e7f-5d8927ae3099' + self._context = context.RequestContext(self._user_id, self._project_id) + + self._setup_stubs() + + self.flags(instances_path=r'C:\Hyper-V\test\instances', + vswitch_name='external') + + self._hypervutils = hypervutils.HyperVUtils() + self._conn = driver_hyperv.HyperVDriver() + + def _setup_stubs(self): + db_fakes.stub_out_db_instance_api(self.stubs) + fake_image.stub_out_image_service(self.stubs) + + def fake_fetch(context, image_id, target, user, project): + self._fetched_image = target + if not os.path.exists(target): + self._hypervutils.create_vhd(target) + self.stubs.Set(images, 'fetch', fake_fetch) + + def fake_get_remote_image_service(context, name): + class FakeGlanceImageService(object): + def update(self_fake, context, image_id, image_metadata, f): + if self._update_image_raise_exception: + raise vmutils.HyperVException( + "Simulated update failure") + self._image_metadata = image_metadata + return (FakeGlanceImageService(), 1) + self.stubs.Set(glance, 'get_remote_image_service', + fake_get_remote_image_service) + + # Modules to mock + modules_to_mock = [ + 'wmi', + 'os', + 'shutil', + 'uuid', + 'time', + 'subprocess', + 'multiprocessing', + '_winreg' + ] + + # Modules in which the mocks are going to be injected + from nova.virt.hyperv import baseops + from nova.virt.hyperv import livemigrationops + from nova.virt.hyperv import snapshotops + from nova.virt.hyperv import vmops + from nova.virt.hyperv import volumeops + from nova.virt.hyperv import volumeutils + + modules_to_test = [ + driver_hyperv, + baseops, + vmops, + vmutils, + volumeops, + volumeutils, + snapshotops, + livemigrationops, + hypervutils, + sys.modules[__name__] + ] + + self._inject_mocks_in_modules(modules_to_mock, modules_to_test) + + if isinstance(snapshotops.wmi, mockproxy.Mock): + from nova.virt.hyperv import ioutils + import StringIO + + def fake_open(name, mode): + return StringIO.StringIO("fake file content") + self.stubs.Set(ioutils, 'open', fake_open) + + def tearDown(self): + try: + if self._instance_data and self._hypervutils.vm_exists( + self._instance_data["name"]): + self._hypervutils.remove_vm(self._instance_data["name"]) + + if self._dest_server and \ + self._hypervutils.remote_vm_exists(self._dest_server, + self._instance_data["name"]): + self._hypervutils.remove_remote_vm(self._dest_server, + self._instance_data["name"]) + + self._hypervutils.logout_iscsi_volume_sessions(self._volume_id) + + shutil.rmtree(flags.FLAGS.instances_path, True) + + fake_image.FakeImageService_reset() + finally: + super(HyperVAPITestCase, self).tearDown() + + def test_list_instances(self): + num_vms = self._hypervutils.get_vm_count() + instances = self._conn.list_instances() + + self.assertEquals(len(instances), num_vms) + + def test_get_info(self): + self._spawn_instance(True) + info = self._conn.get_info(self._instance_data) + + self.assertEquals(info["state"], str(power_state.RUNNING)) + + def test_spawn_cow_image(self): + self._test_spawn_instance(True) + + def test_spawn_no_cow_image(self): + self._test_spawn_instance(False) + + def test_spawn_no_vswitch_exception(self): + # Set flag to a non existing vswitch + self.flags(vswitch_name=str(uuid.uuid4())) + self.assertRaises(vmutils.HyperVException, self._spawn_instance, True) + + self.assertFalse(self._hypervutils.vm_exists( + self._instance_data["name"])) + + def _test_vm_state_change(self, action, from_state, to_state): + self._spawn_instance(True) + if from_state: + self._hypervutils.set_vm_state(self._instance_data["name"], + from_state) + action(self._instance_data) + + vmstate = self._hypervutils.get_vm_state(self._instance_data["name"]) + self.assertEquals(vmstate, to_state) + + def test_pause(self): + self._test_vm_state_change(self._conn.pause, None, + constants.HYPERV_VM_STATE_PAUSED) + + def test_pause_already_paused(self): + self._test_vm_state_change(self._conn.pause, + constants.HYPERV_VM_STATE_PAUSED, + constants.HYPERV_VM_STATE_PAUSED) + + def test_unpause(self): + self._test_vm_state_change(self._conn.unpause, + constants.HYPERV_VM_STATE_PAUSED, + constants.HYPERV_VM_STATE_ENABLED) + + def test_unpause_already_running(self): + self._test_vm_state_change(self._conn.unpause, None, + constants.HYPERV_VM_STATE_ENABLED) + + def test_suspend(self): + self._test_vm_state_change(self._conn.suspend, None, + constants.HYPERV_VM_STATE_SUSPENDED) + + def test_suspend_already_suspended(self): + self._test_vm_state_change(self._conn.suspend, + constants.HYPERV_VM_STATE_SUSPENDED, + constants.HYPERV_VM_STATE_SUSPENDED) + + def test_resume(self): + self._test_vm_state_change(self._conn.resume, + constants.HYPERV_VM_STATE_SUSPENDED, + constants.HYPERV_VM_STATE_ENABLED) + + def test_resume_already_running(self): + self._test_vm_state_change(self._conn.resume, None, + constants.HYPERV_VM_STATE_ENABLED) + + def test_power_off(self): + self._test_vm_state_change(self._conn.power_off, None, + constants.HYPERV_VM_STATE_DISABLED) + + def test_power_off_already_powered_off(self): + self._test_vm_state_change(self._conn.suspend, + constants.HYPERV_VM_STATE_DISABLED, + constants.HYPERV_VM_STATE_DISABLED) + + def test_power_on(self): + self._test_vm_state_change(self._conn.power_on, + constants.HYPERV_VM_STATE_DISABLED, + constants.HYPERV_VM_STATE_ENABLED) + + def test_power_on_already_running(self): + self._test_vm_state_change(self._conn.power_on, None, + constants.HYPERV_VM_STATE_ENABLED) + + def test_reboot(self): + self._spawn_instance(True) + + network_info = fake_network.fake_get_instance_nw_info(self.stubs, + spectacular=True) + self._conn.reboot(self._instance_data, network_info, None) + + vmstate = self._hypervutils.get_vm_state(self._instance_data["name"]) + self.assertEquals(vmstate, constants.HYPERV_VM_STATE_ENABLED) + + def test_destroy(self): + self._spawn_instance(True) + (vhd_paths, _) = self._hypervutils.get_vm_disks( + self._instance_data["name"]) + + self._conn.destroy(self._instance_data) + + self.assertFalse(self._hypervutils.vm_exists( + self._instance_data["name"])) + self._instance_data = None + + for vhd_path in vhd_paths: + self.assertFalse(os.path.exists(vhd_path)) + + def test_live_migration(self): + self.flags(limit_cpu_features=True) + self._spawn_instance(False) + + # Existing server + self._dest_server = "HV12RCTest1" + + self._live_migration(self._dest_server) + + instance_name = self._instance_data["name"] + self.assertFalse(self._hypervutils.vm_exists(instance_name)) + self.assertTrue(self._hypervutils.remote_vm_exists(self._dest_server, + instance_name)) + + self.assertTrue(self._post_method_called) + self.assertFalse(self._recover_method_called) + + def test_live_migration_with_target_failure(self): + self.flags(limit_cpu_features=True) + self._spawn_instance(False) + + dest_server = "nonexistingserver" + + exception_raised = False + try: + self._live_migration(dest_server) + except Exception: + exception_raised = True + + # Cannot use assertRaises with pythoncom.com_error on Linux + self.assertTrue(exception_raised) + + instance_name = self._instance_data["name"] + self.assertTrue(self._hypervutils.vm_exists(instance_name)) + + self.assertFalse(self._post_method_called) + self.assertTrue(self._recover_method_called) + + def _live_migration(self, dest_server): + def fake_post_method(context, instance_ref, dest, block_migration): + self._post_method_called = True + + def fake_recover_method(context, instance_ref, dest, block_migration): + self._recover_method_called = True + + self._conn.live_migration(self._context, self._instance_data, + dest_server, fake_post_method, fake_recover_method) + + def test_pre_live_migration_cow_image(self): + self._test_pre_live_migration(True) + + def test_pre_live_migration_no_cow_image(self): + self._test_pre_live_migration(False) + + def _test_pre_live_migration(self, cow): + self.flags(use_cow_images=cow) + + instance_name = 'openstack_unit_test_vm_' + str(uuid.uuid4()) + + network_info = fake_network.fake_get_instance_nw_info(self.stubs, + spectacular=True) + instance_data = db_fakes.get_fake_instance_data(instance_name, + self._project_id, self._user_id) + block_device_info = None + + self._conn.pre_live_migration(self._context, instance_data, + block_device_info, network_info) + + if cow: + self.assertTrue(not self._fetched_image is None) + else: + self.assertTrue(self._fetched_image is None) + + def test_snapshot_with_update_failure(self): + self._spawn_instance(True) + + self._update_image_raise_exception = True + snapshot_name = 'test_snapshot_' + str(uuid.uuid4()) + self.assertRaises(vmutils.HyperVException, self._conn.snapshot, + self._context, self._instance_data, snapshot_name) + + # assert VM snapshots have been removed + self.assertEquals(self._hypervutils.get_vm_snapshots_count( + self._instance_data["name"]), 0) + + def test_snapshot(self): + self._spawn_instance(True) + + snapshot_name = 'test_snapshot_' + str(uuid.uuid4()) + self._conn.snapshot(self._context, self._instance_data, snapshot_name) + + self.assertTrue(self._image_metadata and + "disk_format" in self._image_metadata and + self._image_metadata["disk_format"] == "vhd") + + # assert VM snapshots have been removed + self.assertEquals(self._hypervutils.get_vm_snapshots_count( + self._instance_data["name"]), 0) + + def _spawn_instance(self, cow, block_device_info=None): + self.flags(use_cow_images=cow) + + instance_name = 'openstack_unit_test_vm_' + str(uuid.uuid4()) + + self._instance_data = db_fakes.get_fake_instance_data(instance_name, + self._project_id, self._user_id) + instance = db.instance_create(self._context, self._instance_data) + + image = db_fakes.get_fake_image_data(self._project_id, self._user_id) + + network_info = fake_network.fake_get_instance_nw_info(self.stubs, + spectacular=True) + + self._conn.spawn(self._context, instance, image, network_info, + block_device_info) + + def _test_spawn_instance(self, cow): + self._spawn_instance(cow) + + self.assertTrue(self._hypervutils.vm_exists( + self._instance_data["name"])) + + vmstate = self._hypervutils.get_vm_state(self._instance_data["name"]) + self.assertEquals(vmstate, constants.HYPERV_VM_STATE_ENABLED) + + (vhd_paths, _) = self._hypervutils.get_vm_disks( + self._instance_data["name"]) + self.assertEquals(len(vhd_paths), 1) + + parent_path = self._hypervutils.get_vhd_parent_path(vhd_paths[0]) + if cow: + self.assertTrue(not parent_path is None) + self.assertEquals(self._fetched_image, parent_path) + else: + self.assertTrue(parent_path is None) + self.assertEquals(self._fetched_image, vhd_paths[0]) + + def _attach_volume(self): + self._spawn_instance(True) + connection_info = db_fakes.get_fake_volume_info_data( + self._volume_target_portal, self._volume_id) + + self._conn.attach_volume(connection_info, + self._instance_data["name"], '/dev/sdc') + + def test_attach_volume(self): + self._attach_volume() + + (_, volumes_paths) = self._hypervutils.get_vm_disks( + self._instance_data["name"]) + self.assertEquals(len(volumes_paths), 1) + + sessions_exist = self._hypervutils.iscsi_volume_sessions_exist( + self._volume_id) + self.assertTrue(sessions_exist) + + def test_detach_volume(self): + self._attach_volume() + connection_info = db_fakes.get_fake_volume_info_data( + self._volume_target_portal, self._volume_id) + + self._conn.detach_volume(connection_info, + self._instance_data["name"], '/dev/sdc') + + (_, volumes_paths) = self._hypervutils.get_vm_disks( + self._instance_data["name"]) + self.assertEquals(len(volumes_paths), 0) + + sessions_exist = self._hypervutils.iscsi_volume_sessions_exist( + self._volume_id) + self.assertFalse(sessions_exist) + + def test_boot_from_volume(self): + block_device_info = db_fakes.get_fake_block_device_info( + self._volume_target_portal, self._volume_id) + + self._spawn_instance(False, block_device_info) + + (_, volumes_paths) = self._hypervutils.get_vm_disks( + self._instance_data["name"]) + + self.assertEquals(len(volumes_paths), 1) + + sessions_exist = self._hypervutils.iscsi_volume_sessions_exist( + self._volume_id) + self.assertTrue(sessions_exist) + + def test_attach_volume_with_target_connection_failure(self): + self._spawn_instance(True) + + target = 'nonexistingtarget:3260' + connection_info = db_fakes.get_fake_volume_info_data(target, + self._volume_id) + + self.assertRaises(vmutils.HyperVException, self._conn.attach_volume, + connection_info, self._instance_data["name"], '/dev/sdc') diff --git a/nova/virt/hyperv/README.rst b/nova/virt/hyperv/README.rst new file mode 100644 index 000000000..c0609f310 --- /dev/null +++ b/nova/virt/hyperv/README.rst @@ -0,0 +1,44 @@ +Hyper-V Volumes Management +============================================= + +To enable the volume features, the first thing that needs to be done is to +enable the iSCSI service on the Windows compute nodes and set it to start +automatically. + +sc config msiscsi start= auto +net start msiscsi + +In Windows Server 2012, it's important to execute the following commands to +prevent having the volumes being online by default: + +diskpart +san policy=OfflineAll +exit + +How to check if your iSCSI configuration is working properly: + +On your OpenStack controller: + +1. Create a volume with e.g. "nova volume-create 1" and note the generated +volume id + +On Windows: + +2. iscsicli QAddTargetPortal +3. iscsicli ListTargets + +The output should contain the iqn related to your volume: +iqn.2010-10.org.openstack:volume- + +How to test Boot from volume in Hyper-V from the OpenStack dashboard: + +1. Fist of all create a volume +2. Get the volume ID of the created volume +3. Upload and untar to the Cloud controller the next VHD image: +http://dev.opennebula.org/attachments/download/482/ttylinux.vhd.gz +4. sudo dd if=/path/to/vhdfileofstep3 +of=/dev/nova-volumes/volume-XXXXX <- Related to the ID of step 2 +5. Launch an instance from any image (this is not important because we are +just booting from a volume) from the dashboard, and don't forget to select +boot from volume and select the volume created in step2. Important: Device +name must be "vda". diff --git a/nova/virt/hyperv/__init__.py b/nova/virt/hyperv/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/nova/virt/hyperv/baseops.py b/nova/virt/hyperv/baseops.py new file mode 100644 index 000000000..3d941a854 --- /dev/null +++ b/nova/virt/hyperv/baseops.py @@ -0,0 +1,61 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Management base class for Hyper-V operations. +""" +import sys + +from nova.openstack.common import log as logging + +# Check needed for unit testing on Unix +if sys.platform == 'win32': + import wmi + +LOG = logging.getLogger(__name__) + + +class BaseOps(object): + def __init__(self): + self.__conn = None + self.__conn_v2 = None + self.__conn_cimv2 = None + self.__conn_wmi = None + + @property + def _conn(self): + if self.__conn is None: + self.__conn = wmi.WMI(moniker='//./root/virtualization') + return self.__conn + + @property + def _conn_v2(self): + if self.__conn_v2 is None: + self.__conn_v2 = wmi.WMI(moniker='//./root/virtualization/v2') + return self.__conn_v2 + + @property + def _conn_cimv2(self): + if self.__conn_cimv2 is None: + self.__conn_cimv2 = wmi.WMI(moniker='//./root/cimv2') + return self.__conn_cimv2 + + @property + def _conn_wmi(self): + if self.__conn_wmi is None: + self.__conn_wmi = wmi.WMI(moniker='//./root/wmi') + return self.__conn_wmi diff --git a/nova/virt/hyperv/constants.py b/nova/virt/hyperv/constants.py new file mode 100644 index 000000000..392dcfa13 --- /dev/null +++ b/nova/virt/hyperv/constants.py @@ -0,0 +1,54 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Constants used in ops classes +""" + +from nova.compute import power_state + +HYPERV_VM_STATE_ENABLED = 2 +HYPERV_VM_STATE_DISABLED = 3 +HYPERV_VM_STATE_REBOOT = 10 +HYPERV_VM_STATE_RESET = 11 +HYPERV_VM_STATE_PAUSED = 32768 +HYPERV_VM_STATE_SUSPENDED = 32769 + +HYPERV_POWER_STATE = { + HYPERV_VM_STATE_DISABLED: power_state.SHUTDOWN, + HYPERV_VM_STATE_ENABLED: power_state.RUNNING, + HYPERV_VM_STATE_PAUSED: power_state.PAUSED, + HYPERV_VM_STATE_SUSPENDED: power_state.SUSPENDED +} + +REQ_POWER_STATE = { + 'Enabled': HYPERV_VM_STATE_ENABLED, + 'Disabled': HYPERV_VM_STATE_DISABLED, + 'Reboot': HYPERV_VM_STATE_REBOOT, + 'Reset': HYPERV_VM_STATE_RESET, + 'Paused': HYPERV_VM_STATE_PAUSED, + 'Suspended': HYPERV_VM_STATE_SUSPENDED, +} + +WMI_JOB_STATUS_STARTED = 4096 +WMI_JOB_STATE_RUNNING = 4 +WMI_JOB_STATE_COMPLETED = 7 + +VM_SUMMARY_NUM_PROCS = 4 +VM_SUMMARY_ENABLED_STATE = 100 +VM_SUMMARY_MEMORY_USAGE = 103 +VM_SUMMARY_UPTIME = 105 diff --git a/nova/virt/hyperv/driver.py b/nova/virt/hyperv/driver.py new file mode 100644 index 000000000..0a29c9426 --- /dev/null +++ b/nova/virt/hyperv/driver.py @@ -0,0 +1,226 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2010 Cloud.com, Inc +# Copyright (c) 2012 Cloudbase Solutions Srl +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +A connection to Hyper-V . +Uses Windows Management Instrumentation (WMI) calls to interact with Hyper-V +Hyper-V WMI usage: + http://msdn.microsoft.com/en-us/library/cc723875%28v=VS.85%29.aspx +The Hyper-V object model briefly: + The physical computer and its hosted virtual machines are each represented + by the Msvm_ComputerSystem class. + + Each virtual machine is associated with a + Msvm_VirtualSystemGlobalSettingData (vs_gs_data) instance and one or more + Msvm_VirtualSystemSettingData (vmsetting) instances. For each vmsetting + there is a series of Msvm_ResourceAllocationSettingData (rasd) objects. + The rasd objects describe the settings for each device in a VM. + Together, the vs_gs_data, vmsettings and rasds describe the configuration + of the virtual machine. + + Creating new resources such as disks and nics involves cloning a default + rasd object and appropriately modifying the clone and calling the + AddVirtualSystemResources WMI method + Changing resources such as memory uses the ModifyVirtualSystemResources + WMI method + +Using the Python WMI library: + Tutorial: + http://timgolden.me.uk/python/wmi/tutorial.html + Hyper-V WMI objects can be retrieved simply by using the class name + of the WMI object and optionally specifying a column to filter the + result set. More complex filters can be formed using WQL (sql-like) + queries. + The parameters and return tuples of WMI method calls can gleaned by + examining the doc string. For example: + >>> vs_man_svc.ModifyVirtualSystemResources.__doc__ + ModifyVirtualSystemResources (ComputerSystem, ResourceSettingData[]) + => (Job, ReturnValue)' + When passing setting data (ResourceSettingData) to the WMI method, + an XML representation of the data is passed in using GetText_(1). + Available methods on a service can be determined using method.keys(): + >>> vs_man_svc.methods.keys() + vmsettings and rasds for a vm can be retrieved using the 'associators' + method with the appropriate return class. + Long running WMI commands generally return a Job (an instance of + Msvm_ConcreteJob) whose state can be polled to determine when it finishes + +""" + +from nova.openstack.common import log as logging +from nova.virt import driver +from nova.virt.hyperv import livemigrationops +from nova.virt.hyperv import snapshotops +from nova.virt.hyperv import vmops +from nova.virt.hyperv import volumeops + +LOG = logging.getLogger(__name__) + + +class HyperVDriver(driver.ComputeDriver): + def __init__(self): + super(HyperVDriver, self).__init__() + + self._volumeops = volumeops.VolumeOps() + self._vmops = vmops.VMOps(self._volumeops) + self._snapshotops = snapshotops.SnapshotOps() + self._livemigrationops = livemigrationops.LiveMigrationOps( + self._volumeops) + + def init_host(self, host): + self._host = host + + def list_instances(self): + return self._vmops.list_instances() + + def list_instances_detail(self): + return self._vmops.list_instances_detail() + + def spawn(self, context, instance, image_meta, network_info, + block_device_info=None): + self._vmops.spawn(context, instance, image_meta, network_info, + block_device_info) + + def reboot(self, instance, network_info, reboot_type): + self._vmops.reboot(instance, network_info, reboot_type) + + def destroy(self, instance, network_info=None, cleanup=True): + self._vmops.destroy(instance, network_info, cleanup) + + def get_info(self, instance): + return self._vmops.get_info(instance) + + def attach_volume(self, connection_info, instance_name, mountpoint): + """Attach volume storage to VM instance""" + return self._volumeops.attach_volume(connection_info, + instance_name, + mountpoint) + + def detach_volume(self, connection_info, instance_name, mountpoint): + """Detach volume storage to VM instance""" + return self._volumeops.detach_volume(connection_info, + instance_name, + mountpoint) + + def get_volume_connector(self, instance): + return self._volumeops.get_volume_connector(instance) + + def poll_rescued_instances(self, timeout): + pass + + def update_available_resource(self, context, host): + self._vmops.update_available_resource(context, host) + + def update_host_status(self): + """See xenapi_conn.py implementation.""" + pass + + def get_host_stats(self, refresh=False): + """See xenapi_conn.py implementation.""" + return {} + + def host_power_action(self, host, action): + """Reboots, shuts down or powers up the host.""" + pass + + def set_host_enabled(self, host, enabled): + """Sets the specified host's ability to accept new instances.""" + pass + + def snapshot(self, context, instance, name): + self._snapshotops.snapshot(context, instance, name) + + def pause(self, instance): + self._vmops.pause(instance) + + def unpause(self, instance): + self._vmops.unpause(instance) + + def suspend(self, instance): + self._vmops.suspend(instance) + + def resume(self, instance): + self._vmops.resume(instance) + + def power_off(self, instance): + self._vmops.power_off(instance) + + def power_on(self, instance): + self._vmops.power_on(instance) + + def live_migration(self, context, instance_ref, dest, post_method, + recover_method, block_migration=False): + self._livemigrationops.live_migration(context, instance_ref, dest, + post_method, recover_method, block_migration) + + def compare_cpu(self, cpu_info): + return self._livemigrationops.compare_cpu(cpu_info) + + def pre_live_migration(self, context, instance, block_device_info, + network_info): + self._livemigrationops.pre_live_migration(context, instance, + block_device_info, network_info) + + def post_live_migration_at_destination(self, ctxt, instance_ref, + network_info, block_migration): + self._livemigrationops.post_live_migration_at_destination(ctxt, + instance_ref, network_info, block_migration) + + def check_can_live_migrate_destination(self, ctxt, instance, + block_migration, disk_over_commit): + pass + + def check_can_live_migrate_destination_cleanup(self, ctxt, + dest_check_data): + pass + + def check_can_live_migrate_source(self, ctxt, instance, dest_check_data): + pass + + def plug_vifs(self, instance, network_info): + LOG.debug(_("plug_vifs called"), instance=instance) + + def unplug_vifs(self, instance, network_info): + LOG.debug(_("plug_vifs called"), instance=instance) + + def ensure_filtering_rules_for_instance(self, instance_ref, network_info): + LOG.debug(_("ensure_filtering_rules_for_instance called"), + instance=instance_ref) + + def unfilter_instance(self, instance, network_info): + """Stop filtering instance""" + LOG.debug(_("unfilter_instance called"), instance=instance) + + def confirm_migration(self, migration, instance, network_info): + """Confirms a resize, destroying the source VM""" + LOG.debug(_("confirm_migration called"), instance=instance) + + def finish_revert_migration(self, instance, network_info): + """Finish reverting a resize, powering back on the instance""" + LOG.debug(_("finish_revert_migration called"), instance=instance) + + def finish_migration(self, context, migration, instance, disk_info, + network_info, image_meta, resize_instance=False): + """Completes a resize, turning on the migrated instance""" + LOG.debug(_("finish_migration called"), instance=instance) + + def get_console_output(self, instance): + LOG.debug(_("get_console_output called"), instance=instance) + return '' + + def legacy_nwinfo(self): + return False diff --git a/nova/virt/hyperv/ioutils.py b/nova/virt/hyperv/ioutils.py new file mode 100644 index 000000000..d927e317f --- /dev/null +++ b/nova/virt/hyperv/ioutils.py @@ -0,0 +1,26 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Utility class to ease the task of creating stubs of built in IO functions. +""" + +import __builtin__ + + +def open(name, mode): + return __builtin__.open(name, mode) diff --git a/nova/virt/hyperv/livemigrationops.py b/nova/virt/hyperv/livemigrationops.py new file mode 100644 index 000000000..1f97adf24 --- /dev/null +++ b/nova/virt/hyperv/livemigrationops.py @@ -0,0 +1,162 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Management class for live migration VM operations. +""" +import os +import sys + +from nova import exception +from nova import flags +from nova.openstack.common import excutils +from nova.openstack.common import log as logging +from nova.virt.hyperv import baseops +from nova.virt.hyperv import constants +from nova.virt.hyperv import vmutils + +# Check needed for unit testing on Unix +if sys.platform == 'win32': + import wmi + +LOG = logging.getLogger(__name__) +FLAGS = flags.FLAGS + + +class LiveMigrationOps(baseops.BaseOps): + def __init__(self, volumeops): + super(LiveMigrationOps, self).__init__() + + self._vmutils = vmutils.VMUtils() + self._volumeops = volumeops + + def _check_live_migration_config(self): + try: + self._conn_v2 + except Exception: + raise vmutils.HyperVException( + _('Live migration is not supported " \ + "by this version of Hyper-V')) + + migration_svc = self._conn_v2.Msvm_VirtualSystemMigrationService()[0] + vsmssd = migration_svc.associators( + wmi_association_class='Msvm_ElementSettingData', + wmi_result_class='Msvm_VirtualSystemMigrationServiceSettingData')[0] + if not vsmssd.EnableVirtualSystemMigration: + raise vmutils.HyperVException( + _('Live migration is not enabled on this host')) + if not migration_svc.MigrationServiceListenerIPAddressList: + raise vmutils.HyperVException( + _('Live migration networks are not configured on this host')) + + def live_migration(self, context, instance_ref, dest, post_method, + recover_method, block_migration=False): + LOG.debug(_("live_migration called"), instance=instance_ref) + instance_name = instance_ref["name"] + + try: + self._check_live_migration_config() + + vm_name = self._vmutils.lookup(self._conn, instance_name) + if vm_name is None: + raise exception.InstanceNotFound(instance=instance_name) + vm = self._conn_v2.Msvm_ComputerSystem( + ElementName=instance_name)[0] + vm_settings = vm.associators( + wmi_association_class='Msvm_SettingsDefineState', + wmi_result_class='Msvm_VirtualSystemSettingData')[0] + + new_resource_setting_data = [] + sasds = vm_settings.associators( + wmi_association_class='Msvm_VirtualSystemSettingDataComponent', + wmi_result_class='Msvm_StorageAllocationSettingData') + for sasd in sasds: + if sasd.ResourceType == 31 and \ + sasd.ResourceSubType == \ + "Microsoft:Hyper-V:Virtual Hard Disk": + #sasd.PoolId = "" + new_resource_setting_data.append(sasd.GetText_(1)) + + LOG.debug(_("Getting live migration networks for remote " + "host: %s"), dest) + _conn_v2_remote = wmi.WMI( + moniker='//' + dest + '/root/virtualization/v2') + migration_svc_remote = \ + _conn_v2_remote.Msvm_VirtualSystemMigrationService()[0] + remote_ip_address_list = \ + migration_svc_remote.MigrationServiceListenerIPAddressList + + # VirtualSystemAndStorage + vsmsd = self._conn_v2.query("select * from " + "Msvm_VirtualSystemMigrationSettingData " + "where MigrationType = 32771")[0] + vsmsd.DestinationIPAddressList = remote_ip_address_list + migration_setting_data = vsmsd.GetText_(1) + + migration_svc =\ + self._conn_v2.Msvm_VirtualSystemMigrationService()[0] + + LOG.debug(_("Starting live migration for instance: %s"), + instance_name) + (job_path, ret_val) = migration_svc.MigrateVirtualSystemToHost( + ComputerSystem=vm.path_(), + DestinationHost=dest, + MigrationSettingData=migration_setting_data, + NewResourceSettingData=new_resource_setting_data) + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._vmutils.check_job_status(job_path) + else: + success = (ret_val == 0) + if not success: + raise vmutils.HyperVException( + _('Failed to live migrate VM %s') % instance_name) + except Exception: + with excutils.save_and_reraise_exception(): + LOG.debug(_("Calling live migration recover_method " + "for instance: %s"), instance_name) + recover_method(context, instance_ref, dest, block_migration) + + LOG.debug(_("Calling live migration post_method for instance: %s"), + instance_name) + post_method(context, instance_ref, dest, block_migration) + + def pre_live_migration(self, context, instance, block_device_info, + network_info): + LOG.debug(_("pre_live_migration called"), instance=instance) + self._check_live_migration_config() + + if FLAGS.use_cow_images: + ebs_root = self._volumeops.volume_in_mapping( + self._volumeops.get_default_root_device(), + block_device_info) + if not ebs_root: + base_vhd_path = self._vmutils.get_base_vhd_path( + instance["image_ref"]) + if not os.path.exists(base_vhd_path): + self._vmutils.fetch_image(base_vhd_path, context, + instance["image_ref"], + instance["user_id"], + instance["project_id"]) + + def post_live_migration_at_destination(self, ctxt, instance_ref, + network_info, block_migration): + LOG.debug(_("post_live_migration_at_destination called"), + instance=instance_ref) + + def compare_cpu(self, cpu_info): + LOG.debug(_("compare_cpu called %s"), cpu_info) + return True diff --git a/nova/virt/hyperv/snapshotops.py b/nova/virt/hyperv/snapshotops.py new file mode 100644 index 000000000..5e4676a4a --- /dev/null +++ b/nova/virt/hyperv/snapshotops.py @@ -0,0 +1,187 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Management class for VM snapshot operations. +""" +import os +import shutil +import sys + +from nova import exception +from nova import flags +from nova.image import glance +from nova.openstack.common import log as logging +from nova.virt.hyperv import baseops +from nova.virt.hyperv import constants +from nova.virt.hyperv import ioutils +from nova.virt.hyperv import vmutils +from xml.etree import ElementTree + +# Check needed for unit testing on Unix +if sys.platform == 'win32': + import wmi + +FLAGS = flags.FLAGS +LOG = logging.getLogger(__name__) + + +class SnapshotOps(baseops.BaseOps): + def __init__(self): + super(SnapshotOps, self).__init__() + self._vmutils = vmutils.VMUtils() + + def snapshot(self, context, instance, name): + """Create snapshot from a running VM instance.""" + instance_name = instance["name"] + vm = self._vmutils.lookup(self._conn, instance_name) + if vm is None: + raise exception.InstanceNotFound(instance=instance_name) + vm = self._conn.Msvm_ComputerSystem(ElementName=instance_name)[0] + vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] + + LOG.debug(_("Creating snapshot for instance %s"), instance_name) + (job_path, ret_val, snap_setting_data) = \ + vs_man_svc.CreateVirtualSystemSnapshot(vm.path_()) + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._vmutils.check_job_status(job_path) + if success: + job_wmi_path = job_path.replace('\\', '/') + job = wmi.WMI(moniker=job_wmi_path) + snap_setting_data = job.associators( + wmi_result_class='Msvm_VirtualSystemSettingData')[0] + else: + success = (ret_val == 0) + if not success: + raise vmutils.HyperVException( + _('Failed to create snapshot for VM %s') % + instance_name) + + export_folder = None + f = None + + try: + src_vhd_path = os.path.join(FLAGS.instances_path, instance_name, + instance_name + ".vhd") + + image_man_svc = self._conn.Msvm_ImageManagementService()[0] + + LOG.debug(_("Getting info for VHD %s"), src_vhd_path) + (src_vhd_info, job_path, ret_val) = \ + image_man_svc.GetVirtualHardDiskInfo(src_vhd_path) + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._vmutils.check_job_status(job_path) + else: + success = (ret_val == 0) + if not success: + raise vmutils.HyperVException( + _("Failed to get info for disk %s") % + (src_vhd_path)) + + src_base_disk_path = None + et = ElementTree.fromstring(src_vhd_info) + for item in et.findall("PROPERTY"): + if item.attrib["NAME"] == "ParentPath": + src_base_disk_path = item.find("VALUE").text + break + + export_folder = self._vmutils.make_export_path(instance_name) + + dest_vhd_path = os.path.join(export_folder, os.path.basename( + src_vhd_path)) + LOG.debug(_('Copying VHD %(src_vhd_path)s to %(dest_vhd_path)s'), + locals()) + shutil.copyfile(src_vhd_path, dest_vhd_path) + + image_vhd_path = None + if not src_base_disk_path: + image_vhd_path = dest_vhd_path + else: + dest_base_disk_path = os.path.join(export_folder, + os.path.basename(src_base_disk_path)) + LOG.debug(_('Copying base disk %(src_vhd_path)s to ' + '%(dest_base_disk_path)s'), locals()) + shutil.copyfile(src_base_disk_path, dest_base_disk_path) + + LOG.debug(_("Reconnecting copied base VHD " + "%(dest_base_disk_path)s and diff VHD %(dest_vhd_path)s"), + locals()) + (job_path, ret_val) = \ + image_man_svc.ReconnectParentVirtualHardDisk( + ChildPath=dest_vhd_path, + ParentPath=dest_base_disk_path, + Force=True) + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._vmutils.check_job_status(job_path) + else: + success = (ret_val == 0) + if not success: + raise vmutils.HyperVException( + _("Failed to reconnect base disk " + "%(dest_base_disk_path)s and diff disk " + "%(dest_vhd_path)s") % + locals()) + + LOG.debug(_("Merging base disk %(dest_base_disk_path)s and " + "diff disk %(dest_vhd_path)s"), + locals()) + (job_path, ret_val) = image_man_svc.MergeVirtualHardDisk( + SourcePath=dest_vhd_path, + DestinationPath=dest_base_disk_path) + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._vmutils.check_job_status(job_path) + else: + success = (ret_val == 0) + if not success: + raise vmutils.HyperVException( + _("Failed to merge base disk %(dest_base_disk_path)s " + "and diff disk %(dest_vhd_path)s") % + locals()) + image_vhd_path = dest_base_disk_path + + (glance_image_service, image_id) = \ + glance.get_remote_image_service(context, name) + image_metadata = {"is_public": False, + "disk_format": "vhd", + "container_format": "bare", + "properties": {}} + f = ioutils.open(image_vhd_path, 'rb') + LOG.debug( + _("Updating Glance image %(image_id)s with content from " + "merged disk %(image_vhd_path)s"), + locals()) + glance_image_service.update(context, image_id, image_metadata, f) + + LOG.debug(_("Snapshot image %(image_id)s updated for VM " + "%(instance_name)s"), locals()) + finally: + LOG.debug(_("Removing snapshot %s"), name) + (job_path, ret_val) = vs_man_svc.RemoveVirtualSystemSnapshot( + snap_setting_data.path_()) + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._vmutils.check_job_status(job_path) + else: + success = (ret_val == 0) + if not success: + raise vmutils.HyperVException( + _('Failed to remove snapshot for VM %s') % + instance_name) + if f: + f.close() + if export_folder: + LOG.debug(_('Removing folder %s '), export_folder) + shutil.rmtree(export_folder) diff --git a/nova/virt/hyperv/vmops.py b/nova/virt/hyperv/vmops.py new file mode 100644 index 000000000..94cb7477e --- /dev/null +++ b/nova/virt/hyperv/vmops.py @@ -0,0 +1,650 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Management class for basic VM operations. +""" +import multiprocessing +import os +import uuid + +from nova import db +from nova import exception +from nova import flags +from nova.openstack.common import cfg +from nova.openstack.common import log as logging +from nova import utils +from nova.virt import driver +from nova.virt.hyperv import baseops +from nova.virt.hyperv import constants +from nova.virt.hyperv import vmutils + +LOG = logging.getLogger(__name__) + +hyperv_opts = [ + cfg.StrOpt('vswitch_name', + default=None, + help='Default vSwitch Name, ' + 'if none provided first external is used'), + cfg.BoolOpt('limit_cpu_features', + default=False, + help='required for live migration among ' + 'hosts with different CPU features') + ] + +FLAGS = flags.FLAGS +FLAGS.register_opts(hyperv_opts) + + +class VMOps(baseops.BaseOps): + def __init__(self, volumeops): + super(VMOps, self).__init__() + + self._vmutils = vmutils.VMUtils() + self._volumeops = volumeops + + def list_instances(self): + """ Return the names of all the instances known to Hyper-V. """ + vms = [v.ElementName + for v in self._conn.Msvm_ComputerSystem(['ElementName'], + Caption="Virtual Machine")] + return vms + + def list_instances_detail(self): + instance_infos = [] + for instance_name in self.list_instances(): + info = self._get_info(instance_name) + instance_info = driver.InstanceInfo( + instance_name, int(info['state'])) + instance_infos.append(instance_info) + return instance_infos + + def get_info(self, instance): + """Get information about the VM""" + LOG.debug(_("get_info called for instance"), instance=instance) + instance_name = instance["name"] + return self._get_info(instance_name) + + def _get_info(self, instance_name): + vm = self._vmutils.lookup(self._conn, instance_name) + if vm is None: + raise exception.InstanceNotFound(instance=instance_name) + vm = self._conn.Msvm_ComputerSystem( + ElementName=instance_name)[0] + vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] + vmsettings = vm.associators( + wmi_association_class='Msvm_SettingsDefineState', + wmi_result_class='Msvm_VirtualSystemSettingData') + settings_paths = [v.path_() for v in vmsettings] + #See http://msdn.microsoft.com/en-us/library/cc160706%28VS.85%29.aspx + summary_info = vs_man_svc.GetSummaryInformation( + [constants.VM_SUMMARY_NUM_PROCS, + constants.VM_SUMMARY_ENABLED_STATE, + constants.VM_SUMMARY_MEMORY_USAGE, + constants.VM_SUMMARY_UPTIME], + settings_paths)[1] + info = summary_info[0] + + LOG.debug(_("hyperv vm state: %s"), info.EnabledState) + state = str(constants.HYPERV_POWER_STATE[info.EnabledState]) + memusage = str(info.MemoryUsage) + numprocs = str(info.NumberOfProcessors) + uptime = str(info.UpTime) + + LOG.debug(_("Got Info for vm %(instance_name)s: state=%(state)s," + " mem=%(memusage)s, num_cpu=%(numprocs)s," + " uptime=%(uptime)s"), locals()) + + return {'state': state, + 'max_mem': info.MemoryUsage, + 'mem': info.MemoryUsage, + 'num_cpu': info.NumberOfProcessors, + 'cpu_time': info.UpTime} + + def spawn(self, context, instance, image_meta, network_info, + block_device_info=None): + """ Create a new VM and start it.""" + instance_name = instance["name"] + vm = self._vmutils.lookup(self._conn, instance_name) + if vm is not None: + raise exception.InstanceExists(name=instance_name) + + ebs_root = self._volumeops.volume_in_mapping( + self._volumeops.get_default_root_device(), + block_device_info) + + #If is not a boot from volume spawn + if not (ebs_root): + #Fetch the file, assume it is a VHD file. + vhdfile = self._vmutils.get_vhd_path(instance_name) + try: + self._cache_image(fn=self._vmutils.fetch_image, + context=context, + target=vhdfile, + fname=instance['image_ref'], + image_id=instance['image_ref'], + user=instance['user_id'], + project=instance['project_id'], + cow=FLAGS.use_cow_images) + except Exception as exn: + LOG.exception(_('cache image failed: %s'), exn) + self.destroy(instance) + + try: + self._create_vm(instance) + + if not ebs_root: + self._create_disk(instance['name'], vhdfile) + else: + self._volumeops.attach_boot_volume(block_device_info, + instance_name) + + #A SCSI controller for volumes connection is created + self._create_scsi_controller(instance['name']) + + for vif in network_info: + mac_address = vif['address'].replace(':', '') + self._create_nic(instance['name'], mac_address) + + LOG.debug(_('Starting VM %s '), instance_name) + self._set_vm_state(instance['name'], 'Enabled') + LOG.info(_('Started VM %s '), instance_name) + except Exception as exn: + LOG.exception(_('spawn vm failed: %s'), exn) + self.destroy(instance) + raise exn + + def _create_vm(self, instance): + """Create a VM but don't start it. """ + instance_name = instance["name"] + vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] + + vs_gs_data = self._conn.Msvm_VirtualSystemGlobalSettingData.new() + vs_gs_data.ElementName = instance_name + (job, ret_val) = vs_man_svc.DefineVirtualSystem( + [], None, vs_gs_data.GetText_(1))[1:] + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._vmutils.check_job_status(job) + else: + success = (ret_val == 0) + + if not success: + raise vmutils.HyperVException(_('Failed to create VM %s') % + instance_name) + + LOG.debug(_('Created VM %s...'), instance_name) + vm = self._conn.Msvm_ComputerSystem(ElementName=instance_name)[0] + + vmsettings = vm.associators( + wmi_result_class='Msvm_VirtualSystemSettingData') + vmsetting = [s for s in vmsettings + if s.SettingType == 3][0] # avoid snapshots + memsetting = vmsetting.associators( + wmi_result_class='Msvm_MemorySettingData')[0] + #No Dynamic Memory, so reservation, limit and quantity are identical. + mem = long(str(instance['memory_mb'])) + memsetting.VirtualQuantity = mem + memsetting.Reservation = mem + memsetting.Limit = mem + + (job, ret_val) = vs_man_svc.ModifyVirtualSystemResources( + vm.path_(), [memsetting.GetText_(1)]) + LOG.debug(_('Set memory for vm %s...'), instance_name) + procsetting = vmsetting.associators( + wmi_result_class='Msvm_ProcessorSettingData')[0] + vcpus = long(instance['vcpus']) + procsetting.VirtualQuantity = vcpus + procsetting.Reservation = vcpus + procsetting.Limit = 100000 # static assignment to 100% + + if FLAGS.limit_cpu_features: + procsetting.LimitProcessorFeatures = True + + (job, ret_val) = vs_man_svc.ModifyVirtualSystemResources( + vm.path_(), [procsetting.GetText_(1)]) + LOG.debug(_('Set vcpus for vm %s...'), instance_name) + + def _create_scsi_controller(self, vm_name): + """ Create an iscsi controller ready to mount volumes """ + LOG.debug(_('Creating a scsi controller for %(vm_name)s for volume ' + 'attaching') % locals()) + vms = self._conn.MSVM_ComputerSystem(ElementName=vm_name) + vm = vms[0] + scsicontrldefault = self._conn.query( + "SELECT * FROM Msvm_ResourceAllocationSettingData \ + WHERE ResourceSubType = 'Microsoft Synthetic SCSI Controller'\ + AND InstanceID LIKE '%Default%'")[0] + if scsicontrldefault is None: + raise vmutils.HyperVException(_('Controller not found')) + scsicontrl = self._vmutils.clone_wmi_obj(self._conn, + 'Msvm_ResourceAllocationSettingData', scsicontrldefault) + scsicontrl.VirtualSystemIdentifiers = ['{' + str(uuid.uuid4()) + '}'] + scsiresource = self._vmutils.add_virt_resource(self._conn, + scsicontrl, vm) + if scsiresource is None: + raise vmutils.HyperVException( + _('Failed to add scsi controller to VM %s') % + vm_name) + + def _create_disk(self, vm_name, vhdfile): + """Create a disk and attach it to the vm""" + LOG.debug(_('Creating disk for %(vm_name)s by attaching' + ' disk file %(vhdfile)s') % locals()) + #Find the IDE controller for the vm. + vms = self._conn.MSVM_ComputerSystem(ElementName=vm_name) + vm = vms[0] + vmsettings = vm.associators( + wmi_result_class='Msvm_VirtualSystemSettingData') + rasds = vmsettings[0].associators( + wmi_result_class='MSVM_ResourceAllocationSettingData') + ctrller = [r for r in rasds + if r.ResourceSubType == 'Microsoft Emulated IDE Controller' + and r.Address == "0"] + #Find the default disk drive object for the vm and clone it. + diskdflt = self._conn.query( + "SELECT * FROM Msvm_ResourceAllocationSettingData \ + WHERE ResourceSubType LIKE 'Microsoft Synthetic Disk Drive'\ + AND InstanceID LIKE '%Default%'")[0] + diskdrive = self._vmutils.clone_wmi_obj(self._conn, + 'Msvm_ResourceAllocationSettingData', diskdflt) + #Set the IDE ctrller as parent. + diskdrive.Parent = ctrller[0].path_() + diskdrive.Address = 0 + #Add the cloned disk drive object to the vm. + new_resources = self._vmutils.add_virt_resource(self._conn, + diskdrive, vm) + if new_resources is None: + raise vmutils.HyperVException( + _('Failed to add diskdrive to VM %s') % + vm_name) + diskdrive_path = new_resources[0] + LOG.debug(_('New disk drive path is %s'), diskdrive_path) + #Find the default VHD disk object. + vhddefault = self._conn.query( + "SELECT * FROM Msvm_ResourceAllocationSettingData \ + WHERE ResourceSubType LIKE 'Microsoft Virtual Hard Disk' AND \ + InstanceID LIKE '%Default%' ")[0] + + #Clone the default and point it to the image file. + vhddisk = self._vmutils.clone_wmi_obj(self._conn, + 'Msvm_ResourceAllocationSettingData', vhddefault) + #Set the new drive as the parent. + vhddisk.Parent = diskdrive_path + vhddisk.Connection = [vhdfile] + + #Add the new vhd object as a virtual hard disk to the vm. + new_resources = self._vmutils.add_virt_resource(self._conn, + vhddisk, vm) + if new_resources is None: + raise vmutils.HyperVException( + _('Failed to add vhd file to VM %s') % + vm_name) + LOG.info(_('Created disk for %s'), vm_name) + + def _create_nic(self, vm_name, mac): + """Create a (synthetic) nic and attach it to the vm""" + LOG.debug(_('Creating nic for %s '), vm_name) + #Find the vswitch that is connected to the physical nic. + vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name) + extswitch = self._find_external_network() + if extswitch is None: + raise vmutils.HyperVException(_('Cannot find vSwitch')) + + vm = vms[0] + switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0] + #Find the default nic and clone it to create a new nic for the vm. + #Use Msvm_SyntheticEthernetPortSettingData for Windows or Linux with + #Linux Integration Components installed. + syntheticnics_data = self._conn.Msvm_SyntheticEthernetPortSettingData() + default_nic_data = [n for n in syntheticnics_data + if n.InstanceID.rfind('Default') > 0] + new_nic_data = self._vmutils.clone_wmi_obj(self._conn, + 'Msvm_SyntheticEthernetPortSettingData', + default_nic_data[0]) + #Create a port on the vswitch. + (new_port, ret_val) = switch_svc.CreateSwitchPort( + Name=str(uuid.uuid4()), + FriendlyName=vm_name, + ScopeOfResidence="", + VirtualSwitch=extswitch.path_()) + if ret_val != 0: + LOG.error(_('Failed creating a port on the external vswitch')) + raise vmutils.HyperVException(_('Failed creating port for %s') % + vm_name) + ext_path = extswitch.path_() + LOG.debug(_("Created switch port %(vm_name)s on switch %(ext_path)s") + % locals()) + #Connect the new nic to the new port. + new_nic_data.Connection = [new_port] + new_nic_data.ElementName = vm_name + ' nic' + new_nic_data.Address = mac + new_nic_data.StaticMacAddress = 'True' + new_nic_data.VirtualSystemIdentifiers = ['{' + str(uuid.uuid4()) + '}'] + #Add the new nic to the vm. + new_resources = self._vmutils.add_virt_resource(self._conn, + new_nic_data, vm) + if new_resources is None: + raise vmutils.HyperVException(_('Failed to add nic to VM %s') % + vm_name) + LOG.info(_("Created nic for %s "), vm_name) + + def _find_external_network(self): + """Find the vswitch that is connected to the physical nic. + Assumes only one physical nic on the host + """ + #If there are no physical nics connected to networks, return. + LOG.debug(_("Attempting to bind NIC to %s ") + % FLAGS.vswitch_name) + if FLAGS.vswitch_name: + LOG.debug(_("Attempting to bind NIC to %s ") + % FLAGS.vswitch_name) + bound = self._conn.Msvm_VirtualSwitch( + ElementName=FLAGS.vswitch_name) + else: + LOG.debug(_("No vSwitch specified, attaching to default")) + self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE') + if len(bound) == 0: + return None + if FLAGS.vswitch_name: + return self._conn.Msvm_VirtualSwitch( + ElementName=FLAGS.vswitch_name)[0]\ + .associators(wmi_result_class='Msvm_SwitchPort')[0]\ + .associators(wmi_result_class='Msvm_VirtualSwitch')[0] + else: + return self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')\ + .associators(wmi_result_class='Msvm_SwitchPort')[0]\ + .associators(wmi_result_class='Msvm_VirtualSwitch')[0] + + def reboot(self, instance, network_info, reboot_type): + instance_name = instance["name"] + """Reboot the specified instance.""" + vm = self._vmutils.lookup(self._conn, instance_name) + if vm is None: + raise exception.InstanceNotFound(instance_id=instance["id"]) + self._set_vm_state(instance_name, 'Reboot') + + def destroy(self, instance, network_info=None, cleanup=True): + """Destroy the VM. Also destroy the associated VHD disk files""" + instance_name = instance["name"] + LOG.debug(_("Got request to destroy vm %s"), instance_name) + vm = self._vmutils.lookup(self._conn, instance_name) + if vm is None: + return + vm = self._conn.Msvm_ComputerSystem(ElementName=instance_name)[0] + vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0] + #Stop the VM first. + self._set_vm_state(instance_name, 'Disabled') + vmsettings = vm.associators( + wmi_result_class='Msvm_VirtualSystemSettingData') + rasds = vmsettings[0].associators( + wmi_result_class='MSVM_ResourceAllocationSettingData') + disks = [r for r in rasds + if r.ResourceSubType == 'Microsoft Virtual Hard Disk'] + disk_files = [] + volumes = [r for r in rasds + if r.ResourceSubType == 'Microsoft Physical Disk Drive'] + volumes_drives_list = [] + #collect the volumes information before destroying the VM. + for volume in volumes: + hostResources = volume.HostResource + drive_path = hostResources[0] + #Appending the Msvm_Disk path + volumes_drives_list.append(drive_path) + #Collect disk file information before destroying the VM. + for disk in disks: + disk_files.extend([c for c in disk.Connection]) + #Nuke the VM. Does not destroy disks. + (job, ret_val) = vs_man_svc.DestroyVirtualSystem(vm.path_()) + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._vmutils.check_job_status(job) + elif ret_val == 0: + success = True + if not success: + raise vmutils.HyperVException(_('Failed to destroy vm %s') % + instance_name) + #Disconnect volumes + for volume_drive in volumes_drives_list: + self._volumeops.disconnect_volume(volume_drive) + #Delete associated vhd disk files. + for disk in disk_files: + vhdfile = self._conn_cimv2.query( + "Select * from CIM_DataFile where Name = '" + + disk.replace("'", "''") + "'")[0] + LOG.debug(_("Del: disk %(vhdfile)s vm %(instance_name)s") + % locals()) + vhdfile.Delete() + + def pause(self, instance): + """Pause VM instance.""" + LOG.debug(_("Pause instance"), instance=instance) + self._set_vm_state(instance["name"], 'Paused') + + def unpause(self, instance): + """Unpause paused VM instance.""" + LOG.debug(_("Unpause instance"), instance=instance) + self._set_vm_state(instance["name"], 'Enabled') + + def suspend(self, instance): + """Suspend the specified instance.""" + print instance + LOG.debug(_("Suspend instance"), instance=instance) + self._set_vm_state(instance["name"], 'Suspended') + + def resume(self, instance): + """Resume the suspended VM instance.""" + LOG.debug(_("Resume instance"), instance=instance) + self._set_vm_state(instance["name"], 'Enabled') + + def power_off(self, instance): + """Power off the specified instance.""" + LOG.debug(_("Power off instance"), instance=instance) + self._set_vm_state(instance["name"], 'Disabled') + + def power_on(self, instance): + """Power on the specified instance""" + LOG.debug(_("Power on instance"), instance=instance) + self._set_vm_state(instance["name"], 'Enabled') + + def _set_vm_state(self, vm_name, req_state): + """Set the desired state of the VM""" + vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name) + if len(vms) == 0: + return False + (job, ret_val) = vms[0].RequestStateChange( + constants.REQ_POWER_STATE[req_state]) + success = False + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._vmutils.check_job_status(job) + elif ret_val == 0: + success = True + elif ret_val == 32775: + #Invalid state for current operation. Typically means it is + #already in the state requested + success = True + if success: + LOG.info(_("Successfully changed vm state of %(vm_name)s" + " to %(req_state)s") % locals()) + else: + msg = _("Failed to change vm state of %(vm_name)s" + " to %(req_state)s") % locals() + LOG.error(msg) + raise vmutils.HyperVException(msg) + + def _get_vcpu_total(self): + """Get vcpu number of physical computer. + :returns: the number of cpu core. + """ + # On certain platforms, this will raise a NotImplementedError. + try: + return multiprocessing.cpu_count() + except NotImplementedError: + LOG.warn(_("Cannot get the number of cpu, because this " + "function is not implemented for this platform. " + "This error can be safely ignored for now.")) + return 0 + + def _get_memory_mb_total(self): + """Get the total memory size(MB) of physical computer. + :returns: the total amount of memory(MB). + """ + total_kb = self._conn_cimv2.query( + "SELECT TotalVisibleMemorySize FROM win32_operatingsystem")[0]\ + .TotalVisibleMemorySize + total_mb = long(total_kb) / 1024 + return total_mb + + def _get_local_gb_total(self): + """Get the total hdd size(GB) of physical computer. + :returns: + The total amount of HDD(GB). + Note that this value shows a partition where + NOVA-INST-DIR/instances mounts. + """ + #TODO(jordanrinke): This binds to C only right now, + #need to bind to instance dir + total_kb = self._conn_cimv2.query( + "SELECT Size FROM win32_logicaldisk WHERE DriveType=3")[0].Size + total_gb = long(total_kb) / (1024 ** 3) + return total_gb + + def _get_vcpu_used(self): + """ Get vcpu usage number of physical computer. + :returns: The total number of vcpu that currently used. + """ + #TODO(jordanrinke) figure out a way to count assigned VCPUs + total_vcpu = 0 + return total_vcpu + + def _get_memory_mb_used(self): + """Get the free memory size(MB) of physical computer. + :returns: the total usage of memory(MB). + """ + total_kb = self._conn_cimv2.query( + "SELECT FreePhysicalMemory FROM win32_operatingsystem")[0]\ + .FreePhysicalMemory + total_mb = long(total_kb) / 1024 + + return total_mb + + def _get_local_gb_used(self): + """Get the free hdd size(GB) of physical computer. + :returns: + The total usage of HDD(GB). + Note that this value shows a partition where + NOVA-INST-DIR/instances mounts. + """ + #TODO(jordanrinke): This binds to C only right now, + #need to bind to instance dir + total_kb = self._conn_cimv2.query( + "SELECT FreeSpace FROM win32_logicaldisk WHERE DriveType=3")[0]\ + .FreeSpace + total_gb = long(total_kb) / (1024 ** 3) + return total_gb + + def _get_hypervisor_version(self): + """Get hypervisor version. + :returns: hypervisor version (ex. 12003) + """ + version = self._conn_cimv2.Win32_OperatingSystem()[0]\ + .Version.replace('.', '') + LOG.info(_('Windows version: %s ') % version) + return version + + def update_available_resource(self, context, host): + """Updates compute manager resource info on ComputeNode table. + + This method is called as an periodic tasks and is used only + in live migration currently. + + :param ctxt: security context + :param host: hostname that compute manager is currently running + + """ + + try: + service_ref = db.service_get_all_compute_by_host(context, host)[0] + except exception.NotFound: + raise exception.ComputeServiceUnavailable(host=host) + + # Updating host information + # TODO(alexpilotti) implemented cpu_info + dic = {'vcpus': self._get_vcpu_total(), + 'memory_mb': self._get_memory_mb_total(), + 'local_gb': self._get_local_gb_total(), + 'vcpus_used': self._get_vcpu_used(), + 'memory_mb_used': self._get_memory_mb_used(), + 'local_gb_used': self._get_local_gb_used(), + 'hypervisor_type': "hyperv", + 'hypervisor_version': self._get_hypervisor_version(), + 'cpu_info': "unknown", + 'service_id': service_ref['id'], + 'disk_available_least': 1} + + compute_node_ref = service_ref['compute_node'] + if not compute_node_ref: + LOG.info(_('Compute_service record created for %s ') % host) + db.compute_node_create(context, dic) + else: + LOG.info(_('Compute_service record updated for %s ') % host) + db.compute_node_update(context, compute_node_ref[0]['id'], dic) + + def _cache_image(self, fn, target, fname, cow=False, Size=None, + *args, **kwargs): + """Wrapper for a method that creates an image that caches the image. + + This wrapper will save the image into a common store and create a + copy for use by the hypervisor. + + The underlying method should specify a kwarg of target representing + where the image will be saved. + + fname is used as the filename of the base image. The filename needs + to be unique to a given image. + + If cow is True, it will make a CoW image instead of a copy. + """ + @utils.synchronized(fname) + def call_if_not_exists(path, fn, *args, **kwargs): + if not os.path.exists(path): + fn(target=path, *args, **kwargs) + + if not os.path.exists(target): + LOG.debug(_("use_cow_image:%s"), cow) + if cow: + base = self._vmutils.get_base_vhd_path(fname) + call_if_not_exists(base, fn, *args, **kwargs) + + image_service = self._conn.query( + "Select * from Msvm_ImageManagementService")[0] + (job, ret_val) = \ + image_service.CreateDifferencingVirtualHardDisk( + Path=target, ParentPath=base) + LOG.debug( + "Creating difference disk: JobID=%s, Source=%s, Target=%s", + job, base, target) + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self._vmutils.check_job_status(job) + else: + success = (ret_val == 0) + + if not success: + raise vmutils.HyperVException( + _('Failed to create Difference Disk from ' + '%(base)s to %(target)s') % locals()) + + else: + call_if_not_exists(target, fn, *args, **kwargs) diff --git a/nova/virt/hyperv/vmutils.py b/nova/virt/hyperv/vmutils.py new file mode 100644 index 000000000..2e54e6d47 --- /dev/null +++ b/nova/virt/hyperv/vmutils.py @@ -0,0 +1,146 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Cloudbase Solutions Srl / Pedro Navarro Perez +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Utility class for VM related operations. +""" + +import os +import shutil +import sys +import time +import uuid + +from nova import exception +from nova import flags +from nova.openstack.common import log as logging +from nova.virt.hyperv import constants +from nova.virt import images + +# Check needed for unit testing on Unix +if sys.platform == 'win32': + import wmi + +FLAGS = flags.FLAGS +LOG = logging.getLogger(__name__) + + +class HyperVException(exception.NovaException): + def __init__(self, message=None): + super(HyperVException, self).__init__(message) + + +class VMUtils(object): + def lookup(self, conn, i): + vms = conn.Msvm_ComputerSystem(ElementName=i) + n = len(vms) + if n == 0: + return None + elif n > 1: + raise HyperVException(_('duplicate name found: %s') % i) + else: + return vms[0].ElementName + + #TODO(alexpilotti): use the reactor to poll instead of sleep + def check_job_status(self, jobpath): + """Poll WMI job state for completion""" + job_wmi_path = jobpath.replace('\\', '/') + job = wmi.WMI(moniker=job_wmi_path) + + while job.JobState == constants.WMI_JOB_STATE_RUNNING: + time.sleep(0.1) + job = wmi.WMI(moniker=job_wmi_path) + if job.JobState != constants.WMI_JOB_STATE_COMPLETED: + LOG.debug(_("WMI job failed: %(ErrorSummaryDescription)s - " + "%(ErrorDescription)s - %(ErrorCode)s") % job) + return False + desc = job.Description + elap = job.ElapsedTime + LOG.debug(_("WMI job succeeded: %(desc)s, Elapsed=%(elap)s ") + % locals()) + return True + + def get_vhd_path(self, instance_name): + base_vhd_folder = os.path.join(FLAGS.instances_path, instance_name) + if not os.path.exists(base_vhd_folder): + LOG.debug(_('Creating folder %s '), base_vhd_folder) + os.makedirs(base_vhd_folder) + return os.path.join(base_vhd_folder, instance_name + ".vhd") + + def get_base_vhd_path(self, image_name): + base_dir = os.path.join(FLAGS.instances_path, '_base') + if not os.path.exists(base_dir): + os.makedirs(base_dir) + return os.path.join(base_dir, image_name + ".vhd") + + def make_export_path(self, instance_name): + export_folder = os.path.join(FLAGS.instances_path, "export", + instance_name) + if os.path.isdir(export_folder): + LOG.debug(_('Removing existing folder %s '), export_folder) + shutil.rmtree(export_folder) + LOG.debug(_('Creating folder %s '), export_folder) + os.makedirs(export_folder) + return export_folder + + def clone_wmi_obj(self, conn, wmi_class, wmi_obj): + """Clone a WMI object""" + cl = conn.__getattr__(wmi_class) # get the class + newinst = cl.new() + #Copy the properties from the original. + for prop in wmi_obj._properties: + if prop == "VirtualSystemIdentifiers": + strguid = [] + strguid.append(str(uuid.uuid4())) + newinst.Properties_.Item(prop).Value = strguid + else: + newinst.Properties_.Item(prop).Value = \ + wmi_obj.Properties_.Item(prop).Value + return newinst + + def add_virt_resource(self, conn, res_setting_data, target_vm): + """Add a new resource (disk/nic) to the VM""" + vs_man_svc = conn.Msvm_VirtualSystemManagementService()[0] + (job, new_resources, ret_val) = vs_man_svc.\ + AddVirtualSystemResources([res_setting_data.GetText_(1)], + target_vm.path_()) + success = True + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self.check_job_status(job) + else: + success = (ret_val == 0) + if success: + return new_resources + else: + return None + + def remove_virt_resource(self, conn, res_setting_data, target_vm): + """Add a new resource (disk/nic) to the VM""" + vs_man_svc = conn.Msvm_VirtualSystemManagementService()[0] + (job, ret_val) = vs_man_svc.\ + RemoveVirtualSystemResources([res_setting_data.path_()], + target_vm.path_()) + success = True + if ret_val == constants.WMI_JOB_STATUS_STARTED: + success = self.check_job_status(job) + else: + success = (ret_val == 0) + return success + + def fetch_image(self, target, context, image_id, user, project, + *args, **kwargs): + images.fetch(context, image_id, target, user, project) diff --git a/nova/virt/hyperv/volumeops.py b/nova/virt/hyperv/volumeops.py new file mode 100644 index 000000000..a8e5299c0 --- /dev/null +++ b/nova/virt/hyperv/volumeops.py @@ -0,0 +1,297 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Pedro Navarro Perez +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Management class for Storage-related functions (attach, detach, etc). +""" +import time + +from nova import block_device +from nova import flags +from nova.openstack.common import cfg +from nova.openstack.common import log as logging +from nova.virt import driver +from nova.virt.hyperv import baseops +from nova.virt.hyperv import vmutils +from nova.virt.hyperv import volumeutils + +LOG = logging.getLogger(__name__) + +hyper_volumeops_opts = [ + cfg.StrOpt('hyperv_attaching_volume_retry_count', + default=10, + help='The number of times we retry on attaching volume '), + cfg.StrOpt('hyperv_wait_between_attach_retry', + default=5, + help='The seconds to wait between an volume attachment attempt'), + ] + +FLAGS = flags.FLAGS +FLAGS.register_opts(hyper_volumeops_opts) + + +class VolumeOps(baseops.BaseOps): + """ + Management class for Volume-related tasks + """ + + def __init__(self): + super(VolumeOps, self).__init__() + + self._vmutils = vmutils.VMUtils() + self._driver = driver + self._block_device = block_device + self._time = time + self._initiator = None + self._default_root_device = 'vda' + self._attaching_volume_retry_count = \ + FLAGS.hyperv_attaching_volume_retry_count + self._wait_between_attach_retry = \ + FLAGS.hyperv_wait_between_attach_retry + self._volutils = volumeutils.VolumeUtils() + + def attach_boot_volume(self, block_device_info, vm_name): + """Attach the boot volume to the IDE controller""" + LOG.debug(_("block device info: %s"), block_device_info) + ebs_root = self._driver.block_device_info_get_mapping( + block_device_info)[0] + connection_info = ebs_root['connection_info'] + data = connection_info['data'] + target_lun = data['target_lun'] + target_iqn = data['target_iqn'] + target_portal = data['target_portal'] + self._volutils.login_storage_target(target_lun, target_iqn, + target_portal) + try: + #Getting the mounted disk + mounted_disk = self._get_mounted_disk_from_lun(target_iqn, + target_lun) + #Attach to IDE controller + #Find the IDE controller for the vm. + vms = self._conn.MSVM_ComputerSystem(ElementName=vm_name) + vm = vms[0] + vmsettings = vm.associators( + wmi_result_class='Msvm_VirtualSystemSettingData') + rasds = vmsettings[0].associators( + wmi_result_class='MSVM_ResourceAllocationSettingData') + ctrller = [r for r in rasds + if r.ResourceSubType == 'Microsoft Emulated IDE Controller' + and r.Address == "0"] + #Attaching to the same slot as the VHD disk file + self._attach_volume_to_controller(ctrller, 0, mounted_disk, vm) + except Exception as exn: + LOG.exception(_('Attach boot from volume failed: %s'), exn) + self._volutils.logout_storage_target(self._conn_wmi, target_iqn) + raise vmutils.HyperVException( + _('Unable to attach boot volume to instance %s') + % vm_name) + + def volume_in_mapping(self, mount_device, block_device_info): + return self._volutils.volume_in_mapping(mount_device, + block_device_info) + + def attach_volume(self, connection_info, instance_name, mountpoint): + """Attach a volume to the SCSI controller""" + LOG.debug(_("Attach_volume: %(connection_info)s, %(instance_name)s," + " %(mountpoint)s") % locals()) + data = connection_info['data'] + target_lun = data['target_lun'] + target_iqn = data['target_iqn'] + target_portal = data['target_portal'] + self._volutils.login_storage_target(target_lun, target_iqn, + target_portal) + try: + #Getting the mounted disk + mounted_disk = self._get_mounted_disk_from_lun(target_iqn, + target_lun) + #Find the SCSI controller for the vm + vms = self._conn.MSVM_ComputerSystem(ElementName=instance_name) + vm = vms[0] + vmsettings = vm.associators( + wmi_result_class='Msvm_VirtualSystemSettingData') + rasds = vmsettings[0].associators( + wmi_result_class='MSVM_ResourceAllocationSettingData') + ctrller = [r for r in rasds + if r.ResourceSubType == 'Microsoft Synthetic SCSI Controller'] + self._attach_volume_to_controller( + ctrller, self._get_free_controller_slot(ctrller[0]), + mounted_disk, vm) + except Exception as exn: + LOG.exception(_('Attach volume failed: %s'), exn) + self._volutils.logout_storage_target(self._conn_wmi, target_iqn) + raise vmutils.HyperVException( + _('Unable to attach volume to instance %s') + % instance_name) + + def _attach_volume_to_controller(self, controller, address, mounted_disk, + instance): + """Attach a volume to a controller """ + #Find the default disk drive object for the vm and clone it. + diskdflt = self._conn.query( + "SELECT * FROM Msvm_ResourceAllocationSettingData \ + WHERE ResourceSubType LIKE 'Microsoft Physical Disk Drive'\ + AND InstanceID LIKE '%Default%'")[0] + diskdrive = self._vmutils.clone_wmi_obj(self._conn, + 'Msvm_ResourceAllocationSettingData', diskdflt) + diskdrive.Address = address + diskdrive.Parent = controller[0].path_() + diskdrive.HostResource = [mounted_disk[0].path_()] + new_resources = self._vmutils.add_virt_resource(self._conn, diskdrive, + instance) + if new_resources is None: + raise vmutils.HyperVException(_('Failed to add volume to VM %s') % + instance) + + def _get_free_controller_slot(self, scsi_controller): + #Getting volumes mounted in the SCSI controller + volumes = self._conn.query( + "SELECT * FROM Msvm_ResourceAllocationSettingData \ + WHERE ResourceSubType LIKE 'Microsoft Physical Disk Drive'\ + AND Parent = '" + scsi_controller.path_() + "'") + #Slots starts from 0, so the lenght of the disks gives us the free slot + return len(volumes) + + def detach_volume(self, connection_info, instance_name, mountpoint): + """Dettach a volume to the SCSI controller""" + LOG.debug(_("Detach_volume: %(connection_info)s, %(instance_name)s," + " %(mountpoint)s") % locals()) + data = connection_info['data'] + target_lun = data['target_lun'] + target_iqn = data['target_iqn'] + #Getting the mounted disk + mounted_disk = self._get_mounted_disk_from_lun(target_iqn, target_lun) + physical_list = self._conn.query( + "SELECT * FROM Msvm_ResourceAllocationSettingData \ + WHERE ResourceSubType LIKE 'Microsoft Physical Disk Drive'") + physical_disk = 0 + for phydisk in physical_list: + host_resource_list = phydisk.HostResource + if host_resource_list is None: + continue + host_resource = str(host_resource_list[0].lower()) + mounted_disk_path = str(mounted_disk[0].path_().lower()) + LOG.debug(_("Mounted disk to detach is: %s"), mounted_disk_path) + LOG.debug(_("host_resource disk detached is: %s"), host_resource) + if host_resource == mounted_disk_path: + physical_disk = phydisk + LOG.debug(_("Physical disk detached is: %s"), physical_disk) + vms = self._conn.MSVM_ComputerSystem(ElementName=instance_name) + vm = vms[0] + remove_result = self._vmutils.remove_virt_resource(self._conn, + physical_disk, vm) + if remove_result is False: + raise vmutils.HyperVException( + _('Failed to remove volume from VM %s') % + instance_name) + #Sending logout + self._volutils.logout_storage_target(self._conn_wmi, target_iqn) + + def get_volume_connector(self, instance): + if not self._initiator: + self._initiator = self._get_iscsi_initiator() + if not self._initiator: + LOG.warn(_('Could not determine iscsi initiator name'), + instance=instance) + return { + 'ip': FLAGS.my_ip, + 'initiator': self._initiator, + } + + def _get_iscsi_initiator(self): + return self._volutils.get_iscsi_initiator(self._conn_cimv2) + + def _get_mounted_disk_from_lun(self, target_iqn, target_lun): + initiator_session = self._conn_wmi.query( + "SELECT * FROM MSiSCSIInitiator_SessionClass \ + WHERE TargetName='" + target_iqn + "'")[0] + devices = initiator_session.Devices + device_number = None + for device in devices: + LOG.debug(_("device.InitiatorName: %s"), device.InitiatorName) + LOG.debug(_("device.TargetName: %s"), device.TargetName) + LOG.debug(_("device.ScsiPortNumber: %s"), device.ScsiPortNumber) + LOG.debug(_("device.ScsiPathId: %s"), device.ScsiPathId) + LOG.debug(_("device.ScsiTargetId): %s"), device.ScsiTargetId) + LOG.debug(_("device.ScsiLun: %s"), device.ScsiLun) + LOG.debug(_("device.DeviceInterfaceGuid :%s"), + device.DeviceInterfaceGuid) + LOG.debug(_("device.DeviceInterfaceName: %s"), + device.DeviceInterfaceName) + LOG.debug(_("device.LegacyName: %s"), device.LegacyName) + LOG.debug(_("device.DeviceType: %s"), device.DeviceType) + LOG.debug(_("device.DeviceNumber %s"), device.DeviceNumber) + LOG.debug(_("device.PartitionNumber :%s"), device.PartitionNumber) + scsi_lun = device.ScsiLun + if scsi_lun == target_lun: + device_number = device.DeviceNumber + if device_number is None: + raise vmutils.HyperVException( + _('Unable to find a mounted disk for' + ' target_iqn: %s') % target_iqn) + LOG.debug(_("Device number : %s"), device_number) + LOG.debug(_("Target lun : %s"), target_lun) + #Finding Mounted disk drive + for i in range(1, self._attaching_volume_retry_count): + mounted_disk = self._conn.query( + "SELECT * FROM Msvm_DiskDrive WHERE DriveNumber=" + + str(device_number) + "") + LOG.debug(_("Mounted disk is: %s"), mounted_disk) + if len(mounted_disk) > 0: + break + self._time.sleep(self._wait_between_attach_retry) + mounted_disk = self._conn.query( + "SELECT * FROM Msvm_DiskDrive WHERE DriveNumber=" + + str(device_number) + "") + LOG.debug(_("Mounted disk is: %s"), mounted_disk) + if len(mounted_disk) == 0: + raise vmutils.HyperVException( + _('Unable to find a mounted disk for' + ' target_iqn: %s') % target_iqn) + return mounted_disk + + def disconnect_volume(self, physical_drive_path): + #Get the session_id of the ISCSI connection + session_id = self._get_session_id_from_mounted_disk( + physical_drive_path) + #Logging out the target + self._volutils.execute_log_out(session_id) + + def _get_session_id_from_mounted_disk(self, physical_drive_path): + drive_number = self._get_drive_number_from_disk_path( + physical_drive_path) + LOG.debug(_("Drive number to disconnect is: %s"), drive_number) + initiator_sessions = self._conn_wmi.query( + "SELECT * FROM MSiSCSIInitiator_SessionClass") + for initiator_session in initiator_sessions: + devices = initiator_session.Devices + for device in devices: + deviceNumber = str(device.DeviceNumber) + LOG.debug(_("DeviceNumber : %s"), deviceNumber) + if deviceNumber == drive_number: + return initiator_session.SessionId + + def _get_drive_number_from_disk_path(self, disk_path): + LOG.debug(_("Disk path to parse: %s"), disk_path) + start_device_id = disk_path.find('"', disk_path.find('DeviceID')) + LOG.debug(_("start_device_id: %s"), start_device_id) + end_device_id = disk_path.find('"', start_device_id + 1) + LOG.debug(_("end_device_id: %s"), end_device_id) + deviceID = disk_path[start_device_id + 1:end_device_id] + return deviceID[deviceID.find("\\") + 2:] + + def get_default_root_device(self): + return self._default_root_device diff --git a/nova/virt/hyperv/volumeutils.py b/nova/virt/hyperv/volumeutils.py new file mode 100644 index 000000000..018a4c278 --- /dev/null +++ b/nova/virt/hyperv/volumeutils.py @@ -0,0 +1,122 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2012 Pedro Navarro Perez +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Helper methods for operations related to the management of volumes, +and storage repositories +""" + +import subprocess +import sys +import time + +from nova import block_device +from nova import flags +from nova.openstack.common import log as logging +from nova.virt import driver +from nova.virt.hyperv import vmutils + +# Check needed for unit testing on Unix +if sys.platform == 'win32': + import _winreg + +LOG = logging.getLogger(__name__) +FLAGS = flags.FLAGS + + +class VolumeUtils(object): + def execute(self, *args, **kwargs): + proc = subprocess.Popen( + [args], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + stdout_value, stderr_value = proc.communicate() + if stdout_value.find('The operation completed successfully') == -1: + raise vmutils.HyperVException(_('An error has occurred when ' + 'calling the iscsi initiator: %s') % stdout_value) + + def get_iscsi_initiator(self, cim_conn): + """Get iscsi initiator name for this machine""" + + computer_system = cim_conn.Win32_ComputerSystem()[0] + hostname = computer_system.name + keypath = \ + r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\iSCSI\Discovery" + try: + key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keypath, 0, + _winreg.KEY_ALL_ACCESS) + temp = _winreg.QueryValueEx(key, 'DefaultInitiatorName') + initiator_name = str(temp[0]) + _winreg.CloseKey(key) + except Exception: + LOG.info(_("The ISCSI initiator name can't be found. " + "Choosing the default one")) + computer_system = cim_conn.Win32_ComputerSystem()[0] + initiator_name = "iqn.1991-05.com.microsoft:" + \ + hostname.lower() + return { + 'ip': FLAGS.my_ip, + 'initiator': initiator_name, + } + + def login_storage_target(self, target_lun, target_iqn, target_portal): + """Add target portal, list targets and logins to the target""" + separator = target_portal.find(':') + target_address = target_portal[:separator] + target_port = target_portal[separator + 1:] + #Adding target portal to iscsi initiator. Sending targets + self.execute('iscsicli.exe ' + 'AddTargetPortal ' + + target_address + ' ' + target_port + + ' * * * * * * * * * * * * *') + #Listing targets + self.execute('iscsicli.exe ' + 'LisTargets') + #Sending login + self.execute('iscsicli.exe ' + 'qlogintarget ' + target_iqn) + #Waiting the disk to be mounted. Research this + time.sleep(FLAGS.hyperv_wait_between_attach_retry) + + def logout_storage_target(self, _conn_wmi, target_iqn): + """ Logs out storage target through its session id """ + + sessions = _conn_wmi.query( + "SELECT * FROM MSiSCSIInitiator_SessionClass \ + WHERE TargetName='" + target_iqn + "'") + for session in sessions: + self.execute_log_out(session.SessionId) + + def execute_log_out(self, session_id): + """ Executes log out of the session described by its session ID """ + self.execute('iscsicli.exe ' + 'logouttarget ' + session_id) + + def volume_in_mapping(self, mount_device, block_device_info): + block_device_list = [block_device.strip_dev(vol['mount_device']) + for vol in + driver.block_device_info_get_mapping( + block_device_info)] + swap = driver.block_device_info_get_swap(block_device_info) + if driver.swap_is_usable(swap): + block_device_list.append( + block_device.strip_dev(swap['device_name'])) + block_device_list += [block_device.strip_dev( + ephemeral['device_name']) + for ephemeral in + driver.block_device_info_get_ephemerals(block_device_info)] + + LOG.debug(_("block_device_list %s"), block_device_list) + return block_device.strip_dev(mount_device) in block_device_list -- cgit