summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xbin/nova-api9
-rwxr-xr-xbin/nova-api-ec22
-rwxr-xr-xbin/nova-dhcpbridge5
-rwxr-xr-xbin/nova-manage9
-rwxr-xr-xbin/nova-novncproxy9
-rwxr-xr-xbin/nova-spicehtml5proxy9
-rw-r--r--contrib/boto_v6/ec2/connection.py4
-rw-r--r--contrib/boto_v6/ec2/instance.py18
-rw-r--r--doc/api_samples/all_extensions/extensions-get-resp.json8
-rw-r--r--doc/api_samples/all_extensions/extensions-get-resp.xml3
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.json5
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.xml4
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.json8
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.xml2
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-create-req.json14
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-create-req.xml12
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-create-resp.json16
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-create-resp.xml15
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-list-resp.json25
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-list-resp.xml23
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.json5
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.xml4
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-show-resp.json23
-rw-r--r--doc/api_samples/os-baremetal-nodes/baremetal-node-show-resp.xml21
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json6
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml5
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json6
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml5
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json3
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml2
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json6
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml5
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json3
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml2
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json3
-rw-r--r--doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml2
-rw-r--r--doc/api_samples/os-floating-ip-pools/floatingippools-list-resp.json10
-rw-r--r--doc/api_samples/os-floating-ip-pools/floatingippools-list-resp.xml5
-rw-r--r--doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.json17
-rw-r--r--doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.xml16
-rw-r--r--doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.json17
-rw-r--r--doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.xml16
-rw-r--r--etc/nova/policy.json23
-rw-r--r--etc/nova/rootwrap.d/compute.filters6
-rw-r--r--nova/api/ec2/cloud.py29
-rw-r--r--nova/api/openstack/compute/contrib/availability_zone.py155
-rw-r--r--nova/api/openstack/compute/contrib/baremetal_nodes.py210
-rw-r--r--nova/api/openstack/compute/contrib/coverage_ext.py11
-rw-r--r--nova/api/openstack/compute/contrib/flavorextraspecs.py14
-rw-r--r--nova/api/openstack/compute/contrib/volumes.py14
-rw-r--r--nova/api/openstack/compute/servers.py4
-rw-r--r--nova/api/openstack/wsgi.py12
-rw-r--r--nova/availability_zones.py23
-rw-r--r--nova/compute/api.py59
-rw-r--r--nova/compute/manager.py34
-rw-r--r--nova/compute/resource_tracker.py6
-rw-r--r--nova/compute/utils.py37
-rw-r--r--nova/conductor/api.py38
-rw-r--r--nova/conductor/manager.py24
-rw-r--r--nova/conductor/rpcapi.py25
-rw-r--r--nova/context.py6
-rw-r--r--nova/db/api.py33
-rw-r--r--nova/db/sqlalchemy/api.py178
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/147_no_service_zones.py6
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/152_change_type_of_deleted_column.py225
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/153_instance_type_in_system_metadata.py49
-rw-r--r--nova/db/sqlalchemy/models.py101
-rw-r--r--nova/db/sqlalchemy/session.py38
-rw-r--r--nova/exception.py25
-rw-r--r--nova/image/glance.py2
-rw-r--r--nova/network/api.py63
-rw-r--r--nova/network/l3.py25
-rw-r--r--nova/network/linux_net.py92
-rw-r--r--nova/network/manager.py113
-rw-r--r--nova/network/model.py5
-rw-r--r--nova/network/quantumv2/__init__.py1
-rw-r--r--nova/network/quantumv2/api.py5
-rw-r--r--nova/notifications.py9
-rw-r--r--nova/scheduler/driver.py2
-rw-r--r--nova/scheduler/filter_scheduler.py12
-rw-r--r--nova/scheduler/filters/affinity_filter.py22
-rw-r--r--nova/scheduler/filters/availability_zone_filter.py1
-rw-r--r--nova/service.py32
-rw-r--r--nova/servicegroup/__init__.py4
-rw-r--r--nova/servicegroup/api.py4
-rw-r--r--nova/servicegroup/drivers/db.py1
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_availability_zone.py247
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_baremetal_nodes.py197
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_coverage_ext.py6
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py17
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_quota_classes.py1
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_quotas.py1
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_security_groups.py3
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_server_diagnostics.py1
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_services.py82
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py2
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_snapshots.py2
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_virtual_interfaces.py1
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_volumes.py4
-rw-r--r--nova/tests/api/openstack/compute/test_limits.py3
-rw-r--r--nova/tests/api/openstack/compute/test_servers.py55
-rw-r--r--nova/tests/api/openstack/compute/test_versions.py1
-rw-r--r--nova/tests/api/openstack/test_common.py8
-rw-r--r--nova/tests/baremetal/test_nova_baremetal_manage.py4
-rw-r--r--nova/tests/baremetal/test_pxe.py44
-rw-r--r--nova/tests/baremetal/test_utils.py37
-rw-r--r--nova/tests/cells/test_cells_messaging.py2
-rw-r--r--nova/tests/compute/test_compute.py128
-rw-r--r--nova/tests/compute/test_compute_utils.py14
-rw-r--r--nova/tests/conductor/test_conductor.py37
-rw-r--r--nova/tests/conf_fixture.py4
-rw-r--r--nova/tests/fake_policy.py22
-rw-r--r--nova/tests/fake_volume.py2
-rw-r--r--nova/tests/fakelibvirt.py6
-rw-r--r--nova/tests/hyperv/README.rst83
-rw-r--r--nova/tests/hyperv/__init__.py16
-rw-r--r--nova/tests/hyperv/basetestcase.py105
-rw-r--r--nova/tests/hyperv/db_fakes.py86
-rw-r--r--nova/tests/hyperv/fake.py46
-rw-r--r--nova/tests/hyperv/hypervutils.py262
-rw-r--r--nova/tests/hyperv/mockproxy.py272
-rw-r--r--nova/tests/hyperv/stubs/README.rst2
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gzbin670 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gzbin2768 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gzbin257 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gzbin660 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gzbin702 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gzbin571 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gzbin277 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gzbin652 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gzbin23220 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gzbin28631 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gzbin385 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gzbin260 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gzbin578 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gzbin20274 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gzbin725 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gzbin426 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gzbin257 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gzbin660 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gzbin31833 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gzbin726 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gzbin250 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gzbin621 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gzbin744 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gzbin267 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gzbin640 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gzbin25238 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gzbin29404 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_nova.utils.p.gzbin278 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_nova.virt.configdrive.p.gzbin603 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gzbin724 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_shutil.p.gzbin300 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gzbin2806 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gzbin441 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gzbin756 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_nova.utils.p.gzbin308 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_nova.virt.configdrive.p.gzbin634 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gzbin753 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_shutil.p.gzbin331 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gzbin605 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gzbin458 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gzbin743 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gzbin21875 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gzbin29013 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_nova.utils.p.gzbin280 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_nova.virt.configdrive.p.gzbin607 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_shutil.p.gzbin303 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gzbin2810 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gzbin443 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gzbin673 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gzbin19822 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_nova.utils.p.gzbin272 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_nova.virt.configdrive.p.gzbin598 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_os.p.gzbin750 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_shutil.p.gzbin294 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_time.p.gzbin416 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_uuid.p.gzbin710 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_wmi.p.gzbin23205 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_nova.utils.p.gzbin277 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_nova.virt.configdrive.p.gzbin606 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gzbin720 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_shutil.p.gzbin300 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gzbin2806 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gzbin441 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gzbin756 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gzbin29674 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_ctypes.p.gzbin929 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_multiprocessing.p.gzbin266 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_os.p.gzbin423 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_shutil.p.gzbin309 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_wmi.p.gzbin1465 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_os.p.gzbin415 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_shutil.p.gzbin301 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_wmi.p.gzbin1075 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_nova.utils.p.gzbin273 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_nova.virt.configdrive.p.gzbin600 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_os.p.gzbin718 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_shutil.p.gzbin295 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_time.p.gzbin417 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_uuid.p.gzbin705 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_wmi.p.gzbin22566 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_shutil.p.gzbin277 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_wmi.p.gzbin7893 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_shutil.p.gzbin301 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_wmi.p.gzbin1071 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_nova.utils.p.gzbin279 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_nova.virt.configdrive.p.gzbin605 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gzbin601 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_shutil.p.gzbin301 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gzbin424 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gzbin716 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_nova.utils.p.gzbin299 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_nova.virt.configdrive.p.gzbin625 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gzbin621 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_shutil.p.gzbin321 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gzbin441 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gzbin732 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gzbin23107 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gzbin25350 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_nova.utils.p.gzbin285 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_nova.virt.configdrive.p.gzbin612 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_os.p.gzbin731 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_shutil.p.gzbin307 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_time.p.gzbin429 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_uuid.p.gzbin720 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_wmi.p.gzbin22768 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_nova.utils.p.gzbin269 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_nova.virt.configdrive.p.gzbin597 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_os.p.gzbin715 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_shutil.p.gzbin292 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_time.p.gzbin414 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_uuid.p.gzbin708 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_wmi.p.gzbin22557 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_nova.utils.p.gzbin294 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_nova.virt.configdrive.p.gzbin620 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_os.p.gzbin738 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_shutil.p.gzbin316 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_time.p.gzbin437 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_uuid.p.gzbin730 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_wmi.p.gzbin22712 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_nova.utils.p.gzbin273 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_nova.virt.configdrive.p.gzbin599 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_os.p.gzbin718 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_shutil.p.gzbin296 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_time.p.gzbin418 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_uuid.p.gzbin710 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_wmi.p.gzbin22466 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_nova.utils.p.gzbin287 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_nova.virt.configdrive.p.gzbin617 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_os.p.gzbin735 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_shutil.p.gzbin311 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_time.p.gzbin434 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_uuid.p.gzbin726 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_wmi.p.gzbin22540 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_nova.utils.p.gzbin273 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_nova.virt.configdrive.p.gzbin600 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_os.p.gzbin717 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_shutil.p.gzbin295 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_time.p.gzbin418 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_uuid.p.gzbin707 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_wmi.p.gzbin22780 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_nova.utils.p.gzbin343 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_os.p.gzbin536 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_shutil.p.gzbin315 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_time.p.gzbin273 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_uuid.p.gzbin386 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_wmi.p.gzbin1642 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_nova.utils.p.gzbin345 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_shutil.p.gzbin318 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_uuid.p.gzbin388 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_wmi.p.gzbin1073 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_nova.utils.p.gzbin270 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_nova.virt.configdrive.p.gzbin597 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_os.p.gzbin716 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_shutil.p.gzbin293 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_time.p.gzbin416 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_uuid.p.gzbin709 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_wmi.p.gzbin22819 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_nova.utils.p.gzbin287 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_nova.virt.configdrive.p.gzbin613 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_os.p.gzbin732 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_shutil.p.gzbin309 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_time.p.gzbin432 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_uuid.p.gzbin723 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_wmi.p.gzbin22530 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_nova.utils.p.gzbin270 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_nova.virt.configdrive.p.gzbin594 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_os.p.gzbin715 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_shutil.p.gzbin293 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_time.p.gzbin418 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_uuid.p.gzbin707 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_wmi.p.gzbin23017 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_nova.utils.p.gzbin272 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_nova.virt.configdrive.p.gzbin600 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_os.p.gzbin1012 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_shutil.p.gzbin433 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_time.p.gzbin419 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_uuid.p.gzbin750 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_nova.utils.p.gzbin292 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_nova.virt.configdrive.p.gzbin619 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_os.p.gzbin1034 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_shutil.p.gzbin458 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_time.p.gzbin439 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_uuid.p.gzbin773 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_wmi.p.gzbin23801 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_wmi.p.gzbin23695 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_nova.utils.p.gzbin289 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_nova.virt.configdrive.p.gzbin828 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_os.p.gzbin890 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_shutil.p.gzbin311 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_time.p.gzbin432 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_uuid.p.gzbin811 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_wmi.p.gzbin30294 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_nova.utils.p.gzbin493 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_nova.virt.configdrive.p.gzbin822 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_os.p.gzbin913 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_shutil.p.gzbin305 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_time.p.gzbin427 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_uuid.p.gzbin804 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_wmi.p.gzbin29760 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_nova.utils.p.gzbin280 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_nova.virt.configdrive.p.gzbin607 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_os.p.gzbin725 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_shutil.p.gzbin302 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_time.p.gzbin424 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_uuid.p.gzbin716 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_wmi.p.gzbin23822 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_nova.utils.p.gzbin285 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_nova.virt.configdrive.p.gzbin616 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_os.p.gzbin731 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_shutil.p.gzbin308 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_time.p.gzbin430 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_uuid.p.gzbin721 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_wmi.p.gzbin23138 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_nova.utils.p.gzbin283 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_nova.virt.configdrive.p.gzbin608 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_os.p.gzbin605 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_shutil.p.gzbin305 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_time.p.gzbin426 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_uuid.p.gzbin717 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_wmi.p.gzbin23617 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.utils.p.gzbin291 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.virt.configdrive.p.gzbin618 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_os.p.gzbin734 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_shutil.p.gzbin313 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_time.p.gzbin430 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_uuid.p.gzbin725 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_wmi.p.gzbin21340 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_nova.utils.p.gzbin291 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_nova.virt.configdrive.p.gzbin616 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_os.p.gzbin734 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_shutil.p.gzbin312 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_time.p.gzbin433 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_uuid.p.gzbin729 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_wmi.p.gzbin22722 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_nova.utils.p.gzbin271 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_nova.virt.configdrive.p.gzbin598 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_os.p.gzbin717 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_shutil.p.gzbin294 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_time.p.gzbin418 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_uuid.p.gzbin710 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_wmi.p.gzbin22741 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_nova.utils.p.gzbin288 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_nova.virt.configdrive.p.gzbin614 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_os.p.gzbin732 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_shutil.p.gzbin310 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_time.p.gzbin432 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_uuid.p.gzbin724 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_wmi.p.gzbin22524 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_nova.utils.p.gzbin272 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_nova.virt.configdrive.p.gzbin599 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_os.p.gzbin716 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_shutil.p.gzbin294 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_time.p.gzbin416 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_uuid.p.gzbin707 -> 0 bytes
-rw-r--r--nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_wmi.p.gzbin22903 -> 0 bytes
-rw-r--r--nova/tests/image/test_glance.py1
-rw-r--r--nova/tests/integrated/api_samples/README.rst18
-rw-r--r--nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl8
-rw-r--r--nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl3
-rw-r--r--nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.json.tpl48
-rw-r--r--nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.xml.tpl44
-rw-r--r--nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.json.tpl18
-rw-r--r--nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.xml.tpl12
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.json.tpl5
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.xml.tpl4
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.json.tpl8
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.xml.tpl7
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-req.json.tpl14
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-req.xml.tpl12
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-resp.json.tpl16
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-resp.xml.tpl15
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-list-resp.json.tpl21
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-list-resp.xml.tpl19
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.json.tpl5
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.xml.tpl4
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-show-resp.json.tpl21
-rw-r--r--nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-show-resp.xml.tpl17
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json.tpl6
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml.tpl5
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json.tpl6
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml.tpl5
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json.tpl3
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml.tpl2
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json.tpl6
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml.tpl5
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json.tpl3
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml.tpl2
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json.tpl3
-rw-r--r--nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml.tpl2
-rw-r--r--nova/tests/integrated/api_samples/os-floating-ip-pools/floatingippools-list-resp.json.tpl10
-rw-r--r--nova/tests/integrated/api_samples/os-floating-ip-pools/floatingippools-list-resp.xml.tpl4
-rw-r--r--nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.json.tpl17
-rw-r--r--nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.xml.tpl16
-rw-r--r--nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.json.tpl17
-rw-r--r--nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.xml.tpl16
-rw-r--r--nova/tests/integrated/test_api_samples.py194
-rw-r--r--nova/tests/integrated/test_multiprocess_api.py4
-rw-r--r--nova/tests/network/test_api.py28
-rw-r--r--nova/tests/network/test_linux_net.py24
-rw-r--r--nova/tests/network/test_manager.py55
-rw-r--r--nova/tests/scheduler/test_host_filters.py33
-rw-r--r--nova/tests/ssl_cert/ca.crt35
-rw-r--r--nova/tests/ssl_cert/certificate.crt30
-rw-r--r--nova/tests/ssl_cert/privatekey.key51
-rw-r--r--nova/tests/test_api.py6
-rw-r--r--nova/tests/test_availability_zones.py1
-rw-r--r--nova/tests/test_bdm.py1
-rw-r--r--nova/tests/test_context.py19
-rw-r--r--nova/tests/test_db_api.py67
-rw-r--r--nova/tests/test_hypervapi.py904
-rw-r--r--nova/tests/test_imagecache.py1
-rw-r--r--nova/tests/test_iptables_network.py19
-rw-r--r--nova/tests/test_libvirt.py22
-rw-r--r--nova/tests/test_libvirt_vif.py128
-rw-r--r--nova/tests/test_metadata.py36
-rw-r--r--nova/tests/test_migrations.py407
-rw-r--r--nova/tests/test_notifications.py2
-rw-r--r--nova/tests/test_periodic_tasks.py19
-rw-r--r--nova/tests/test_sqlalchemy.py63
-rw-r--r--nova/tests/test_utils.py11
-rw-r--r--nova/tests/test_virt_disk.py25
-rw-r--r--nova/tests/test_virt_disk_vfs_localfs.py1
-rw-r--r--nova/tests/test_virt_drivers.py8
-rw-r--r--nova/tests/test_vmwareapi.py99
-rw-r--r--nova/tests/test_wsgi.py118
-rw-r--r--nova/tests/test_xenapi.py68
-rw-r--r--nova/tests/virt/xenapi/imageupload/__init__.py0
-rw-r--r--nova/tests/virt/xenapi/imageupload/test_glance.py74
-rw-r--r--nova/tests/virt/xenapi/test_vm_utils.py2
-rw-r--r--nova/tests/virt/xenapi/test_volumeops.py16
-rw-r--r--nova/utils.py19
-rw-r--r--nova/virt/baremetal/__init__.py4
-rw-r--r--nova/virt/baremetal/db/api.py13
-rw-r--r--nova/virt/baremetal/db/migration.py3
-rw-r--r--nova/virt/baremetal/db/sqlalchemy/api.py71
-rw-r--r--nova/virt/baremetal/db/sqlalchemy/migration.py15
-rw-r--r--nova/virt/baremetal/fake.py4
-rw-r--r--nova/virt/baremetal/ipmi.py16
-rw-r--r--nova/virt/baremetal/pxe.py10
-rw-r--r--nova/virt/baremetal/utils.py26
-rw-r--r--nova/virt/disk/api.py73
-rw-r--r--nova/virt/hyperv/__init__.py16
-rw-r--r--nova/virt/hyperv/baseops.py69
-rw-r--r--nova/virt/hyperv/basevolumeutils.py67
-rw-r--r--nova/virt/hyperv/constants.py9
-rw-r--r--nova/virt/hyperv/driver.py96
-rw-r--r--nova/virt/hyperv/hostops.py164
-rw-r--r--nova/virt/hyperv/hostutils.py74
-rw-r--r--nova/virt/hyperv/ioutils.py26
-rw-r--r--nova/virt/hyperv/livemigrationops.py114
-rw-r--r--nova/virt/hyperv/livemigrationutils.py115
-rw-r--r--nova/virt/hyperv/networkutils.py62
-rw-r--r--nova/virt/hyperv/pathutils.py67
-rw-r--r--nova/virt/hyperv/snapshotops.py160
-rw-r--r--nova/virt/hyperv/vhdutils.py72
-rw-r--r--nova/virt/hyperv/vif.py75
-rw-r--r--nova/virt/hyperv/vmops.py511
-rw-r--r--nova/virt/hyperv/vmutils.py510
-rw-r--r--nova/virt/hyperv/volumeops.py277
-rw-r--r--nova/virt/hyperv/volumeutils.py80
-rw-r--r--nova/virt/hyperv/volumeutilsV2.py70
-rw-r--r--nova/virt/hyperv/volumeutilsv2.py75
-rw-r--r--nova/virt/libvirt/__init__.py4
-rw-r--r--nova/virt/libvirt/driver.py188
-rw-r--r--nova/virt/libvirt/firewall.py13
-rw-r--r--nova/virt/libvirt/utils.py30
-rw-r--r--nova/virt/libvirt/vif.py186
-rw-r--r--nova/virt/libvirt/volume.py74
-rw-r--r--nova/virt/libvirt/volume_nfs.py19
-rw-r--r--nova/virt/powervm/__init__.py4
-rw-r--r--nova/virt/powervm/blockdev.py7
-rw-r--r--nova/virt/powervm/driver.py8
-rw-r--r--nova/virt/powervm/operator.py38
-rw-r--r--nova/virt/vmwareapi/__init__.py4
-rw-r--r--nova/virt/vmwareapi/driver.py210
-rw-r--r--nova/virt/vmwareapi/fake.py86
-rw-r--r--nova/virt/vmwareapi/host.py140
-rw-r--r--nova/virt/vmwareapi/io_util.py9
-rw-r--r--nova/virt/vmwareapi/network_util.py15
-rw-r--r--nova/virt/vmwareapi/read_write_util.py2
-rw-r--r--nova/virt/vmwareapi/vif.py2
-rw-r--r--nova/virt/vmwareapi/vm_util.py276
-rw-r--r--nova/virt/vmwareapi/vmops.py854
-rw-r--r--nova/virt/vmwareapi/vmware_images.py14
-rw-r--r--nova/virt/vmwareapi/volume_util.py177
-rw-r--r--nova/virt/vmwareapi/volumeops.py183
-rw-r--r--nova/virt/xenapi/__init__.py4
-rw-r--r--nova/virt/xenapi/imageupload/__init__.py0
-rw-r--r--nova/virt/xenapi/imageupload/glance.py54
-rw-r--r--nova/virt/xenapi/vm_utils.py40
-rw-r--r--nova/virt/xenapi/vmops.py25
-rw-r--r--nova/virt/xenapi/volumeops.py2
-rw-r--r--nova/volume/cinder.py8
-rw-r--r--nova/wsgi.py98
-rwxr-xr-xrun_tests.sh21
-rw-r--r--smoketests/base.py9
-rw-r--r--smoketests/public_network_smoketests.py1
-rw-r--r--tools/flakes.py4
-rwxr-xr-xtools/hacking.py62
-rw-r--r--tools/pip-requires2
-rw-r--r--tools/test-requires4
-rwxr-xr-xtools/unused_imports.sh4
-rwxr-xr-xtools/xenserver/cleanup_sm_locks.py123
-rw-r--r--tox.ini5
526 files changed, 8941 insertions, 4013 deletions
diff --git a/bin/nova-api b/bin/nova-api
index 8457ea43d..d957f3e58 100755
--- a/bin/nova-api
+++ b/bin/nova-api
@@ -44,13 +44,20 @@ from nova import utils
CONF = cfg.CONF
CONF.import_opt('enabled_apis', 'nova.service')
+CONF.import_opt('enabled_ssl_apis', 'nova.service')
if __name__ == '__main__':
config.parse_args(sys.argv)
logging.setup("nova")
utils.monkey_patch()
+
launcher = service.ProcessLauncher()
for api in CONF.enabled_apis:
- server = service.WSGIService(api)
+ should_use_ssl = api in CONF.enabled_ssl_apis
+ if api == 'ec2':
+ server = service.WSGIService(api, use_ssl=should_use_ssl,
+ max_url_len=16384)
+ else:
+ server = service.WSGIService(api, use_ssl=should_use_ssl)
launcher.launch_server(server, workers=server.workers or 1)
launcher.wait()
diff --git a/bin/nova-api-ec2 b/bin/nova-api-ec2
index c7b08845d..d1b3d45ea 100755
--- a/bin/nova-api-ec2
+++ b/bin/nova-api-ec2
@@ -41,6 +41,6 @@ if __name__ == '__main__':
config.parse_args(sys.argv)
logging.setup("nova")
utils.monkey_patch()
- server = service.WSGIService('ec2')
+ server = service.WSGIService('ec2', max_url_len=16384)
service.serve(server, workers=server.workers)
service.wait()
diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge
index ee7bf2da9..0438ee6ff 100755
--- a/bin/nova-dhcpbridge
+++ b/bin/nova-dhcpbridge
@@ -96,10 +96,15 @@ def init_leases(network_id):
def add_action_parsers(subparsers):
parser = subparsers.add_parser('init')
+ # NOTE(cfb): dnsmasq always passes mac, and ip. hostname
+ # is passed if known. We don't care about
+ # hostname, but argparse will complain if we
+ # do not accept it.
for action in ['add', 'del', 'old']:
parser = subparsers.add_parser(action)
parser.add_argument('mac')
parser.add_argument('ip')
+ parser.add_argument('hostname', nargs='?', default='')
parser.set_defaults(func=globals()[action + '_lease'])
diff --git a/bin/nova-manage b/bin/nova-manage
index 4f3d889ea..90d191eca 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -1128,8 +1128,13 @@ def add_command_parsers(subparsers):
action_kwargs = []
for args, kwargs in getattr(action_fn, 'args', []):
- action_kwargs.append(kwargs['dest'])
- kwargs['dest'] = 'action_kwarg_' + kwargs['dest']
+ if kwargs['dest'].startswith('action_kwarg_'):
+ action_kwargs.append(
+ kwargs['dest'][len('action_kwarg_'):])
+ else:
+ action_kwargs.append(kwargs['dest'])
+ kwargs['dest'] = 'action_kwarg_' + kwargs['dest']
+
parser.add_argument(*args, **kwargs)
parser.set_defaults(action_fn=action_fn)
diff --git a/bin/nova-novncproxy b/bin/nova-novncproxy
index 477510b99..617e2411d 100755
--- a/bin/nova-novncproxy
+++ b/bin/nova-novncproxy
@@ -16,16 +16,16 @@
# License for the specific language governing permissions and limitations
# under the License.
-'''
+"""
Websocket proxy that is compatible with OpenStack Nova
noVNC consoles. Leverages websockify.py by Joel Martin
-'''
+"""
import os
import sys
from nova import config
-from nova.console import websocketproxy as ws
+from nova.console import websocketproxy
from nova.openstack.common import cfg
@@ -77,7 +77,8 @@ if __name__ == '__main__':
sys.exit(-1)
# Create and start the NovaWebSockets proxy
- server = ws.NovaWebSocketProxy(listen_host=CONF.novncproxy_host,
+ server = websocketproxy.NovaWebSocketProxy(
+ listen_host=CONF.novncproxy_host,
listen_port=CONF.novncproxy_port,
source_is_ipv6=CONF.source_is_ipv6,
verbose=CONF.verbose,
diff --git a/bin/nova-spicehtml5proxy b/bin/nova-spicehtml5proxy
index 089ff9d71..405092942 100755
--- a/bin/nova-spicehtml5proxy
+++ b/bin/nova-spicehtml5proxy
@@ -16,16 +16,16 @@
# License for the specific language governing permissions and limitations
# under the License.
-'''
+"""
Websocket proxy that is compatible with OpenStack Nova
SPICE HTML5 consoles. Leverages websockify.py by Joel Martin
-'''
+"""
import os
import sys
from nova import config
-from nova.console import websocketproxy as ws
+from nova.console import websocketproxy
from nova.openstack.common import cfg
@@ -77,7 +77,8 @@ if __name__ == '__main__':
sys.exit(-1)
# Create and start the NovaWebSockets proxy
- server = ws.NovaWebSocketProxy(listen_host=CONF.spicehtml5proxy_host,
+ server = websocketproxy.NovaWebSocketProxy(
+ listen_host=CONF.spicehtml5proxy_host,
listen_port=CONF.spicehtml5proxy_port,
source_is_ipv6=CONF.source_is_ipv6,
verbose=CONF.verbose,
diff --git a/contrib/boto_v6/ec2/connection.py b/contrib/boto_v6/ec2/connection.py
index 940608ffd..4cec65ad8 100644
--- a/contrib/boto_v6/ec2/connection.py
+++ b/contrib/boto_v6/ec2/connection.py
@@ -6,7 +6,7 @@ Created on 2010/12/20
import base64
import boto
import boto.ec2
-from boto.ec2.securitygroup import SecurityGroup
+import boto.ec2.securitygroup as securitygroup
from boto_v6.ec2.instance import ReservationV6
@@ -114,7 +114,7 @@ class EC2ConnectionV6(boto.ec2.EC2Connection):
if security_groups:
l = []
for group in security_groups:
- if isinstance(group, SecurityGroup):
+ if isinstance(group, securitygroup.SecurityGroup):
l.append(group.name)
else:
l.append(group)
diff --git a/contrib/boto_v6/ec2/instance.py b/contrib/boto_v6/ec2/instance.py
index 74adccc00..6f088c67e 100644
--- a/contrib/boto_v6/ec2/instance.py
+++ b/contrib/boto_v6/ec2/instance.py
@@ -3,31 +3,29 @@ Created on 2010/12/20
@author: Nachi Ueno <ueno.nachi@lab.ntt.co.jp>
'''
-from boto.ec2.instance import Group
-from boto.ec2.instance import Instance
-from boto.ec2.instance import Reservation
-from boto.resultset import ResultSet
+from boto.ec2 import instance
+from boto import resultset
-class ReservationV6(Reservation):
+class ReservationV6(instance.Reservation):
def startElement(self, name, attrs, connection):
if name == 'instancesSet':
- self.instances = ResultSet([('item', InstanceV6)])
+ self.instances = resultset.ResultSet([('item', InstanceV6)])
return self.instances
elif name == 'groupSet':
- self.groups = ResultSet([('item', Group)])
+ self.groups = resultset.ResultSet([('item', instance.Group)])
return self.groups
else:
return None
-class InstanceV6(Instance):
+class InstanceV6(instance.Instance):
def __init__(self, connection=None):
- Instance.__init__(self, connection)
+ instance.Instance.__init__(self, connection)
self.dns_name_v6 = None
def endElement(self, name, value, connection):
- Instance.endElement(self, name, value, connection)
+ instance.Instance.endElement(self, name, value, connection)
if name == 'dnsNameV6':
self.dns_name_v6 = value
diff --git a/doc/api_samples/all_extensions/extensions-get-resp.json b/doc/api_samples/all_extensions/extensions-get-resp.json
index bd002c080..604ad6763 100644
--- a/doc/api_samples/all_extensions/extensions-get-resp.json
+++ b/doc/api_samples/all_extensions/extensions-get-resp.json
@@ -89,6 +89,14 @@
"updated": "2012-08-09T00:00:00+00:00"
},
{
+ "alias": "os-baremetal-nodes",
+ "description": "Admin-only bare-metal node administration.",
+ "links": [],
+ "name": "BareMetalNodes",
+ "namespace": "http://docs.openstack.org/compute/ext/baremetal_nodes/api/v2",
+ "updated": "2013-01-04T00:00:00+00:00"
+ },
+ {
"alias": "os-cells",
"description": "Enables cells-related functionality such as adding neighbor cells,\n listing neighbor cells, and getting the capabilities of the local cell.\n ",
"links": [],
diff --git a/doc/api_samples/all_extensions/extensions-get-resp.xml b/doc/api_samples/all_extensions/extensions-get-resp.xml
index ebb1c4302..d7f483745 100644
--- a/doc/api_samples/all_extensions/extensions-get-resp.xml
+++ b/doc/api_samples/all_extensions/extensions-get-resp.xml
@@ -37,6 +37,9 @@
<extension alias="os-availability-zone" updated="2012-08-09T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/availabilityzone/api/v1.1" name="AvailabilityZone">
<description>Add availability_zone to the Create Server v1.1 API.</description>
</extension>
+ <extension alias="os-baremetal-nodes" updated="2013-01-04T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/baremetal_nodes/api/v2" name="BareMetalNodes">
+ <description>Admin-only bare-metal node administration.</description>
+ </extension>
<extension alias="os-cells" updated="2011-09-21T00:00:00+00:00" namespace="http://docs.openstack.org/compute/ext/cells/api/v1.1" name="Cells">
<description>Enables cells-related functionality such as adding child cells,
listing child cells, getting the capabilities of the local cell,
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.json b/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.json
new file mode 100644
index 000000000..2e795e483
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.json
@@ -0,0 +1,5 @@
+{
+ "add_interface": {
+ "address": "aa:aa:aa:aa:aa:aa"
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.xml b/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.xml
new file mode 100644
index 000000000..63ca9c21e
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<add_interface
+ address="aa:aa:aa:aa:aa:aa"
+/> \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.json b/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.json
new file mode 100644
index 000000000..d0b9cc3fb
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.json
@@ -0,0 +1,8 @@
+{
+ "interface": {
+ "address": "aa:aa:aa:aa:aa:aa",
+ "datapath_id": null,
+ "id": 1,
+ "port_no": null
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.xml b/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.xml
new file mode 100644
index 000000000..1da1dd284
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<interface datapath_id="None" id="1" port_no="None" address="aa:aa:aa:aa:aa:aa"/> \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-create-req.json b/doc/api_samples/os-baremetal-nodes/baremetal-node-create-req.json
new file mode 100644
index 000000000..d8b9eb452
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-create-req.json
@@ -0,0 +1,14 @@
+{
+ "node": {
+ "service_host": "host",
+ "cpus": 8,
+ "memory_mb": 8192,
+ "local_gb": 128,
+ "pm_address": "10.1.2.3",
+ "pm_user": "pm_user",
+ "pm_password": "pm_pass",
+ "prov_mac_address": "12:34:56:78:90:ab",
+ "prov_vlan_id": 1234,
+ "terminal_port": 8000
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-create-req.xml b/doc/api_samples/os-baremetal-nodes/baremetal-node-create-req.xml
new file mode 100644
index 000000000..85c863a97
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-create-req.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node
+ service_host="host"
+ cpus="8"
+ memory_mb="8192"
+ local_gb="128"
+ pm_address="10.1.2.3"
+ pm_user="pm_user"
+ prov_mac_address="12:34:56:78:90:ab"
+ prov_vlan_id="1234"
+ terminal_port="8000"
+/> \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-create-resp.json b/doc/api_samples/os-baremetal-nodes/baremetal-node-create-resp.json
new file mode 100644
index 000000000..b62a9e663
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-create-resp.json
@@ -0,0 +1,16 @@
+{
+ "node": {
+ "cpus": 8,
+ "id": 1,
+ "instance_uuid": null,
+ "interfaces": [],
+ "local_gb": 128,
+ "memory_mb": 8192,
+ "pm_address": "10.1.2.3",
+ "pm_user": "pm_user",
+ "prov_mac_address": "12:34:56:78:90:ab",
+ "prov_vlan_id": 1234,
+ "service_host": "host",
+ "terminal_port": 8000
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-create-resp.xml b/doc/api_samples/os-baremetal-nodes/baremetal-node-create-resp.xml
new file mode 100644
index 000000000..9b8421f0f
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-create-resp.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<node
+ instance_uuid="None"
+ pm_address="10.1.2.3"
+ cpus="8"
+ prov_vlan_id="1234"
+ memory_mb="8192"
+ prov_mac_address="12:34:56:78:90:ab"
+ service_host="host"
+ local_gb="128"
+ id="1"
+ pm_user="pm_user"
+ terminal_port="8000">
+ <interfaces/>
+</node> \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-list-resp.json b/doc/api_samples/os-baremetal-nodes/baremetal-node-list-resp.json
new file mode 100644
index 000000000..d43d580ed
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-list-resp.json
@@ -0,0 +1,25 @@
+{
+ "nodes": [
+ {
+ "cpus": 8,
+ "id": 1,
+ "instance_uuid": null,
+ "interfaces": [
+ {
+ "address": "aa:aa:aa:aa:aa:aa",
+ "datapath_id": null,
+ "id": 1,
+ "port_no": null
+ }
+ ],
+ "local_gb": 128,
+ "memory_mb": 8192,
+ "pm_address": "10.1.2.3",
+ "pm_user": "pm_user",
+ "prov_mac_address": "12:34:56:78:90:ab",
+ "prov_vlan_id": 1234,
+ "service_host": "host",
+ "terminal_port": 8000
+ }
+ ]
+} \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-list-resp.xml b/doc/api_samples/os-baremetal-nodes/baremetal-node-list-resp.xml
new file mode 100644
index 000000000..7cd1b5d8a
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-list-resp.xml
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<nodes>
+ <node
+ instance_uuid="None"
+ pm_address="10.1.2.3"
+ cpus="8"
+ prov_vlan_id="1234"
+ memory_mb="8192"
+ prov_mac_address="12:34:56:78:90:ab"
+ service_host="host"
+ local_gb="128"
+ id="1"
+ pm_user="pm_user"
+ terminal_port="8000">
+ <interfaces>
+ <interface
+ datapath_id="None"
+ id="1"
+ port_no="None"
+ address="aa:aa:aa:aa:aa:aa"/>
+ </interfaces>
+ </node>
+</nodes> \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.json b/doc/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.json
new file mode 100644
index 000000000..0ce85577d
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.json
@@ -0,0 +1,5 @@
+{
+ "remove_interface": {
+ "address": "aa:aa:aa:aa:aa:aa"
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.xml b/doc/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.xml
new file mode 100644
index 000000000..6457b059b
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<remove_interface
+ address="aa:aa:aa:aa:aa:aa"
+/> \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-show-resp.json b/doc/api_samples/os-baremetal-nodes/baremetal-node-show-resp.json
new file mode 100644
index 000000000..d42365752
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-show-resp.json
@@ -0,0 +1,23 @@
+{
+ "node": {
+ "cpus": 8,
+ "id": 1,
+ "instance_uuid": null,
+ "interfaces": [
+ {
+ "address": "aa:aa:aa:aa:aa:aa",
+ "datapath_id": null,
+ "id": 1,
+ "port_no": null
+ }
+ ],
+ "local_gb": 128,
+ "memory_mb": 8192,
+ "pm_address": "10.1.2.3",
+ "pm_user": "pm_user",
+ "prov_mac_address": "12:34:56:78:90:ab",
+ "prov_vlan_id": 1234,
+ "service_host": "host",
+ "terminal_port": 8000
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-baremetal-nodes/baremetal-node-show-resp.xml b/doc/api_samples/os-baremetal-nodes/baremetal-node-show-resp.xml
new file mode 100644
index 000000000..6d5f9719f
--- /dev/null
+++ b/doc/api_samples/os-baremetal-nodes/baremetal-node-show-resp.xml
@@ -0,0 +1,21 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<node
+ instance_uuid="None"
+ pm_address="10.1.2.3"
+ cpus="8"
+ prov_vlan_id="1234"
+ memory_mb="8192"
+ prov_mac_address="12:34:56:78:90:ab"
+ service_host="host"
+ local_gb="128"
+ id="1"
+ pm_user="pm_user"
+ terminal_port="8000">
+ <interfaces>
+ <interface
+ datapath_id="None"
+ id="1"
+ port_no="None"
+ address="aa:aa:aa:aa:aa:aa"/>
+ </interfaces>
+</node> \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json
new file mode 100644
index 000000000..63fc8738b
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "value1",
+ "key2": "value2"
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml
new file mode 100644
index 000000000..95c1daab9
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<extra_specs>
+ <key1>value1</key1>
+ <key2>value2</key2>
+</extra_specs> \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json
new file mode 100644
index 000000000..63fc8738b
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "value1",
+ "key2": "value2"
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml
new file mode 100644
index 000000000..06b01a9fc
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_specs>
+ <key2>value2</key2>
+ <key1>value1</key1>
+</extra_specs> \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json
new file mode 100644
index 000000000..e71755fe6
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json
@@ -0,0 +1,3 @@
+{
+ "key1": "value1"
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml
new file mode 100644
index 000000000..d57579ba6
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_spec key="key1">value1</extra_spec> \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json
new file mode 100644
index 000000000..63fc8738b
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "value1",
+ "key2": "value2"
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml
new file mode 100644
index 000000000..06b01a9fc
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_specs>
+ <key2>value2</key2>
+ <key1>value1</key1>
+</extra_specs> \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json
new file mode 100644
index 000000000..a40d79e32
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json
@@ -0,0 +1,3 @@
+{
+ "key1": "new_value1"
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml
new file mode 100644
index 000000000..b7ae6732b
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+ <key1>new_value1</key1> \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json
new file mode 100644
index 000000000..a40d79e32
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json
@@ -0,0 +1,3 @@
+{
+ "key1": "new_value1"
+} \ No newline at end of file
diff --git a/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml
new file mode 100644
index 000000000..13208ad7c
--- /dev/null
+++ b/doc/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_spec key="key1">new_value1</extra_spec> \ No newline at end of file
diff --git a/doc/api_samples/os-floating-ip-pools/floatingippools-list-resp.json b/doc/api_samples/os-floating-ip-pools/floatingippools-list-resp.json
new file mode 100644
index 000000000..7b6482987
--- /dev/null
+++ b/doc/api_samples/os-floating-ip-pools/floatingippools-list-resp.json
@@ -0,0 +1,10 @@
+{
+ "floating_ip_pools": [
+ {
+ "name": "pool1"
+ },
+ {
+ "name": "pool2"
+ }
+ ]
+} \ No newline at end of file
diff --git a/doc/api_samples/os-floating-ip-pools/floatingippools-list-resp.xml b/doc/api_samples/os-floating-ip-pools/floatingippools-list-resp.xml
new file mode 100644
index 000000000..ca09fbf95
--- /dev/null
+++ b/doc/api_samples/os-floating-ip-pools/floatingippools-list-resp.xml
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<floating_ip_pools>
+ <floating_ip_pool name="pool1"/>
+ <floating_ip_pool name="pool2"/>
+</floating_ip_pools> \ No newline at end of file
diff --git a/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.json b/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.json
new file mode 100644
index 000000000..1d308d4ae
--- /dev/null
+++ b/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.json
@@ -0,0 +1,17 @@
+{
+ "instance_usage_audit_logs": {
+ "hosts_not_run": [
+ "f4eb7cfd155f4574967f8b55a7faed75"
+ ],
+ "log": {},
+ "num_hosts": 1,
+ "num_hosts_done": 0,
+ "num_hosts_not_run": 1,
+ "num_hosts_running": 0,
+ "overall_status": "0 of 1 hosts done. 0 errors.",
+ "period_beginning": "2012-12-01 00:00:00",
+ "period_ending": "2013-01-01 00:00:00",
+ "total_errors": 0,
+ "total_instances": 0
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.xml b/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.xml
new file mode 100644
index 000000000..82d157fb9
--- /dev/null
+++ b/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.xml
@@ -0,0 +1,16 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<instance_usage_audit_logs>
+ <total_errors>0</total_errors>
+ <total_instances>0</total_instances>
+ <log/>
+ <num_hosts_running>0</num_hosts_running>
+ <num_hosts_done>0</num_hosts_done>
+ <num_hosts_not_run>1</num_hosts_not_run>
+ <hosts_not_run>
+ <item>107debd115684f098d4c73ffac7ec515</item>
+ </hosts_not_run>
+ <overall_status>0 of 1 hosts done. 0 errors.</overall_status>
+ <period_ending>2013-01-01 00:00:00</period_ending>
+ <period_beginning>2012-12-01 00:00:00</period_beginning>
+ <num_hosts>1</num_hosts>
+</instance_usage_audit_logs> \ No newline at end of file
diff --git a/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.json b/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.json
new file mode 100644
index 000000000..2b5fe54c1
--- /dev/null
+++ b/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.json
@@ -0,0 +1,17 @@
+{
+ "instance_usage_audit_log": {
+ "hosts_not_run": [
+ "8e33da2b48684ef3ab165444d6a7384c"
+ ],
+ "log": {},
+ "num_hosts": 1,
+ "num_hosts_done": 0,
+ "num_hosts_not_run": 1,
+ "num_hosts_running": 0,
+ "overall_status": "0 of 1 hosts done. 0 errors.",
+ "period_beginning": "2012-06-01 00:00:00",
+ "period_ending": "2012-07-01 00:00:00",
+ "total_errors": 0,
+ "total_instances": 0
+ }
+} \ No newline at end of file
diff --git a/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.xml b/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.xml
new file mode 100644
index 000000000..453689737
--- /dev/null
+++ b/doc/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.xml
@@ -0,0 +1,16 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<instance_usage_audit_log>
+ <total_errors>0</total_errors>
+ <total_instances>0</total_instances>
+ <log/>
+ <num_hosts_running>0</num_hosts_running>
+ <num_hosts_done>0</num_hosts_done>
+ <num_hosts_not_run>1</num_hosts_not_run>
+ <hosts_not_run>
+ <item>4b54478b73734afcbf0e2676a3303d1a</item>
+ </hosts_not_run>
+ <overall_status>0 of 1 hosts done. 0 errors.</overall_status>
+ <period_ending>2012-07-01 00:00:00</period_ending>
+ <period_beginning>2012-06-01 00:00:00</period_beginning>
+ <num_hosts>1</num_hosts>
+</instance_usage_audit_log> \ No newline at end of file
diff --git a/etc/nova/policy.json b/etc/nova/policy.json
index fd1f9c2e0..1a446263f 100644
--- a/etc/nova/policy.json
+++ b/etc/nova/policy.json
@@ -29,6 +29,7 @@
"compute_extension:admin_actions:migrate": "rule:admin_api",
"compute_extension:aggregates": "rule:admin_api",
"compute_extension:agents": "rule:admin_api",
+ "compute_extension:baremetal_nodes": "rule:admin_api",
"compute_extension:cells": "rule:admin_api",
"compute_extension:certificates": "",
"compute_extension:cloudpipe": "rule:admin_api",
@@ -82,7 +83,13 @@
"compute_extension:virtual_interfaces": "",
"compute_extension:virtual_storage_arrays": "",
"compute_extension:volumes": "",
+ "compute_extension:volume_attachments:index": "",
+ "compute_extension:volume_attachments:show": "",
+ "compute_extension:volume_attachments:create": "",
+ "compute_extension:volume_attachments:delete": "",
"compute_extension:volumetypes": "",
+ "compute_extension:availability_zone:list": "",
+ "compute_extension:availability_zone:detail": "rule:admin_api",
"volume:create": "",
@@ -99,15 +106,20 @@
"volume_extension:volume_admin_actions:force_delete": "rule:admin_api",
- "network:get_all_networks": "",
- "network:get_network": "",
- "network:delete_network": "",
- "network:disassociate_network": "",
+ "network:get_all": "",
+ "network:get": "",
+ "network:create": "",
+ "network:delete": "",
+ "network:associate": "",
+ "network:disassociate": "",
"network:get_vifs_by_instance": "",
"network:allocate_for_instance": "",
"network:deallocate_for_instance": "",
"network:validate_networks": "",
"network:get_instance_uuids_by_ip_filter": "",
+ "network:get_instance_id_by_floating_address": "",
+ "network:setup_networks_on_host": "",
+ "network:get_backdoor_port": "",
"network:get_floating_ip": "",
"network:get_floating_ip_pools": "",
@@ -118,6 +130,9 @@
"network:deallocate_floating_ip": "",
"network:associate_floating_ip": "",
"network:disassociate_floating_ip": "",
+ "network:release_floating_ip": "",
+ "network:migrate_instance_start": "",
+ "network:migrate_instance_finish": "",
"network:get_fixed_ip": "",
"network:get_fixed_ip_by_address": "",
diff --git a/etc/nova/rootwrap.d/compute.filters b/etc/nova/rootwrap.d/compute.filters
index e1113a9e7..9562a23aa 100644
--- a/etc/nova/rootwrap.d/compute.filters
+++ b/etc/nova/rootwrap.d/compute.filters
@@ -174,3 +174,9 @@ vgs: CommandFilter, /sbin/vgs, root
# nova/virt/baremetal/volume_driver.py: 'tgtadm', '--lld', 'iscsi', ...
tgtadm: CommandFilter, /usr/sbin/tgtadm, root
+
+# nova/utils.py:read_file_as_root: 'cat', file_path
+# (called from nova/virt/disk/vfs/localfs.py:VFSLocalFS.read_file)
+read_passwd: RegExpFilter, cat, root, cat, (/var|/usr)?/tmp/openstack-vfs-localfs[^/]+/etc/passwd
+read_shadow: RegExpFilter, cat, root, cat, (/var|/usr)?/tmp/openstack-vfs-localfs[^/]+/etc/shadow
+
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 31f486b81..48b0f632f 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -250,32 +250,10 @@ class CloudController(object):
else:
return self._describe_availability_zones(context, **kwargs)
- def _get_zones(self, context):
- """Return available and unavailable zones."""
- enabled_services = db.service_get_all(context, False)
- disabled_services = db.service_get_all(context, True)
- enabled_services = availability_zones.set_availability_zones(context,
- enabled_services)
- disabled_services = availability_zones.set_availability_zones(context,
- disabled_services)
-
- available_zones = []
- for zone in [service['availability_zone'] for service
- in enabled_services]:
- if not zone in available_zones:
- available_zones.append(zone)
-
- not_available_zones = []
- zones = [service['available_zones'] for service in disabled_services
- if service['available_zones'] not in available_zones]
- for zone in zones:
- if zone not in not_available_zones:
- not_available_zones.append(zone)
- return (available_zones, not_available_zones)
-
def _describe_availability_zones(self, context, **kwargs):
ctxt = context.elevated()
- available_zones, not_available_zones = self._get_zones(ctxt)
+ available_zones, not_available_zones = \
+ availability_zones.get_availability_zones(ctxt)
result = []
for zone in available_zones:
@@ -291,7 +269,8 @@ class CloudController(object):
def _describe_availability_zones_verbose(self, context, **kwargs):
ctxt = context.elevated()
- available_zones, not_available_zones = self._get_zones(ctxt)
+ available_zones, not_available_zones = \
+ availability_zones.get_availability_zones(ctxt)
# Available services
enabled_services = db.service_get_all(context, False)
diff --git a/nova/api/openstack/compute/contrib/availability_zone.py b/nova/api/openstack/compute/contrib/availability_zone.py
index 2955b68eb..6cde5ca64 100644
--- a/nova/api/openstack/compute/contrib/availability_zone.py
+++ b/nova/api/openstack/compute/contrib/availability_zone.py
@@ -14,14 +14,165 @@
# License for the specific language governing permissions and limitations
# under the License
+from nova.api.openstack import common
from nova.api.openstack import extensions
+from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
+from nova import availability_zones
+from nova import db
+from nova.openstack.common import cfg
+from nova.openstack.common import log as logging
+from nova import servicegroup
+
+
+LOG = logging.getLogger(__name__)
+CONF = cfg.CONF
+
+authorize_list = extensions.extension_authorizer('compute',
+ 'availability_zone:list')
+authorize_detail = extensions.extension_authorizer('compute',
+ 'availability_zone:detail')
+
+
+def make_availability_zone(elem):
+ elem.set('name', 'zoneName')
+
+ zoneStateElem = xmlutil.SubTemplateElement(elem, 'zoneState',
+ selector='zoneState')
+ zoneStateElem.set('available')
+
+ hostsElem = xmlutil.SubTemplateElement(elem, 'hosts', selector='hosts')
+ hostElem = xmlutil.SubTemplateElement(hostsElem, 'host',
+ selector=xmlutil.get_items)
+ hostElem.set('name', 0)
+
+ svcsElem = xmlutil.SubTemplateElement(hostElem, 'services', selector=1)
+ svcElem = xmlutil.SubTemplateElement(svcsElem, 'service',
+ selector=xmlutil.get_items)
+ svcElem.set('name', 0)
+
+ svcStateElem = xmlutil.SubTemplateElement(svcElem, 'serviceState',
+ selector=1)
+ svcStateElem.set('available')
+ svcStateElem.set('active')
+ svcStateElem.set('updated_at')
+
+ # Attach metadata node
+ elem.append(common.MetadataTemplate())
+
+
+class AvailabilityZonesTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('availabilityZones')
+ zoneElem = xmlutil.SubTemplateElement(root, 'availabilityZone',
+ selector='availabilityZoneInfo')
+ make_availability_zone(zoneElem)
+ return xmlutil.MasterTemplate(root, 1, nsmap={
+ Availability_zone.alias: Availability_zone.namespace})
+
+
+class AvailabilityZoneController(wsgi.Controller):
+ """The Availability Zone API controller for the OpenStack API."""
+
+ def __init__(self):
+ super(AvailabilityZoneController, self).__init__()
+ self.servicegroup_api = servicegroup.API()
+
+ def _describe_availability_zones(self, context, **kwargs):
+ ctxt = context.elevated()
+ available_zones, not_available_zones = \
+ availability_zones.get_availability_zones(ctxt)
+
+ result = []
+ for zone in available_zones:
+ # Hide internal_service_availability_zone
+ if zone == CONF.internal_service_availability_zone:
+ continue
+ result.append({'zoneName': zone,
+ 'zoneState': {'available': True},
+ "hosts": None})
+ for zone in not_available_zones:
+ result.append({'zoneName': zone,
+ 'zoneState': {'available': False},
+ "hosts": None})
+ return {'availabilityZoneInfo': result}
+
+ def _describe_availability_zones_verbose(self, context, **kwargs):
+ ctxt = context.elevated()
+ available_zones, not_available_zones = \
+ availability_zones.get_availability_zones(ctxt)
+
+ # Available services
+ enabled_services = db.service_get_all(context, False)
+ enabled_services = availability_zones.set_availability_zones(context,
+ enabled_services)
+ zone_hosts = {}
+ host_services = {}
+ for service in enabled_services:
+ zone_hosts.setdefault(service['availability_zone'], [])
+ if not service['host'] in zone_hosts[service['availability_zone']]:
+ zone_hosts[service['availability_zone']].append(
+ service['host'])
+
+ host_services.setdefault(service['availability_zone'] +
+ service['host'], [])
+ host_services[service['availability_zone'] + service['host']].\
+ append(service)
+
+ result = []
+ for zone in available_zones:
+ hosts = {}
+ for host in zone_hosts[zone]:
+ hosts[host] = {}
+ for service in host_services[zone + host]:
+ alive = self.servicegroup_api.service_is_up(service)
+ hosts[host][service['binary']] = {'available': alive,
+ 'active': True != service['disabled'],
+ 'updated_at': service['updated_at']}
+ result.append({'zoneName': zone,
+ 'zoneState': {'available': True},
+ "hosts": hosts})
+
+ for zone in not_available_zones:
+ result.append({'zoneName': zone,
+ 'zoneState': {'available': False},
+ "hosts": None})
+ return {'availabilityZoneInfo': result}
+
+ @wsgi.serializers(xml=AvailabilityZonesTemplate)
+ def index(self, req):
+ """Returns a summary list of availability zone."""
+ context = req.environ['nova.context']
+ authorize_list(context)
+
+ return self._describe_availability_zones(context)
+
+ @wsgi.serializers(xml=AvailabilityZonesTemplate)
+ def detail(self, req):
+ """Returns a detailed list of availability zone."""
+ context = req.environ['nova.context']
+ authorize_detail(context)
+
+ return self._describe_availability_zones_verbose(context)
class Availability_zone(extensions.ExtensionDescriptor):
- """Add availability_zone to the Create Server v1.1 API."""
+ """1. Add availability_zone to the Create Server v1.1 API.
+ 2. Add availability zones describing.
+ """
name = "AvailabilityZone"
alias = "os-availability-zone"
namespace = ("http://docs.openstack.org/compute/ext/"
"availabilityzone/api/v1.1")
- updated = "2012-08-09T00:00:00+00:00"
+ updated = "2012-12-21T00:00:00+00:00"
+
+ def get_resources(self):
+ resources = []
+
+ res = extensions.ResourceExtension('os-availability-zone',
+ AvailabilityZoneController(),
+ collection_actions={'detail': 'GET'})
+ resources.append(res)
+
+ return resources
diff --git a/nova/api/openstack/compute/contrib/baremetal_nodes.py b/nova/api/openstack/compute/contrib/baremetal_nodes.py
new file mode 100644
index 000000000..38d66d2ae
--- /dev/null
+++ b/nova/api/openstack/compute/contrib/baremetal_nodes.py
@@ -0,0 +1,210 @@
+# Copyright (c) 2013 NTT DOCOMO, INC.
+# 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.
+
+"""The bare-metal admin extension."""
+
+import webob
+
+from nova.api.openstack import extensions
+from nova.api.openstack import wsgi
+from nova.api.openstack import xmlutil
+from nova import exception
+from nova.openstack.common import log as logging
+from nova.virt.baremetal import db
+
+LOG = logging.getLogger(__name__)
+authorize = extensions.extension_authorizer('compute', 'baremetal_nodes')
+
+node_fields = ['id', 'cpus', 'local_gb', 'memory_mb', 'pm_address',
+ 'pm_user', 'prov_mac_address', 'prov_vlan_id',
+ 'service_host', 'terminal_port', 'instance_uuid',
+ ]
+
+interface_fields = ['id', 'address', 'datapath_id', 'port_no']
+
+
+def _node_dict(node_ref):
+ d = {}
+ for f in node_fields:
+ d[f] = node_ref.get(f)
+ return d
+
+
+def _interface_dict(interface_ref):
+ d = {}
+ for f in interface_fields:
+ d[f] = interface_ref.get(f)
+ return d
+
+
+def _make_node_elem(elem):
+ for f in node_fields:
+ elem.set(f)
+
+
+def _make_interface_elem(elem):
+ for f in interface_fields:
+ elem.set(f)
+
+
+class NodeTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ node_elem = xmlutil.TemplateElement('node', selector='node')
+ _make_node_elem(node_elem)
+ ifs_elem = xmlutil.TemplateElement('interfaces')
+ if_elem = xmlutil.SubTemplateElement(ifs_elem, 'interface',
+ selector='interfaces')
+ _make_interface_elem(if_elem)
+ node_elem.append(ifs_elem)
+ return xmlutil.MasterTemplate(node_elem, 1)
+
+
+class NodesTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('nodes')
+ node_elem = xmlutil.SubTemplateElement(root, 'node', selector='nodes')
+ _make_node_elem(node_elem)
+ ifs_elem = xmlutil.TemplateElement('interfaces')
+ if_elem = xmlutil.SubTemplateElement(ifs_elem, 'interface',
+ selector='interfaces')
+ _make_interface_elem(if_elem)
+ node_elem.append(ifs_elem)
+ return xmlutil.MasterTemplate(root, 1)
+
+
+class InterfaceTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ root = xmlutil.TemplateElement('interface', selector='interface')
+ _make_interface_elem(root)
+ return xmlutil.MasterTemplate(root, 1)
+
+
+class BareMetalNodeController(wsgi.Controller):
+ """The Bare-Metal Node API controller for the OpenStack API."""
+
+ @wsgi.serializers(xml=NodesTemplate)
+ def index(self, req):
+ context = req.environ['nova.context']
+ authorize(context)
+ nodes_from_db = db.bm_node_get_all(context)
+ nodes = []
+ for node_from_db in nodes_from_db:
+ try:
+ ifs = db.bm_interface_get_all_by_bm_node_id(
+ context, node_from_db['id'])
+ except exception.InstanceNotFound:
+ ifs = []
+ node = _node_dict(node_from_db)
+ node['interfaces'] = [_interface_dict(i) for i in ifs]
+ nodes.append(node)
+ return {'nodes': nodes}
+
+ @wsgi.serializers(xml=NodeTemplate)
+ def show(self, req, id):
+ context = req.environ['nova.context']
+ authorize(context)
+ try:
+ node = db.bm_node_get(context, id)
+ except exception.InstanceNotFound:
+ raise webob.exc.HTTPNotFound
+ try:
+ ifs = db.bm_interface_get_all_by_bm_node_id(context, id)
+ except exception.InstanceNotFound:
+ ifs = []
+ node = _node_dict(node)
+ node['interfaces'] = [_interface_dict(i) for i in ifs]
+ return {'node': node}
+
+ @wsgi.serializers(xml=NodeTemplate)
+ def create(self, req, body):
+ context = req.environ['nova.context']
+ authorize(context)
+ node = db.bm_node_create(context, body['node'])
+ node = _node_dict(node)
+ node['interfaces'] = []
+ return {'node': node}
+
+ def delete(self, req, id):
+ context = req.environ['nova.context']
+ authorize(context)
+ try:
+ db.bm_node_destroy(context, id)
+ except exception.InstanceNotFound:
+ raise webob.exc.HTTPNotFound
+ return webob.Response(status_int=202)
+
+ def _check_node_exists(self, context, node_id):
+ try:
+ db.bm_node_get(context, node_id)
+ except exception.InstanceNotFound:
+ raise webob.exc.HTTPNotFound
+
+ @wsgi.serializers(xml=InterfaceTemplate)
+ @wsgi.action('add_interface')
+ def _add_interface(self, req, id, body):
+ context = req.environ['nova.context']
+ authorize(context)
+ self._check_node_exists(context, id)
+ body = body['add_interface']
+ address = body['address']
+ datapath_id = body.get('datapath_id')
+ port_no = body.get('port_no')
+ if_id = db.bm_interface_create(context,
+ bm_node_id=id,
+ address=address,
+ datapath_id=datapath_id,
+ port_no=port_no)
+ if_ref = db.bm_interface_get(context, if_id)
+ return {'interface': _interface_dict(if_ref)}
+
+ @wsgi.response(202)
+ @wsgi.action('remove_interface')
+ def _remove_interface(self, req, id, body):
+ context = req.environ['nova.context']
+ authorize(context)
+ self._check_node_exists(context, id)
+ body = body['remove_interface']
+ print "body(%s)" % body
+ if_id = body.get('id')
+ address = body.get('address')
+ if not if_id and not address:
+ raise webob.exc.HTTPBadRequest(
+ explanation=_("Must specify id or address"))
+ ifs = db.bm_interface_get_all_by_bm_node_id(context, id)
+ for i in ifs:
+ if if_id and if_id != i['id']:
+ continue
+ if address and address != i['address']:
+ continue
+ db.bm_interface_destroy(context, i['id'])
+ return webob.Response(status_int=202)
+ raise webob.exc.HTTPNotFound
+
+
+class Baremetal_nodes(extensions.ExtensionDescriptor):
+ """Admin-only bare-metal node administration."""
+
+ name = "BareMetalNodes"
+ alias = "os-baremetal-nodes"
+ namespace = "http://docs.openstack.org/compute/ext/baremetal_nodes/api/v2"
+ updated = "2013-01-04T00:00:00+00:00"
+
+ def get_resources(self):
+ resources = []
+ res = extensions.ResourceExtension('os-baremetal-nodes',
+ BareMetalNodeController(),
+ member_actions={"action": "POST", })
+ resources.append(res)
+ return resources
diff --git a/nova/api/openstack/compute/contrib/coverage_ext.py b/nova/api/openstack/compute/contrib/coverage_ext.py
index 4b7d4e57f..6edf9244f 100644
--- a/nova/api/openstack/compute/contrib/coverage_ext.py
+++ b/nova/api/openstack/compute/contrib/coverage_ext.py
@@ -23,7 +23,6 @@ import sys
import telnetlib
import tempfile
-from coverage import coverage
from webob import exc
from nova.api.openstack import extensions
@@ -47,7 +46,6 @@ class CoverageController(object):
def __init__(self):
self.data_path = tempfile.mkdtemp(prefix='nova-coverage_')
data_out = os.path.join(self.data_path, '.nova-coverage')
- self.coverInst = coverage(data_file=data_out)
self.compute_api = compute_api.API()
self.network_api = network_api.API()
self.conductor_api = conductor_api.API()
@@ -57,6 +55,12 @@ class CoverageController(object):
self.cert_api = cert_api.CertAPI()
self.services = []
self.combine = False
+ try:
+ import coverage
+ self.coverInst = coverage.coverage(data_file=data_out)
+ self.has_coverage = True
+ except ImportError:
+ self.has_coverage = False
super(CoverageController, self).__init__()
def _find_services(self, req):
@@ -238,6 +242,9 @@ class CoverageController(object):
'report': self._report_coverage,
}
authorize(req.environ['nova.context'])
+ if not self.has_coverage:
+ msg = _("Python coverage module is not installed.")
+ raise exc.HTTPServiceUnavailable(explanation=msg)
for action, data in body.iteritems():
if action == 'stop':
return _actions[action](req)
diff --git a/nova/api/openstack/compute/contrib/flavorextraspecs.py b/nova/api/openstack/compute/contrib/flavorextraspecs.py
index c8deb7b4c..84f157b6a 100644
--- a/nova/api/openstack/compute/contrib/flavorextraspecs.py
+++ b/nova/api/openstack/compute/contrib/flavorextraspecs.py
@@ -34,6 +34,15 @@ class ExtraSpecsTemplate(xmlutil.TemplateBuilder):
return xmlutil.MasterTemplate(xmlutil.make_flat_dict('extra_specs'), 1)
+class ExtraSpecTemplate(xmlutil.TemplateBuilder):
+ def construct(self):
+ sel = xmlutil.Selector(xmlutil.get_items, 0)
+ root = xmlutil.TemplateElement('extra_spec', selector=sel)
+ root.set('key', 0)
+ root.text = 1
+ return xmlutil.MasterTemplate(root, 1)
+
+
class FlavorExtraSpecsController(object):
"""The flavor extra specs API controller for the OpenStack API."""
@@ -70,7 +79,7 @@ class FlavorExtraSpecsController(object):
raise exc.HTTPBadRequest(explanation=unicode(error))
return body
- @wsgi.serializers(xml=ExtraSpecsTemplate)
+ @wsgi.serializers(xml=ExtraSpecTemplate)
def update(self, req, flavor_id, id, body):
context = req.environ['nova.context']
authorize(context)
@@ -87,10 +96,9 @@ class FlavorExtraSpecsController(object):
body)
except exception.MetadataLimitExceeded as error:
raise exc.HTTPBadRequest(explanation=unicode(error))
-
return body
- @wsgi.serializers(xml=ExtraSpecsTemplate)
+ @wsgi.serializers(xml=ExtraSpecTemplate)
def show(self, req, flavor_id, id):
"""Return a single extra spec item."""
context = req.environ['nova.context']
diff --git a/nova/api/openstack/compute/contrib/volumes.py b/nova/api/openstack/compute/contrib/volumes.py
index 47c717495..3fc503217 100644
--- a/nova/api/openstack/compute/contrib/volumes.py
+++ b/nova/api/openstack/compute/contrib/volumes.py
@@ -33,6 +33,15 @@ from nova import volume
LOG = logging.getLogger(__name__)
authorize = extensions.extension_authorizer('compute', 'volumes')
+authorize_attach_index = extensions.extension_authorizer('compute',
+ 'volume_attachments:index')
+authorize_attach_show = extensions.extension_authorizer('compute',
+ 'volume_attachments:show')
+authorize_attach_create = extensions.extension_authorizer('compute',
+ 'volume_attachments:create')
+authorize_attach_delete = extensions.extension_authorizer('compute',
+ 'volume_attachments:delete')
+
def _translate_volume_detail_view(context, vol):
"""Maps keys for volumes details view."""
@@ -329,6 +338,8 @@ class VolumeAttachmentController(wsgi.Controller):
@wsgi.serializers(xml=VolumeAttachmentsTemplate)
def index(self, req, server_id):
"""Returns the list of volume attachments for a given instance."""
+ context = req.environ['nova.context']
+ authorize_attach_index(context)
return self._items(req, server_id,
entity_maker=_translate_attachment_summary_view)
@@ -337,6 +348,7 @@ class VolumeAttachmentController(wsgi.Controller):
"""Return data about the given volume attachment."""
context = req.environ['nova.context']
authorize(context)
+ authorize_attach_show(context)
volume_id = id
try:
@@ -377,6 +389,7 @@ class VolumeAttachmentController(wsgi.Controller):
"""Attach a volume to an instance."""
context = req.environ['nova.context']
authorize(context)
+ authorize_attach_create(context)
if not self.is_valid_body(body, 'volumeAttachment'):
raise exc.HTTPUnprocessableEntity()
@@ -423,6 +436,7 @@ class VolumeAttachmentController(wsgi.Controller):
"""Detach a volume from an instance."""
context = req.environ['nova.context']
authorize(context)
+ authorize_attach_delete(context)
volume_id = id
LOG.audit(_("Detach volume %s"), volume_id, context=context)
diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py
index f7f186870..90d4c37b3 100644
--- a/nova/api/openstack/compute/servers.py
+++ b/nova/api/openstack/compute/servers.py
@@ -906,9 +906,13 @@ class Controller(wsgi.Controller):
raise exc.HTTPBadRequest(explanation=unicode(error))
except exception.InvalidMetadataSize as error:
raise exc.HTTPRequestEntityTooLarge(explanation=unicode(error))
+ except exception.InvalidRequest as error:
+ raise exc.HTTPBadRequest(explanation=unicode(error))
except exception.ImageNotFound as error:
msg = _("Can not find requested image")
raise exc.HTTPBadRequest(explanation=msg)
+ except exception.ImageNotActive as error:
+ raise exc.HTTPBadRequest(explanation=unicode(error))
except exception.FlavorNotFound as error:
msg = _("Invalid flavorRef provided.")
raise exc.HTTPBadRequest(explanation=msg)
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index 733685b14..a6f255081 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -406,6 +406,8 @@ class XMLDictSerializer(DictSerializer):
if k in attrs:
result.setAttribute(k, str(v))
else:
+ if k == "deleted":
+ v = str(bool(v))
node = self._to_xml_node(doc, metadata, k, v)
result.appendChild(node)
else:
@@ -1180,10 +1182,18 @@ class Fault(webob.exc.HTTPException):
# Replace the body with fault details.
code = self.wrapped_exc.status_int
fault_name = self._fault_names.get(code, "computeFault")
+ explanation = self.wrapped_exc.explanation
+ offset = explanation.find("Traceback")
+ if offset is not -1:
+ LOG.debug(_("API request failed, fault raised to the top of"
+ " the stack. Detailed stacktrace %s") %
+ explanation)
+ explanation = explanation[0:offset - 1]
+
fault_data = {
fault_name: {
'code': code,
- 'message': self.wrapped_exc.explanation}}
+ 'message': explanation}}
if code == 413:
retry = self.wrapped_exc.headers.get('Retry-After', None)
if retry:
diff --git a/nova/availability_zones.py b/nova/availability_zones.py
index 62c83f6ed..711eee1fa 100644
--- a/nova/availability_zones.py
+++ b/nova/availability_zones.py
@@ -17,7 +17,6 @@
from nova import db
from nova.openstack.common import cfg
-from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
availability_zone_opts = [
@@ -60,3 +59,25 @@ def get_host_availability_zone(context, host):
return list(metadata['availability_zone'])[0]
else:
return CONF.default_availability_zone
+
+
+def get_availability_zones(context):
+ """Return available and unavailable zones."""
+ enabled_services = db.service_get_all(context, False)
+ disabled_services = db.service_get_all(context, True)
+ enabled_services = set_availability_zones(context, enabled_services)
+ disabled_services = set_availability_zones(context, disabled_services)
+
+ available_zones = []
+ for zone in [service['availability_zone'] for service
+ in enabled_services]:
+ if not zone in available_zones:
+ available_zones.append(zone)
+
+ not_available_zones = []
+ zones = [service['available_zones'] for service in disabled_services
+ if service['available_zones'] not in available_zones]
+ for zone in zones:
+ if zone not in not_available_zones:
+ not_available_zones.append(zone)
+ return (available_zones, not_available_zones)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index f6090b40c..a9d0a1bdd 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -433,7 +433,11 @@ class API(base.Base):
max_count = min_count
block_device_mapping = block_device_mapping or []
-
+ if min_count > 1 or max_count > 1:
+ if any(map(lambda bdm: 'volume_id' in bdm, block_device_mapping)):
+ msg = _('Cannot attach one or more volumes to multiple'
+ ' instances')
+ raise exception.InvalidRequest(msg)
if instance_type['disabled']:
raise exception.InstanceTypeNotFound(
instance_type_id=instance_type['id'])
@@ -504,6 +508,13 @@ class API(base.Base):
availability_zone, forced_host = self._handle_availability_zone(
availability_zone)
+ system_metadata = {}
+ instance_type_props = ['id', 'name', 'memory_mb', 'vcpus',
+ 'root_gb', 'ephemeral_gb', 'flavorid',
+ 'swap', 'rxtx_factor', 'vcpu_weight']
+ for k in instance_type_props:
+ system_metadata["instance_type_%s" % k] = instance_type[k]
+
base_options = {
'reservation_id': reservation_id,
'image_ref': image_href,
@@ -533,7 +544,8 @@ class API(base.Base):
'access_ip_v6': access_ip_v6,
'availability_zone': availability_zone,
'root_device_name': root_device_name,
- 'progress': 0}
+ 'progress': 0,
+ 'system_metadata': system_metadata}
options_from_image = self._inherit_properties_from_image(
image, auto_disk_config)
@@ -554,6 +566,11 @@ class API(base.Base):
security_group, block_device_mapping)
instances.append(instance)
instance_uuids.append(instance['uuid'])
+ self._validate_bdm(context, instance)
+ # send a state update notification for the initial create to
+ # show it going from non-existent to BUILDING
+ notifications.send_update_with_states(context, instance, None,
+ vm_states.BUILDING, None, None, service="api")
# In the case of any exceptions, attempt DB cleanup and rollback the
# quota reservations.
@@ -700,6 +717,23 @@ class API(base.Base):
self.db.block_device_mapping_update_or_create(elevated_context,
values)
+ def _validate_bdm(self, context, instance):
+ for bdm in self.db.block_device_mapping_get_all_by_instance(
+ context, instance['uuid']):
+ # NOTE(vish): For now, just make sure the volumes are accessible.
+ snapshot_id = bdm.get('snapshot_id')
+ volume_id = bdm.get('volume_id')
+ if volume_id is not None:
+ try:
+ self.volume_api.get(context, volume_id)
+ except Exception:
+ raise exception.InvalidBDMVolume(id=volume_id)
+ elif snapshot_id is not None:
+ try:
+ self.volume_api.get_snapshot(context, snapshot_id)
+ except Exception:
+ raise exception.InvalidBDMSnapshot(id=snapshot_id)
+
def _populate_instance_for_bdm(self, context, instance, instance_type,
image, block_device_mapping):
"""Populate instance block device mapping information."""
@@ -814,11 +848,6 @@ class API(base.Base):
self._populate_instance_for_bdm(context, instance,
instance_type, image, block_device_mapping)
- # send a state update notification for the initial create to
- # show it going from non-existent to BUILDING
- notifications.send_update_with_states(context, instance, None,
- vm_states.BUILDING, None, None, service="api")
-
return instance
def _check_create_policies(self, context, availability_zone,
@@ -1353,9 +1382,6 @@ class API(base.Base):
None if rotation shouldn't be used (as in the case of snapshots)
:param extra_properties: dict of extra image properties to include
"""
- instance = self.update(context, instance,
- task_state=task_states.IMAGE_BACKUP,
- expected_task_state=None)
if image_id:
# The image entry has already been created, so just pull the
# metadata.
@@ -1364,6 +1390,11 @@ class API(base.Base):
image_meta = self._create_image(context, instance, name,
'backup', backup_type=backup_type,
rotation=rotation, extra_properties=extra_properties)
+
+ instance = self.update(context, instance,
+ task_state=task_states.IMAGE_BACKUP,
+ expected_task_state=None)
+
self.compute_rpcapi.snapshot_instance(context, instance=instance,
image_id=image_meta['id'], image_type='backup',
backup_type=backup_type, rotation=rotation)
@@ -1382,9 +1413,6 @@ class API(base.Base):
:returns: A dict containing image metadata
"""
- instance = self.update(context, instance,
- task_state=task_states.IMAGE_SNAPSHOT,
- expected_task_state=None)
if image_id:
# The image entry has already been created, so just pull the
# metadata.
@@ -1392,6 +1420,11 @@ class API(base.Base):
else:
image_meta = self._create_image(context, instance, name,
'snapshot', extra_properties=extra_properties)
+
+ instance = self.update(context, instance,
+ task_state=task_states.IMAGE_SNAPSHOT,
+ expected_task_state=None)
+
self.compute_rpcapi.snapshot_instance(context, instance=instance,
image_id=image_meta['id'], image_type='snapshot')
return image_meta
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index d006ea049..73361fc23 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -57,13 +57,11 @@ from nova import network
from nova.network import model as network_model
from nova.openstack.common import cfg
from nova.openstack.common import excutils
-from nova.openstack.common import importutils
from nova.openstack.common import jsonutils
from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova.openstack.common.notifier import api as notifier
from nova.openstack.common import rpc
-from nova.openstack.common.rpc import common as rpc_common
from nova.openstack.common import timeutils
from nova import paths
from nova import quota
@@ -172,7 +170,6 @@ CONF.import_opt('allow_resize_to_same_host', 'nova.compute.api')
CONF.import_opt('console_topic', 'nova.console.rpcapi')
CONF.import_opt('host', 'nova.netconf')
CONF.import_opt('my_ip', 'nova.netconf')
-CONF.import_opt('network_manager', 'nova.service')
QUOTAS = quota.QUOTAS
@@ -230,7 +227,8 @@ def wrap_instance_fault(function):
with excutils.save_and_reraise_exception():
compute_utils.add_instance_fault_from_exc(context,
- kwargs['instance'], e, sys.exc_info())
+ self.conductor_api, kwargs['instance'],
+ e, sys.exc_info())
return decorated_function
@@ -301,8 +299,6 @@ class ComputeManager(manager.SchedulerDependentManager):
self.driver = driver.load_compute_driver(self.virtapi, compute_driver)
self.network_api = network.API()
self.volume_api = volume.API()
- self.network_manager = importutils.import_object(
- CONF.network_manager, host=kwargs.get('host', None))
self._last_host_check = 0
self._last_bw_usage_poll = 0
self._last_vol_usage_poll = 0
@@ -735,8 +731,8 @@ class ComputeManager(manager.SchedulerDependentManager):
instance_uuid = instance['uuid']
rescheduled = False
- compute_utils.add_instance_fault_from_exc(context, instance,
- exc_info[1], exc_info=exc_info)
+ compute_utils.add_instance_fault_from_exc(context, self.conductor_api,
+ instance, exc_info[1], exc_info=exc_info)
try:
self._deallocate_network(context, instance)
@@ -1135,8 +1131,7 @@ class ComputeManager(manager.SchedulerDependentManager):
vm_state=vm_states.DELETED,
task_state=None,
terminated_at=timeutils.utcnow())
- system_meta = compute_utils.metadata_to_dict(
- instance['system_metadata'])
+ system_meta = utils.metadata_to_dict(instance['system_metadata'])
self.conductor_api.instance_destroy(context, instance)
# ensure block device mappings are not leaked
@@ -1469,7 +1464,7 @@ class ComputeManager(manager.SchedulerDependentManager):
LOG.error(_('Cannot reboot instance: %(exc)s'), locals(),
context=context, instance=instance)
compute_utils.add_instance_fault_from_exc(context,
- instance, exc, sys.exc_info())
+ self.conductor_api, instance, exc, sys.exc_info())
# Fall through and reset task_state to None
current_power_state = self._get_power_state(context, instance)
@@ -1678,8 +1673,7 @@ class ComputeManager(manager.SchedulerDependentManager):
def _get_rescue_image_ref(self, context, instance):
"""Determine what image should be used to boot the rescue VM."""
- system_meta = compute_utils.metadata_to_dict(
- instance['system_metadata'])
+ system_meta = utils.metadata_to_dict(instance['system_metadata'])
rescue_image_ref = system_meta.get('image_base_image_ref')
@@ -2000,8 +1994,8 @@ class ComputeManager(manager.SchedulerDependentManager):
rescheduled = False
instance_uuid = instance['uuid']
- compute_utils.add_instance_fault_from_exc(context, instance,
- exc_info[0], exc_info=exc_info)
+ compute_utils.add_instance_fault_from_exc(context, self.conductor_api,
+ instance, exc_info[0], exc_info=exc_info)
try:
scheduler_method = self.scheduler_rpcapi.prep_resize
@@ -3074,8 +3068,8 @@ class ComputeManager(manager.SchedulerDependentManager):
vm_state = instance['vm_state']
task_state = instance['task_state']
if vm_state != vm_states.RESIZED or task_state is not None:
- reason = _("In states %(vm_state)s/%(task_state)s, not"
- "RESIZED/None")
+ reason = _("In states %(vm_state)s/%(task_state)s, not "
+ "RESIZED/None")
_set_migration_to_error(migration, reason % locals(),
instance=instance)
continue
@@ -3089,7 +3083,9 @@ class ComputeManager(manager.SchedulerDependentManager):
@manager.periodic_task
def _instance_usage_audit(self, context):
if CONF.instance_usage_audit:
- if not compute_utils.has_audit_been_run(context, self.host):
+ if not compute_utils.has_audit_been_run(context,
+ self.conductor_api,
+ self.host):
begin, end = utils.last_completed_audit_period()
capi = self.conductor_api
instances = capi.instance_get_active_by_window_joined(
@@ -3106,6 +3102,7 @@ class ComputeManager(manager.SchedulerDependentManager):
number_instances=num_instances))
start_time = time.time()
compute_utils.start_instance_usage_audit(context,
+ self.conductor_api,
begin, end,
self.host, num_instances)
for instance in instances:
@@ -3121,6 +3118,7 @@ class ComputeManager(manager.SchedulerDependentManager):
instance=instance)
errors += 1
compute_utils.finish_instance_usage_audit(context,
+ self.conductor_api,
begin, end,
self.host, errors,
"Instance usage audit ran "
diff --git a/nova/compute/resource_tracker.py b/nova/compute/resource_tracker.py
index be0360185..f5d3a8008 100644
--- a/nova/compute/resource_tracker.py
+++ b/nova/compute/resource_tracker.py
@@ -293,12 +293,14 @@ class ResourceTracker(object):
# Need to create the ComputeNode record:
resources['service_id'] = service['id']
self._create(context, resources)
- LOG.info(_('Compute_service record created for %s ') % self.host)
+ LOG.info(_('Compute_service record created for %(host)s:%(node)s')
+ % {'host': self.host, 'node': self.nodename})
else:
# just update the record:
self._update(context, resources, prune_stats=True)
- LOG.info(_('Compute_service record updated for %s ') % self.host)
+ LOG.info(_('Compute_service record updated for %(host)s:%(node)s')
+ % {'host': self.host, 'node': self.nodename})
def _create(self, context, values):
"""Create the compute node in the DB."""
diff --git a/nova/compute/utils.py b/nova/compute/utils.py
index 2b1286e16..8d363fa1c 100644
--- a/nova/compute/utils.py
+++ b/nova/compute/utils.py
@@ -22,7 +22,6 @@ import traceback
from nova import block_device
from nova.compute import instance_types
-from nova import db
from nova import exception
from nova.network import model as network_model
from nova import notifications
@@ -37,14 +36,8 @@ CONF.import_opt('host', 'nova.netconf')
LOG = log.getLogger(__name__)
-def metadata_to_dict(metadata):
- result = {}
- for item in metadata:
- result[item['key']] = item['value']
- return result
-
-
-def add_instance_fault_from_exc(context, instance, fault, exc_info=None):
+def add_instance_fault_from_exc(context, conductor,
+ instance, fault, exc_info=None):
"""Adds the specified fault to the database."""
code = 500
@@ -68,7 +61,7 @@ def add_instance_fault_from_exc(context, instance, fault, exc_info=None):
'details': unicode(details),
'host': CONF.host
}
- db.instance_fault_create(context, values)
+ conductor.instance_fault_create(context, values)
def get_device_name_for_instance(context, instance, bdms, device):
@@ -159,7 +152,8 @@ def notify_usage_exists(context, instance_ref, current_period=False,
ignore_missing_network_data)
if system_metadata is None:
- system_metadata = metadata_to_dict(instance_ref['system_metadata'])
+ system_metadata = utils.metadata_to_dict(
+ instance_ref['system_metadata'])
# add image metadata to the notification:
image_meta = notifications.image_meta(system_metadata)
@@ -211,24 +205,27 @@ def get_nw_info_for_instance(instance):
return network_model.NetworkInfo.hydrate(cached_nwinfo)
-def has_audit_been_run(context, host, timestamp=None):
+def has_audit_been_run(context, conductor, host, timestamp=None):
begin, end = utils.last_completed_audit_period(before=timestamp)
- task_log = db.task_log_get(context, "instance_usage_audit",
- begin, end, host)
+ task_log = conductor.task_log_get(context, "instance_usage_audit",
+ begin, end, host)
if task_log:
return True
else:
return False
-def start_instance_usage_audit(context, begin, end, host, num_instances):
- db.task_log_begin_task(context, "instance_usage_audit", begin, end, host,
- num_instances, "Instance usage audit started...")
+def start_instance_usage_audit(context, conductor, begin, end, host,
+ num_instances):
+ conductor.task_log_begin_task(context, "instance_usage_audit", begin,
+ end, host, num_instances,
+ "Instance usage audit started...")
-def finish_instance_usage_audit(context, begin, end, host, errors, message):
- db.task_log_end_task(context, "instance_usage_audit", begin, end, host,
- errors, message)
+def finish_instance_usage_audit(context, conductor, begin, end, host, errors,
+ message):
+ conductor.task_log_end_task(context, "instance_usage_audit", begin, end,
+ host, errors, message)
def usage_volume_info(vol_usage):
diff --git a/nova/conductor/api.py b/nova/conductor/api.py
index d05c94877..710639305 100644
--- a/nova/conductor/api.py
+++ b/nova/conductor/api.py
@@ -133,6 +133,9 @@ class LocalAPI(object):
def instance_type_get(self, context, instance_type_id):
return self._manager.instance_type_get(context, instance_type_id)
+ def instance_fault_create(self, context, values):
+ return self._manager.instance_fault_create(context, values)
+
def migration_get(self, context, migration_id):
return self._manager.migration_get(context, migration_id)
@@ -293,6 +296,22 @@ class LocalAPI(object):
def service_update(self, context, service, values):
return self._manager.service_update(context, service, values)
+ def task_log_get(self, context, task_name, begin, end, host, state):
+ return self._manager.task_log_get(context, task_name, begin, end,
+ host, state)
+
+ def task_log_begin_task(self, context, task_name, begin, end, host,
+ task_items=None, message=None):
+ return self._manager.task_log_begin_task(context, task_name,
+ begin, end, host,
+ task_items, message)
+
+ def task_log_end_task(self, context, task_name, begin, end, host,
+ errors, message=None):
+ return self._manager.task_log_end_task(context, task_name,
+ begin, end, host,
+ errors, message)
+
class API(object):
"""Conductor API that does updates via RPC to the ConductorManager."""
@@ -391,6 +410,9 @@ class API(object):
return self.conductor_rpcapi.instance_type_get(context,
instance_type_id)
+ def instance_fault_create(self, context, values):
+ return self.conductor_rpcapi.instance_fault_create(context, values)
+
def migration_get(self, context, migration_id):
return self.conductor_rpcapi.migration_get(context, migration_id)
@@ -564,3 +586,19 @@ class API(object):
def service_update(self, context, service, values):
return self.conductor_rpcapi.service_update(context, service, values)
+
+ def task_log_get(self, context, task_name, begin, end, host, state):
+ return self.conductor_rpcapi.task_log_get(context, task_name, begin,
+ end, host, state)
+
+ def task_log_begin_task(self, context, task_name, begin, end, host,
+ task_items=None, message=None):
+ return self.conductor_rpcapi.task_log_begin_task(context, task_name,
+ begin, end, host,
+ task_items, message)
+
+ def task_log_end_task(self, context, task_name, begin, end, host,
+ errors, message=None):
+ return self.conductor_rpcapi.task_log_end_task(context, task_name,
+ begin, end, host,
+ errors, message)
diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py
index 87b143912..232e9da3e 100644
--- a/nova/conductor/manager.py
+++ b/nova/conductor/manager.py
@@ -43,7 +43,7 @@ datetime_fields = ['launched_at', 'terminated_at']
class ConductorManager(manager.SchedulerDependentManager):
"""Mission: TBD."""
- RPC_API_VERSION = '1.35'
+ RPC_API_VERSION = '1.37'
def __init__(self, *args, **kwargs):
super(ConductorManager, self).__init__(service_name='conductor',
@@ -258,6 +258,10 @@ class ConductorManager(manager.SchedulerDependentManager):
result = self.db.instance_type_get(context, instance_type_id)
return jsonutils.to_primitive(result)
+ def instance_fault_create(self, context, values):
+ result = self.db.instance_fault_create(context, values)
+ return jsonutils.to_primitive(result)
+
def vol_get_usage_by_time(self, context, start_time):
result = self.db.vol_get_usage_by_time(context, start_time)
return jsonutils.to_primitive(result)
@@ -319,3 +323,21 @@ class ConductorManager(manager.SchedulerDependentManager):
def service_update(self, context, service, values):
svc = self.db.service_update(context, service['id'], values)
return jsonutils.to_primitive(svc)
+
+ def task_log_get(self, context, task_name, begin, end, host, state=None):
+ result = self.db.task_log_get(context, task_name, begin, end, host,
+ state)
+ return jsonutils.to_primitive(result)
+
+ def task_log_begin_task(self, context, task_name, begin, end, host,
+ task_items=None, message=None):
+ result = self.db.task_log_begin_task(context.elevated(), task_name,
+ begin, end, host, task_items,
+ message)
+ return jsonutils.to_primitive(result)
+
+ def task_log_end_task(self, context, task_name, begin, end, host,
+ errors, message=None):
+ result = self.db.task_log_end_task(context.elevated(), task_name,
+ begin, end, host, errors, message)
+ return jsonutils.to_primitive(result)
diff --git a/nova/conductor/rpcapi.py b/nova/conductor/rpcapi.py
index 1699c85ed..248a4e211 100644
--- a/nova/conductor/rpcapi.py
+++ b/nova/conductor/rpcapi.py
@@ -68,6 +68,8 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy):
1.33 - Added compute_node_create and compute_node_update
1.34 - Added service_update
1.35 - Added instance_get_active_by_window_joined
+ 1.36 - Added instance_fault_create
+ 1.37 - Added task_log_get, task_log_begin_task, task_log_end_task
"""
BASE_RPC_API_VERSION = '1.0'
@@ -293,6 +295,10 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy):
msg = self.make_msg('instance_get_all_by_host', host=host, node=node)
return self.call(context, msg, version='1.32')
+ def instance_fault_create(self, context, values):
+ msg = self.make_msg('instance_fault_create', values=values)
+ return self.call(context, msg, version='1.36')
+
def action_event_start(self, context, values):
msg = self.make_msg('action_event_start', values=values)
return self.call(context, msg, version='1.25')
@@ -330,3 +336,22 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy):
service_p = jsonutils.to_primitive(service)
msg = self.make_msg('service_update', service=service_p, values=values)
return self.call(context, msg, version='1.34')
+
+ def task_log_get(self, context, task_name, begin, end, host, state=None):
+ msg = self.make_msg('task_log_get', task_name=task_name,
+ begin=begin, end=end, host=host, state=state)
+ return self.call(context, msg, version='1.37')
+
+ def task_log_begin_task(self, context, task_name, begin, end, host,
+ task_items=None, message=None):
+ msg = self.make_msg('task_log_begin_task', task_name=task_name,
+ begin=begin, end=end, host=host,
+ task_items=task_items, message=message)
+ return self.call(context, msg, version='1.37')
+
+ def task_log_end_task(self, context, task_name, begin, end, host, errors,
+ message=None):
+ msg = self.make_msg('task_log_end_task', task_name=task_name,
+ begin=begin, end=end, host=host, errors=errors,
+ message=message)
+ return self.call(context, msg, version='1.37')
diff --git a/nova/context.py b/nova/context.py
index 1a566cb5a..8731e012d 100644
--- a/nova/context.py
+++ b/nova/context.py
@@ -46,7 +46,7 @@ class RequestContext(object):
roles=None, remote_address=None, timestamp=None,
request_id=None, auth_token=None, overwrite=True,
quota_class=None, user_name=None, project_name=None,
- service_catalog=None, instance_lock_checked=False, **kwargs):
+ service_catalog=[], instance_lock_checked=False, **kwargs):
"""
:param read_deleted: 'no' indicates deleted records are hidden, 'yes'
indicates deleted records are visible, 'only' indicates that
@@ -79,7 +79,9 @@ class RequestContext(object):
request_id = generate_request_id()
self.request_id = request_id
self.auth_token = auth_token
- self.service_catalog = service_catalog
+ # Only include required parts of service_catalog
+ self.service_catalog = [s for s in service_catalog
+ if s.get('type') in ('volume')]
self.instance_lock_checked = instance_lock_checked
# NOTE(markmc): this attribute is currently only used by the
diff --git a/nova/db/api.py b/nova/db/api.py
index d8a16c52d..3c1425691 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -305,7 +305,7 @@ def floating_ip_destroy(context, address):
def floating_ip_disassociate(context, address):
"""Disassociate a floating ip from a fixed ip by address.
- :returns: the address of the previous fixed ip or None
+ :returns: the fixed ip record joined to network record or None
if the ip was not associated to an ip.
"""
@@ -316,7 +316,7 @@ def floating_ip_fixed_ip_associate(context, floating_address,
fixed_address, host):
"""Associate a floating ip to a fixed_ip by address.
- :returns: the address of the new fixed ip (fixed_address) or None
+ :returns: the fixed ip record joined to network record or None
if the ip was already associated to the fixed ip.
"""
@@ -477,9 +477,12 @@ def fixed_ip_disassociate_all_by_timeout(context, host, time):
return IMPL.fixed_ip_disassociate_all_by_timeout(context, host, time)
-def fixed_ip_get(context, id):
- """Get fixed ip by id or raise if it does not exist."""
- return IMPL.fixed_ip_get(context, id)
+def fixed_ip_get(context, id, get_network=False):
+ """Get fixed ip by id or raise if it does not exist.
+
+ If get_network is true, also return the assocated network.
+ """
+ return IMPL.fixed_ip_get(context, id, get_network)
def fixed_ip_get_all(context):
@@ -1705,16 +1708,14 @@ def task_log_end_task(context, task_name,
period_ending,
host,
errors,
- message=None,
- session=None):
+ message=None):
"""Mark a task as complete for a given host/time period."""
return IMPL.task_log_end_task(context, task_name,
period_beginning,
period_ending,
host,
errors,
- message,
- session)
+ message)
def task_log_begin_task(context, task_name,
@@ -1722,25 +1723,23 @@ def task_log_begin_task(context, task_name,
period_ending,
host,
task_items=None,
- message=None,
- session=None):
+ message=None):
"""Mark a task as started for a given host/time period."""
return IMPL.task_log_begin_task(context, task_name,
period_beginning,
period_ending,
host,
task_items,
- message,
- session)
+ message)
def task_log_get_all(context, task_name, period_beginning,
- period_ending, host=None, state=None, session=None):
+ period_ending, host=None, state=None):
return IMPL.task_log_get_all(context, task_name, period_beginning,
- period_ending, host, state, session)
+ period_ending, host, state)
def task_log_get(context, task_name, period_beginning,
- period_ending, host, state=None, session=None):
+ period_ending, host, state=None):
return IMPL.task_log_get(context, task_name, period_beginning,
- period_ending, host, state, session)
+ period_ending, host, state)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 5317487cd..ad7e4f21f 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -35,7 +35,7 @@ from sqlalchemy.sql.expression import desc
from sqlalchemy.sql import func
from nova import block_device
-from nova.common.sqlalchemyutils import paginate_query
+from nova.common import sqlalchemyutils
from nova.compute import task_states
from nova.compute import vm_states
from nova import db
@@ -172,27 +172,43 @@ def model_query(context, model, *args, **kwargs):
:param project_only: if present and context is user-type, then restrict
query to match the context's project_id. If set to 'allow_none',
restriction includes project_id = None.
+ :param base_model: Where model_query is passed a "model" parameter which is
+ not a subclass of NovaBase, we should pass an extra base_model
+ parameter that is a subclass of NovaBase and corresponds to the
+ model parameter.
"""
session = kwargs.get('session') or get_session()
read_deleted = kwargs.get('read_deleted') or context.read_deleted
project_only = kwargs.get('project_only', False)
+ def issubclassof_nova_base(obj):
+ return isinstance(obj, type) and issubclass(obj, models.NovaBase)
+
+ base_model = model
+ if not issubclassof_nova_base(base_model):
+ base_model = kwargs.get('base_model', None)
+ if not issubclassof_nova_base(base_model):
+ raise Exception(_("model or base_model parameter should be "
+ "subclass of NovaBase"))
+
query = session.query(model, *args)
+ default_deleted_value = base_model.__mapper__.c.deleted.default.arg
if read_deleted == 'no':
- query = query.filter_by(deleted=False)
+ query = query.filter(base_model.deleted == default_deleted_value)
elif read_deleted == 'yes':
pass # omit the filter to include deleted and active
elif read_deleted == 'only':
- query = query.filter_by(deleted=True)
+ query = query.filter(base_model.deleted != default_deleted_value)
else:
- raise Exception(
- _("Unrecognized read_deleted value '%s'") % read_deleted)
+ raise Exception(_("Unrecognized read_deleted value '%s'")
+ % read_deleted)
if is_user_context(context) and project_only:
if project_only == 'allow_none':
- query = query.filter(or_(model.project_id == context.project_id,
- model.project_id == None))
+ query = query.\
+ filter(or_(base_model.project_id == context.project_id,
+ base_model.project_id == None))
else:
query = query.filter_by(project_id=context.project_id)
@@ -408,7 +424,8 @@ def service_get_all_compute_sorted(context):
label = 'instance_cores'
subq = model_query(context, models.Instance.host,
func.sum(models.Instance.vcpus).label(label),
- session=session, read_deleted="no").\
+ base_model=models.Instance, session=session,
+ read_deleted="no").\
group_by(models.Instance.host).\
subquery()
return _service_get_all_topic_subquery(context,
@@ -540,7 +557,7 @@ def _update_stats(context, new_stats, compute_id, session, prune_stats=False):
# prune un-touched old stats:
for stat in statmap.values():
session.add(stat)
- stat.update({'deleted': True})
+ stat.soft_delete(session=session)
# add new and updated stats
for stat in stats:
@@ -563,10 +580,9 @@ def compute_node_update(context, compute_id, values, prune_stats=False):
def compute_node_get_by_host(context, host):
"""Get all capacity entries for the given host."""
- result = model_query(context, models.ComputeNode).\
+ result = model_query(context, models.ComputeNode, read_deleted="no").\
join('service').\
filter(models.Service.host == host).\
- filter_by(deleted=False).\
first()
return result
@@ -586,6 +602,7 @@ def compute_node_statistics(context):
func.sum(models.ComputeNode.current_workload),
func.sum(models.ComputeNode.running_vms),
func.sum(models.ComputeNode.disk_available_least),
+ base_model=models.ComputeNode,
read_deleted="no").first()
# Build a dict of the info--making no assumptions about result
@@ -660,7 +677,8 @@ def floating_ip_get(context, id):
@require_context
def floating_ip_get_pools(context):
pools = []
- for result in model_query(context, models.FloatingIp.pool).distinct():
+ for result in model_query(context, models.FloatingIp.pool,
+ base_model=models.FloatingIp).distinct():
pools.append({'name': result[0]})
return pools
@@ -775,15 +793,16 @@ def floating_ip_fixed_ip_associate(context, floating_address,
floating_ip_ref = _floating_ip_get_by_address(context,
floating_address,
session=session)
- fixed_ip_ref = fixed_ip_get_by_address(context,
- fixed_address,
- session=session)
+ fixed_ip_ref = model_query(context, models.FixedIp, session=session).\
+ filter_by(address=fixed_address).\
+ options(joinedload('network')).\
+ first()
if floating_ip_ref.fixed_ip_id == fixed_ip_ref["id"]:
return None
floating_ip_ref.fixed_ip_id = fixed_ip_ref["id"]
floating_ip_ref.host = host
floating_ip_ref.save(session=session)
- return fixed_address
+ return fixed_ip_ref
@require_context
@@ -816,15 +835,12 @@ def floating_ip_disassociate(context, address):
fixed_ip_ref = model_query(context, models.FixedIp, session=session).\
filter_by(id=floating_ip_ref['fixed_ip_id']).\
+ options(joinedload('network')).\
first()
- if fixed_ip_ref:
- fixed_ip_address = fixed_ip_ref['address']
- else:
- fixed_ip_address = None
floating_ip_ref.fixed_ip_id = None
floating_ip_ref.host = None
floating_ip_ref.save(session=session)
- return fixed_ip_address
+ return fixed_ip_ref
@require_context
@@ -1094,37 +1110,39 @@ def fixed_ip_disassociate_all_by_timeout(context, host, time):
# host; i.e. the network host or the instance
# host matches. Two queries necessary because
# join with update doesn't work.
- host_filter = or_(and_(models.Instance.host == host,
- models.Network.multi_host == True),
- models.Network.host == host)
- result = session.query(models.FixedIp.id).\
- filter(models.FixedIp.deleted == False).\
- filter(models.FixedIp.allocated == False).\
- filter(models.FixedIp.updated_at < time).\
- join((models.Network,
- models.Network.id == models.FixedIp.network_id)).\
- join((models.Instance,
- models.Instance.uuid ==
- models.FixedIp.instance_uuid)).\
- filter(host_filter).\
- all()
- fixed_ip_ids = [fip[0] for fip in result]
- if not fixed_ip_ids:
- return 0
- result = model_query(context, models.FixedIp, session=session).\
- filter(models.FixedIp.id.in_(fixed_ip_ids)).\
- update({'instance_uuid': None,
- 'leased': False,
- 'updated_at': timeutils.utcnow()},
- synchronize_session='fetch')
- return result
-
-
-@require_context
-def fixed_ip_get(context, id):
- result = model_query(context, models.FixedIp).\
- filter_by(id=id).\
- first()
+ with session.begin():
+ host_filter = or_(and_(models.Instance.host == host,
+ models.Network.multi_host == True),
+ models.Network.host == host)
+ result = model_query(context, models.FixedIp.id,
+ base_model=models.FixedIp, read_deleted="no",
+ session=session).\
+ filter(models.FixedIp.allocated == False).\
+ filter(models.FixedIp.updated_at < time).\
+ join((models.Network,
+ models.Network.id == models.FixedIp.network_id)).\
+ join((models.Instance,
+ models.Instance.uuid == models.FixedIp.instance_uuid)).\
+ filter(host_filter).\
+ all()
+ fixed_ip_ids = [fip[0] for fip in result]
+ if not fixed_ip_ids:
+ return 0
+ result = model_query(context, models.FixedIp, session=session).\
+ filter(models.FixedIp.id.in_(fixed_ip_ids)).\
+ update({'instance_uuid': None,
+ 'leased': False,
+ 'updated_at': timeutils.utcnow()},
+ synchronize_session='fetch')
+ return result
+
+
+@require_context
+def fixed_ip_get(context, id, get_network=False):
+ query = model_query(context, models.FixedIp).filter_by(id=id)
+ if get_network:
+ query = query.options(joinedload('network'))
+ result = query.first()
if not result:
raise exception.FixedIpNotFound(id=id)
@@ -1226,7 +1244,7 @@ def fixed_ip_get_by_network_host(context, network_id, host):
first()
if not result:
- raise exception.FixedIpNotFoundForNetworkHost(network_uuid=network_id,
+ raise exception.FixedIpNotFoundForNetworkHost(network_id=network_id,
host=host)
return result
@@ -1468,7 +1486,7 @@ def instance_data_get_for_project(context, project_id, session=None):
func.count(models.Instance.id),
func.sum(models.Instance.vcpus),
func.sum(models.Instance.memory_mb),
- read_deleted="no",
+ base_model=models.Instance,
session=session).\
filter_by(project_id=project_id).\
first()
@@ -1593,12 +1611,12 @@ def instance_get_all_by_filters(context, filters, sort_key, sort_dir,
# Instances can be soft or hard deleted and the query needs to
# include or exclude both
if filters.pop('deleted'):
- deleted = or_(models.Instance.deleted == True,
+ deleted = or_(models.Instance.deleted == models.Instance.id,
models.Instance.vm_state == vm_states.SOFT_DELETED)
query_prefix = query_prefix.filter(deleted)
else:
query_prefix = query_prefix.\
- filter_by(deleted=False).\
+ filter_by(deleted=0).\
filter(models.Instance.vm_state != vm_states.SOFT_DELETED)
if not context.is_admin:
@@ -1626,7 +1644,8 @@ def instance_get_all_by_filters(context, filters, sort_key, sort_dir,
marker = _instance_get_by_uuid(context, marker, session=session)
except exception.InstanceNotFound:
raise exception.MarkerNotFound(marker)
- query_prefix = paginate_query(query_prefix, models.Instance, limit,
+ query_prefix = sqlalchemyutils.paginate_query(query_prefix,
+ models.Instance, limit,
[sort_key, 'created_at', 'id'],
marker=marker,
sort_dir=sort_dir)
@@ -1695,6 +1714,7 @@ def instance_get_active_by_window_joined(context, begin, end=None,
options(joinedload('security_groups')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
+ options(joinedload('system_metadata')).\
filter(or_(models.Instance.terminated_at == None,
models.Instance.terminated_at > begin))
if end:
@@ -2122,19 +2142,21 @@ def network_create_safe(context, values):
def network_delete_safe(context, network_id):
session = get_session()
with session.begin():
- result = session.query(models.FixedIp).\
+ result = model_query(context, models.FixedIp, session=session,
+ read_deleted="no").\
filter_by(network_id=network_id).\
- filter_by(deleted=False).\
filter_by(allocated=True).\
count()
if result != 0:
raise exception.NetworkInUse(network_id=network_id)
network_ref = network_get(context, network_id=network_id,
session=session)
- session.query(models.FixedIp).\
+
+ model_query(context, models.FixedIp, session=session,
+ read_deleted="no").\
filter_by(network_id=network_id).\
- filter_by(deleted=False).\
soft_delete()
+
session.delete(network_ref)
@@ -2213,9 +2235,9 @@ def network_get_associated_fixed_ips(context, network_id, host=None):
# without regenerating the whole list
vif_and = and_(models.VirtualInterface.id ==
models.FixedIp.virtual_interface_id,
- models.VirtualInterface.deleted == False)
+ models.VirtualInterface.deleted == 0)
inst_and = and_(models.Instance.uuid == models.FixedIp.instance_uuid,
- models.Instance.deleted == False)
+ models.Instance.deleted == 0)
session = get_session()
query = session.query(models.FixedIp.address,
models.FixedIp.instance_uuid,
@@ -2225,7 +2247,7 @@ def network_get_associated_fixed_ips(context, network_id, host=None):
models.Instance.hostname,
models.Instance.updated_at,
models.Instance.created_at).\
- filter(models.FixedIp.deleted == False).\
+ filter(models.FixedIp.deleted == 0).\
filter(models.FixedIp.network_id == network_id).\
filter(models.FixedIp.allocated == True).\
join((models.VirtualInterface, vif_and)).\
@@ -2326,6 +2348,7 @@ def network_get_all_by_host(context, host):
fixed_host_filter = or_(models.FixedIp.host == host,
models.Instance.host == host)
fixed_ip_query = model_query(context, models.FixedIp.network_id,
+ base_model=models.FixedIp,
session=session).\
outerjoin((models.VirtualInterface,
models.VirtualInterface.id ==
@@ -3138,13 +3161,14 @@ def security_group_in_use(context, group_id):
with session.begin():
# Are there any instances that haven't been deleted
# that include this group?
- inst_assoc = session.query(models.SecurityGroupInstanceAssociation).\
- filter_by(security_group_id=group_id).\
- filter_by(deleted=False).\
- all()
+ inst_assoc = model_query(context,
+ models.SecurityGroupInstanceAssociation,
+ read_deleted="no", session=session).\
+ filter_by(security_group_id=group_id).\
+ all()
for ia in inst_assoc:
- num_instances = session.query(models.Instance).\
- filter_by(deleted=False).\
+ num_instances = model_query(context, models.Instance,
+ session=session, read_deleted="no").\
filter_by(uuid=ia.instance_uuid).\
count()
if num_instances:
@@ -3595,7 +3619,7 @@ def instance_type_get_all(context, inactive=False, filters=None):
if filters['is_public'] and context.project_id is not None:
the_filter.extend([
models.InstanceTypes.projects.any(
- project_id=context.project_id, deleted=False)
+ project_id=context.project_id, deleted=0)
])
if len(the_filter) > 1:
query = query.filter(or_(*the_filter))
@@ -4037,7 +4061,8 @@ def _instance_type_extra_specs_get_query(context, flavor_id,
session=None):
# Two queries necessary because join with update doesn't work.
t = model_query(context, models.InstanceTypes.id,
- session=session, read_deleted="no").\
+ base_model=models.InstanceTypes, session=session,
+ read_deleted="no").\
filter(models.InstanceTypes.flavorid == flavor_id).\
subquery()
return model_query(context, models.InstanceTypeExtraSpecs,
@@ -4091,6 +4116,7 @@ def instance_type_extra_specs_update_or_create(context, flavor_id, specs):
session = get_session()
with session.begin():
instance_type_id = model_query(context, models.InstanceTypes.id,
+ base_model=models.InstanceTypes,
session=session, read_deleted="no").\
filter(models.InstanceTypes.flavorid == flavor_id).\
first()
@@ -4745,15 +4771,15 @@ def _task_log_get_query(context, task_name, period_beginning,
@require_admin_context
def task_log_get(context, task_name, period_beginning, period_ending, host,
state=None):
- return _task_log_get_query(task_name, period_beginning, period_ending,
- host, state).first()
+ return _task_log_get_query(context, task_name, period_beginning,
+ period_ending, host, state).first()
@require_admin_context
def task_log_get_all(context, task_name, period_beginning, period_ending,
host=None, state=None):
- return _task_log_get_query(task_name, period_beginning, period_ending,
- host, state).all()
+ return _task_log_get_query(context, task_name, period_beginning,
+ period_ending, host, state).all()
@require_admin_context
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/147_no_service_zones.py b/nova/db/sqlalchemy/migrate_repo/versions/147_no_service_zones.py
index a20799fbe..d93cd1ead 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/147_no_service_zones.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/147_no_service_zones.py
@@ -37,9 +37,9 @@ def upgrade(migrate_engine):
if rec['binary'] != 'nova-compute':
continue
# if zone doesn't exist create
- result = aggregate_metadata.select().where(aggregate_metadata.c.key ==
- 'availability_zone' and
- aggregate_metadata.c.key == rec['availability_zone']).execute()
+ result = aggregate_metadata.select().where(
+ aggregate_metadata.c.key == 'availability_zone').where(
+ aggregate_metadata.c.value == rec['availability_zone']).execute()
result = [r for r in result]
if len(result) > 0:
agg_id = result[0].aggregate_id
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/152_change_type_of_deleted_column.py b/nova/db/sqlalchemy/migrate_repo/versions/152_change_type_of_deleted_column.py
new file mode 100644
index 000000000..c49e8272b
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/152_change_type_of_deleted_column.py
@@ -0,0 +1,225 @@
+from sqlalchemy import CheckConstraint
+from sqlalchemy.engine import reflection
+from sqlalchemy.ext.compiler import compiles
+from sqlalchemy import MetaData, Table, Column, Index
+from sqlalchemy import select
+from sqlalchemy.sql.expression import UpdateBase
+from sqlalchemy import Integer, Boolean
+from sqlalchemy.types import NullType, BigInteger
+
+
+all_tables = ['services', 'compute_nodes', 'compute_node_stats',
+ 'certificates', 'instances', 'instance_info_caches',
+ 'instance_types', 'volumes', 'quotas', 'quota_classes',
+ 'quota_usages', 'reservations', 'snapshots',
+ 'block_device_mapping', 'iscsi_targets',
+ 'security_group_instance_association', 'security_groups',
+ 'security_group_rules', 'provider_fw_rules', 'key_pairs',
+ 'migrations', 'networks', 'virtual_interfaces', 'fixed_ips',
+ 'floating_ips', 'console_pools', 'consoles',
+ 'instance_metadata', 'instance_system_metadata',
+ 'instance_type_projects', 'instance_type_extra_specs',
+ 'aggregate_hosts', 'aggregate_metadata', 'aggregates',
+ 'agent_builds', 's3_images',
+ 'instance_faults',
+ 'bw_usage_cache', 'volume_id_mappings', 'snapshot_id_mappings',
+ 'instance_id_mappings', 'volume_usage_cache', 'task_log',
+ 'instance_actions', 'instance_actions_events']
+# note(boris-42): We can't do migration for the dns_domains table because it
+# doesn't have `id` column.
+
+
+class InsertFromSelect(UpdateBase):
+ def __init__(self, table, select):
+ self.table = table
+ self.select = select
+
+
+@compiles(InsertFromSelect)
+def visit_insert_from_select(element, compiler, **kw):
+ return "INSERT INTO %s %s" % (
+ compiler.process(element.table, asfrom=True),
+ compiler.process(element.select))
+
+
+def get_default_deleted_value(table):
+ if isinstance(table.c.id.type, Integer):
+ return 0
+ # NOTE(boris-42): There is only one other type that is used as id (String)
+ return ""
+
+
+def upgrade_enterprise_dbs(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ for table_name in all_tables:
+ table = Table(table_name, meta, autoload=True)
+
+ new_deleted = Column('new_deleted', table.c.id.type,
+ default=get_default_deleted_value(table))
+ new_deleted.create(table, populate_default=True)
+
+ table.update().\
+ where(table.c.deleted == True).\
+ values(new_deleted=table.c.id).\
+ execute()
+ table.c.deleted.drop()
+ table.c.new_deleted.alter(name="deleted")
+
+
+def upgrade(migrate_engine):
+ if migrate_engine.name != "sqlite":
+ return upgrade_enterprise_dbs(migrate_engine)
+
+ # NOTE(boris-42): sqlaclhemy-migrate can't drop column with check
+ # constraints in sqlite DB and our `deleted` column has
+ # 2 check constraints. So there is only one way to remove
+ # these constraints:
+ # 1) Create new table with the same columns, constraints
+ # and indexes. (except deleted column).
+ # 2) Copy all data from old to new table.
+ # 3) Drop old table.
+ # 4) Rename new table to old table name.
+ insp = reflection.Inspector.from_engine(migrate_engine)
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ for table_name in all_tables:
+ table = Table(table_name, meta, autoload=True)
+ default_deleted_value = get_default_deleted_value(table)
+
+ columns = []
+ for column in table.columns:
+ column_copy = None
+ if column.name != "deleted":
+ # NOTE(boris-42): BigInteger is not supported by sqlite, so
+ # after copy it will have NullType, other
+ # types that are used in Nova are supported by
+ # sqlite.
+ if isinstance(column.type, NullType):
+ column_copy = Column(column.name, BigInteger(), default=0)
+ else:
+ column_copy = column.copy()
+ else:
+ column_copy = Column('deleted', table.c.id.type,
+ default=default_deleted_value)
+ columns.append(column_copy)
+
+ def is_deleted_column_constraint(constraint):
+ # NOTE(boris-42): There is no other way to check is CheckConstraint
+ # associated with deleted column.
+ if not isinstance(constraint, CheckConstraint):
+ return False
+ sqltext = str(constraint.sqltext)
+ return (sqltext.endswith("deleted in (0, 1)") or
+ sqltext.endswith("deleted IN (:deleted_1, :deleted_2)"))
+
+ constraints = []
+ for constraint in table.constraints:
+ if not is_deleted_column_constraint(constraint):
+ constraints.append(constraint.copy())
+
+ new_table = Table(table_name + "__tmp__", meta,
+ *(columns + constraints))
+ new_table.create()
+
+ indexes = []
+ for index in insp.get_indexes(table_name):
+ column_names = [new_table.c[c] for c in index['column_names']]
+ indexes.append(Index(index["name"],
+ *column_names,
+ unique=index["unique"]))
+
+ ins = InsertFromSelect(new_table, table.select())
+ migrate_engine.execute(ins)
+
+ table.drop()
+ [index.create(migrate_engine) for index in indexes]
+
+ new_table.rename(table_name)
+ new_table.update().\
+ where(new_table.c.deleted == True).\
+ values(deleted=new_table.c.id).\
+ execute()
+
+ # NOTE(boris-42): Fix value of deleted column: False -> "" or 0.
+ new_table.update().\
+ where(new_table.c.deleted == False).\
+ values(deleted=default_deleted_value).\
+ execute()
+
+
+def downgrade_enterprise_dbs(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ for table_name in all_tables:
+ table = Table(table_name, meta, autoload=True)
+
+ old_deleted = Column('old_deleted', Boolean, default=False)
+ old_deleted.create(table, populate_default=False)
+
+ table.update().\
+ where(table.c.deleted == table.c.id).\
+ values(old_deleted=True).\
+ execute()
+
+ table.c.deleted.drop()
+ table.c.old_deleted.alter(name="deleted")
+
+
+def downgrade(migrate_engine):
+ if migrate_engine.name != "sqlite":
+ return downgrade_enterprise_dbs(migrate_engine)
+
+ insp = reflection.Inspector.from_engine(migrate_engine)
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ for table_name in all_tables:
+ table = Table(table_name, meta, autoload=True)
+
+ columns = []
+ for column in table.columns:
+ column_copy = None
+ if column.name != "deleted":
+ if isinstance(column.type, NullType):
+ column_copy = Column(column.name, BigInteger(), default=0)
+ else:
+ column_copy = column.copy()
+ else:
+ column_copy = Column('deleted', Boolean, default=0)
+ columns.append(column_copy)
+
+ constraints = [constraint.copy() for constraint in table.constraints]
+
+ new_table = Table(table_name + "__tmp__", meta,
+ *(columns + constraints))
+ new_table.create()
+
+ indexes = []
+ for index in insp.get_indexes(table_name):
+ column_names = [new_table.c[c] for c in index['column_names']]
+ indexes.append(Index(index["name"],
+ *column_names,
+ unique=index["unique"]))
+
+ c_select = []
+ for c in table.c:
+ if c.name != "deleted":
+ c_select.append(c)
+ else:
+ c_select.append(table.c.deleted == table.c.id)
+
+ ins = InsertFromSelect(new_table, select(c_select))
+ migrate_engine.execute(ins)
+
+ table.drop()
+ [index.create(migrate_engine) for index in indexes]
+
+ new_table.rename(table_name)
+ new_table.update().\
+ where(new_table.c.deleted == new_table.c.id).\
+ values(deleted=True).\
+ execute()
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/153_instance_type_in_system_metadata.py b/nova/db/sqlalchemy/migrate_repo/versions/153_instance_type_in_system_metadata.py
new file mode 100644
index 000000000..20e75a6eb
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/153_instance_type_in_system_metadata.py
@@ -0,0 +1,49 @@
+# Copyright 2013 IBM Corp.
+#
+# 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.
+
+from sqlalchemy import MetaData, select, Table
+
+
+def upgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+ instances = Table('instances', meta, autoload=True)
+ instance_types = Table('instance_types', meta, autoload=True)
+ sys_meta = Table('instance_system_metadata', meta, autoload=True)
+
+ # Taken from nova/compute/api.py
+ instance_type_props = ['id', 'name', 'memory_mb', 'vcpus',
+ 'root_gb', 'ephemeral_gb', 'flavorid',
+ 'swap', 'rxtx_factor', 'vcpu_weight']
+
+ select_columns = [instances.c.uuid]
+ select_columns += [getattr(instance_types.c, name)
+ for name in instance_type_props]
+
+ q = select(select_columns, from_obj=instances.join(
+ instance_types,
+ instances.c.instance_type_id == instance_types.c.id))
+
+ i = sys_meta.insert()
+ for values in q.execute():
+ for index in range(0, len(instance_type_props)):
+ i.execute({"key": "instance_type_%s" % instance_type_props[index],
+ "value": str(values[index + 1]),
+ "instance_uuid": values[0]})
+
+
+def downgrade(migration_engine):
+ # This migration only touches data, and only metadata at that. No need
+ # to go through and delete old metadata items.
+ pass
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index baa966dbc..fd8348678 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -27,7 +27,7 @@ from sqlalchemy import ForeignKey, DateTime, Boolean, Text, Float
from sqlalchemy.orm import relationship, backref, object_mapper
from nova.db.sqlalchemy.session import get_session
-from nova.db.sqlalchemy.types import IPAddress
+from nova.db.sqlalchemy import types
from nova.openstack.common import cfg
from nova.openstack.common import timeutils
@@ -42,7 +42,7 @@ class NovaBase(object):
created_at = Column(DateTime, default=timeutils.utcnow)
updated_at = Column(DateTime, onupdate=timeutils.utcnow)
deleted_at = Column(DateTime)
- deleted = Column(Boolean, default=False)
+ deleted = Column(Integer, default=0)
metadata = None
def save(self, session=None):
@@ -63,7 +63,7 @@ class NovaBase(object):
def soft_delete(self, session=None):
"""Mark this object as deleted."""
- self.deleted = True
+ self.deleted = self.id
self.deleted_at = timeutils.utcnow()
self.save(session=session)
@@ -129,7 +129,7 @@ class ComputeNode(BASE, NovaBase):
foreign_keys=service_id,
primaryjoin='and_('
'ComputeNode.service_id == Service.id,'
- 'ComputeNode.deleted == False)')
+ 'ComputeNode.deleted == 0)')
vcpus = Column(Integer)
memory_mb = Column(Integer)
@@ -173,7 +173,7 @@ class ComputeNodeStat(BASE, NovaBase):
compute_node_id = Column(Integer, ForeignKey('compute_nodes.id'))
primary_join = ('and_(ComputeNodeStat.compute_node_id == '
- 'ComputeNode.id, ComputeNodeStat.deleted == False)')
+ 'ComputeNode.id, ComputeNodeStat.deleted == 0)')
stats = relationship("ComputeNode", backref="stats",
primaryjoin=primary_join)
@@ -221,7 +221,7 @@ class Instance(BASE, NovaBase):
return base_name
def _extra_keys(self):
- return ['name']
+ return ['name', 'system_metadata']
user_id = Column(String(255))
project_id = Column(String(255))
@@ -291,8 +291,8 @@ class Instance(BASE, NovaBase):
# User editable field meant to represent what ip should be used
# to connect to the instance
- access_ip_v4 = Column(IPAddress())
- access_ip_v6 = Column(IPAddress())
+ access_ip_v4 = Column(types.IPAddress())
+ access_ip_v6 = Column(types.IPAddress())
auto_disk_config = Column(Boolean())
progress = Column(Integer)
@@ -358,6 +358,7 @@ class Volume(BASE, NovaBase):
"""Represents a block storage device that can be attached to a VM."""
__tablename__ = 'volumes'
id = Column(String(36), primary_key=True)
+ deleted = Column(String(36), default="")
@property
def name(self):
@@ -465,13 +466,14 @@ class Reservation(BASE, NovaBase):
"QuotaUsage",
foreign_keys=usage_id,
primaryjoin='and_(Reservation.usage_id == QuotaUsage.id,'
- 'QuotaUsage.deleted == False)')
+ 'QuotaUsage.deleted == 0)')
class Snapshot(BASE, NovaBase):
"""Represents a block storage device that can be attached to a VM."""
__tablename__ = 'snapshots'
id = Column(String(36), primary_key=True)
+ deleted = Column(String(36), default="")
@property
def name(self):
@@ -507,7 +509,7 @@ class BlockDeviceMapping(BASE, NovaBase):
'instance_uuid=='
'Instance.uuid,'
'BlockDeviceMapping.deleted=='
- 'False)')
+ '0)')
device_name = Column(String(255), nullable=False)
# default=False for compatibility of the existing code.
@@ -542,7 +544,7 @@ class IscsiTarget(BASE, NovaBase):
backref=backref('iscsi_target', uselist=False),
foreign_keys=volume_id,
primaryjoin='and_(IscsiTarget.volume_id==Volume.id,'
- 'IscsiTarget.deleted==False)')
+ 'IscsiTarget.deleted==0)')
class SecurityGroupInstanceAssociation(BASE, NovaBase):
@@ -567,14 +569,14 @@ class SecurityGroup(BASE, NovaBase):
primaryjoin='and_('
'SecurityGroup.id == '
'SecurityGroupInstanceAssociation.security_group_id,'
- 'SecurityGroupInstanceAssociation.deleted == False,'
- 'SecurityGroup.deleted == False)',
+ 'SecurityGroupInstanceAssociation.deleted == 0,'
+ 'SecurityGroup.deleted == 0)',
secondaryjoin='and_('
'SecurityGroupInstanceAssociation.instance_uuid == Instance.uuid,'
# (anthony) the condition below shouldn't be necessary now that the
# association is being marked as deleted. However, removing this
# may cause existing deployments to choke, so I'm leaving it
- 'Instance.deleted == False)',
+ 'Instance.deleted == 0)',
backref='security_groups')
@@ -588,12 +590,12 @@ class SecurityGroupIngressRule(BASE, NovaBase):
foreign_keys=parent_group_id,
primaryjoin='and_('
'SecurityGroupIngressRule.parent_group_id == SecurityGroup.id,'
- 'SecurityGroupIngressRule.deleted == False)')
+ 'SecurityGroupIngressRule.deleted == 0)')
protocol = Column(String(5)) # "tcp", "udp", or "icmp"
from_port = Column(Integer)
to_port = Column(Integer)
- cidr = Column(IPAddress())
+ cidr = Column(types.IPAddress())
# Note: This is not the parent SecurityGroup. It's SecurityGroup we're
# granting access for.
@@ -602,7 +604,7 @@ class SecurityGroupIngressRule(BASE, NovaBase):
foreign_keys=group_id,
primaryjoin='and_('
'SecurityGroupIngressRule.group_id == SecurityGroup.id,'
- 'SecurityGroupIngressRule.deleted == False)')
+ 'SecurityGroupIngressRule.deleted == 0)')
class ProviderFirewallRule(BASE, NovaBase):
@@ -613,7 +615,7 @@ class ProviderFirewallRule(BASE, NovaBase):
protocol = Column(String(5)) # "tcp", "udp", or "icmp"
from_port = Column(Integer)
to_port = Column(Integer)
- cidr = Column(IPAddress())
+ cidr = Column(types.IPAddress())
class KeyPair(BASE, NovaBase):
@@ -651,7 +653,7 @@ class Migration(BASE, NovaBase):
instance = relationship("Instance", foreign_keys=instance_uuid,
primaryjoin='and_(Migration.instance_uuid == '
'Instance.uuid, Instance.deleted == '
- 'False)')
+ '0)')
class Network(BASE, NovaBase):
@@ -663,25 +665,25 @@ class Network(BASE, NovaBase):
label = Column(String(255))
injected = Column(Boolean, default=False)
- cidr = Column(IPAddress(), unique=True)
- cidr_v6 = Column(IPAddress(), unique=True)
+ cidr = Column(types.IPAddress(), unique=True)
+ cidr_v6 = Column(types.IPAddress(), unique=True)
multi_host = Column(Boolean, default=False)
- gateway_v6 = Column(IPAddress())
- netmask_v6 = Column(IPAddress())
- netmask = Column(IPAddress())
+ gateway_v6 = Column(types.IPAddress())
+ netmask_v6 = Column(types.IPAddress())
+ netmask = Column(types.IPAddress())
bridge = Column(String(255))
bridge_interface = Column(String(255))
- gateway = Column(IPAddress())
- broadcast = Column(IPAddress())
- dns1 = Column(IPAddress())
- dns2 = Column(IPAddress())
+ gateway = Column(types.IPAddress())
+ broadcast = Column(types.IPAddress())
+ dns1 = Column(types.IPAddress())
+ dns2 = Column(types.IPAddress())
vlan = Column(Integer)
- vpn_public_address = Column(IPAddress())
+ vpn_public_address = Column(types.IPAddress())
vpn_public_port = Column(Integer)
- vpn_private_address = Column(IPAddress())
- dhcp_start = Column(IPAddress())
+ vpn_private_address = Column(types.IPAddress())
+ dhcp_start = Column(types.IPAddress())
rxtx_base = Column(Integer)
@@ -706,7 +708,7 @@ class FixedIp(BASE, NovaBase):
"""Represents a fixed ip for an instance."""
__tablename__ = 'fixed_ips'
id = Column(Integer, primary_key=True)
- address = Column(IPAddress())
+ address = Column(types.IPAddress())
network_id = Column(Integer, nullable=True)
virtual_interface_id = Column(Integer, nullable=True)
instance_uuid = Column(String(36), nullable=True)
@@ -717,13 +719,19 @@ class FixedIp(BASE, NovaBase):
leased = Column(Boolean, default=False)
reserved = Column(Boolean, default=False)
host = Column(String(255))
+ network = relationship(Network,
+ backref=backref('fixed_ips'),
+ foreign_keys=network_id,
+ primaryjoin='and_('
+ 'FixedIp.network_id == Network.id,'
+ 'FixedIp.deleted == 0)')
class FloatingIp(BASE, NovaBase):
"""Represents a floating ip that dynamically forwards to a fixed ip."""
__tablename__ = 'floating_ips'
id = Column(Integer, primary_key=True)
- address = Column(IPAddress())
+ address = Column(types.IPAddress())
fixed_ip_id = Column(Integer, nullable=True)
project_id = Column(String(255))
host = Column(String(255)) # , ForeignKey('hosts.id'))
@@ -735,6 +743,7 @@ class FloatingIp(BASE, NovaBase):
class DNSDomain(BASE, NovaBase):
"""Represents a DNS domain with availability zone or project info."""
__tablename__ = 'dns_domains'
+ deleted = Column(Boolean, default=False)
domain = Column(String(512), primary_key=True)
scope = Column(String(255))
availability_zone = Column(String(255))
@@ -745,7 +754,7 @@ class ConsolePool(BASE, NovaBase):
"""Represents pool of consoles on the same physical node."""
__tablename__ = 'console_pools'
id = Column(Integer, primary_key=True)
- address = Column(IPAddress())
+ address = Column(types.IPAddress())
username = Column(String(255))
password = Column(String(255))
console_type = Column(String(255))
@@ -779,7 +788,7 @@ class InstanceMetadata(BASE, NovaBase):
primaryjoin='and_('
'InstanceMetadata.instance_uuid == '
'Instance.uuid,'
- 'InstanceMetadata.deleted == False)')
+ 'InstanceMetadata.deleted == 0)')
class InstanceSystemMetadata(BASE, NovaBase):
@@ -793,7 +802,7 @@ class InstanceSystemMetadata(BASE, NovaBase):
nullable=False)
primary_join = ('and_(InstanceSystemMetadata.instance_uuid == '
- 'Instance.uuid, InstanceSystemMetadata.deleted == False)')
+ 'Instance.uuid, InstanceSystemMetadata.deleted == 0)')
instance = relationship(Instance, backref="system_metadata",
foreign_keys=instance_uuid,
primaryjoin=primary_join)
@@ -811,7 +820,7 @@ class InstanceTypeProjects(BASE, NovaBase):
foreign_keys=instance_type_id,
primaryjoin='and_('
'InstanceTypeProjects.instance_type_id == InstanceTypes.id,'
- 'InstanceTypeProjects.deleted == False)')
+ 'InstanceTypeProjects.deleted == 0)')
class InstanceTypeExtraSpecs(BASE, NovaBase):
@@ -826,7 +835,7 @@ class InstanceTypeExtraSpecs(BASE, NovaBase):
foreign_keys=instance_type_id,
primaryjoin='and_('
'InstanceTypeExtraSpecs.instance_type_id == InstanceTypes.id,'
- 'InstanceTypeExtraSpecs.deleted == False)')
+ 'InstanceTypeExtraSpecs.deleted == 0)')
class Cell(BASE, NovaBase):
@@ -880,24 +889,24 @@ class Aggregate(BASE, NovaBase):
secondary="aggregate_hosts",
primaryjoin='and_('
'Aggregate.id == AggregateHost.aggregate_id,'
- 'AggregateHost.deleted == False,'
- 'Aggregate.deleted == False)',
+ 'AggregateHost.deleted == 0,'
+ 'Aggregate.deleted == 0)',
secondaryjoin='and_('
'AggregateHost.aggregate_id == Aggregate.id, '
- 'AggregateHost.deleted == False,'
- 'Aggregate.deleted == False)',
+ 'AggregateHost.deleted == 0,'
+ 'Aggregate.deleted == 0)',
backref='aggregates')
_metadata = relationship(AggregateMetadata,
secondary="aggregate_metadata",
primaryjoin='and_('
'Aggregate.id == AggregateMetadata.aggregate_id,'
- 'AggregateMetadata.deleted == False,'
- 'Aggregate.deleted == False)',
+ 'AggregateMetadata.deleted == 0,'
+ 'Aggregate.deleted == 0)',
secondaryjoin='and_('
'AggregateMetadata.aggregate_id == Aggregate.id, '
- 'AggregateMetadata.deleted == False,'
- 'Aggregate.deleted == False)',
+ 'AggregateMetadata.deleted == 0,'
+ 'Aggregate.deleted == 0)',
backref='aggregates')
def _extra_keys(self):
diff --git a/nova/db/sqlalchemy/session.py b/nova/db/sqlalchemy/session.py
index 9c896ae97..28ec613c5 100644
--- a/nova/db/sqlalchemy/session.py
+++ b/nova/db/sqlalchemy/session.py
@@ -228,17 +228,17 @@ from eventlet import db_pool
from eventlet import greenthread
try:
import MySQLdb
+ from MySQLdb.constants import CLIENT as mysql_client_constants
except ImportError:
MySQLdb = None
+ mysql_client_constants = None
from sqlalchemy.exc import DisconnectionError, OperationalError, IntegrityError
import sqlalchemy.interfaces
import sqlalchemy.orm
from sqlalchemy.pool import NullPool, StaticPool
from sqlalchemy.sql.expression import literal_column
-from nova.exception import DBDuplicateEntry
-from nova.exception import DBError
-from nova.exception import InvalidUnicodeParameter
+import nova.exception
from nova.openstack.common import cfg
import nova.openstack.common.log as logging
from nova.openstack.common import timeutils
@@ -362,7 +362,7 @@ def raise_if_duplicate_entry_error(integrity_error, engine_name):
columns = columns.strip().split(", ")
else:
columns = get_columns_from_uniq_cons_or_name(columns)
- raise DBDuplicateEntry(columns, integrity_error)
+ raise nova.exception.DBDuplicateEntry(columns, integrity_error)
def wrap_db_error(f):
@@ -370,7 +370,7 @@ def wrap_db_error(f):
try:
return f(*args, **kwargs)
except UnicodeEncodeError:
- raise InvalidUnicodeParameter()
+ raise nova.exception.InvalidUnicodeParameter()
# note(boris-42): We should catch unique constraint violation and
# wrap it by our own DBDuplicateEntry exception. Unique constraint
# violation is wrapped by IntegrityError.
@@ -381,10 +381,10 @@ def wrap_db_error(f):
# means we should get names of columns, which values violate
# unique constraint, from error message.
raise_if_duplicate_entry_error(e, get_engine().name)
- raise DBError(e)
+ raise nova.exception.DBError(e)
except Exception, e:
LOG.exception(_('DB exception wrapped.'))
- raise DBError(e)
+ raise nova.exception.DBError(e)
_wrap.func_name = f.func_name
return _wrap
@@ -484,9 +484,21 @@ def create_engine(sql_connection):
'user': connection_dict.username,
'min_size': CONF.sql_min_pool_size,
'max_size': CONF.sql_max_pool_size,
- 'max_idle': CONF.sql_idle_timeout}
- creator = db_pool.ConnectionPool(MySQLdb, **pool_args)
- engine_args['creator'] = creator.create
+ 'max_idle': CONF.sql_idle_timeout,
+ 'client_flag': mysql_client_constants.FOUND_ROWS}
+
+ pool = db_pool.ConnectionPool(MySQLdb, **pool_args)
+
+ def creator():
+ conn = pool.create()
+ if isinstance(conn, tuple):
+ # NOTE(belliott) eventlet >= 0.10 returns a tuple
+ now, now, conn = conn
+
+ return conn
+
+ engine_args['creator'] = creator
+
else:
engine_args['pool_size'] = CONF.sql_max_pool_size
if CONF.sql_max_overflow is not None:
@@ -536,7 +548,7 @@ def create_engine(sql_connection):
class Query(sqlalchemy.orm.query.Query):
"""Subclass of sqlalchemy.query with soft_delete() method."""
def soft_delete(self, synchronize_session='evaluate'):
- return self.update({'deleted': True,
+ return self.update({'deleted': literal_column('id'),
'updated_at': literal_column('updated_at'),
'deleted_at': timeutils.utcnow()},
synchronize_session=synchronize_session)
@@ -552,6 +564,10 @@ class Session(sqlalchemy.orm.session.Session):
def flush(self, *args, **kwargs):
return super(Session, self).flush(*args, **kwargs)
+ @wrap_db_error
+ def execute(self, *args, **kwargs):
+ return super(Session, self).execute(*args, **kwargs)
+
def get_maker(engine, autocommit=True, expire_on_commit=False):
"""Return a SQLAlchemy sessionmaker using the given engine."""
diff --git a/nova/exception.py b/nova/exception.py
index c15fc1e43..6915c14bb 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -25,7 +25,6 @@ SHOULD include dedicated exception logging.
"""
import functools
-import itertools
import webob.exc
@@ -227,6 +226,20 @@ class Invalid(NovaException):
code = 400
+class InvalidBDM(Invalid):
+ message = _("Block Device Mapping is Invalid.")
+
+
+class InvalidBDMSnapshot(InvalidBDM):
+ message = _("Block Device Mapping is Invalid: "
+ "failed to get snapshot %(id)s.")
+
+
+class InvalidBDMVolume(InvalidBDM):
+ message = _("Block Device Mapping is Invalid: "
+ "failed to get volume %(id)s.")
+
+
class VolumeUnattached(Invalid):
message = _("Volume %(volume_id)s is not attached to anything")
@@ -317,7 +330,15 @@ class InstanceSuspendFailure(Invalid):
class InstanceResumeFailure(Invalid):
- message = _("Failed to resume server") + ": %(reason)s."
+ message = _("Failed to resume instance: %(reason)s.")
+
+
+class InstancePowerOnFailure(Invalid):
+ message = _("Failed to power on instance: %(reason)s.")
+
+
+class InstancePowerOffFailure(Invalid):
+ message = _("Failed to power off instance: %(reason)s.")
class InstanceRebootFailure(Invalid):
diff --git a/nova/image/glance.py b/nova/image/glance.py
index 1a6bba62f..6eac96c35 100644
--- a/nova/image/glance.py
+++ b/nova/image/glance.py
@@ -485,6 +485,8 @@ def get_remote_image_service(context, image_href):
:returns: a tuple of the form (image_service, image_id)
"""
+ # Calling out to another service may take a while, so lets log this
+ LOG.debug(_("fetching image %s from glance") % image_href)
#NOTE(bcwaldon): If image_href doesn't look like a URI, assume its a
# standalone image ID
if '/' not in str(image_href):
diff --git a/nova/network/api.py b/nova/network/api.py
index 5e3762e89..59172d9ec 100644
--- a/nova/network/api.py
+++ b/nova/network/api.py
@@ -25,6 +25,7 @@ from nova import exception
from nova.network import model as network_model
from nova.network import rpcapi as network_rpcapi
from nova.openstack.common import log as logging
+from nova import policy
LOG = logging.getLogger(__name__)
@@ -73,6 +74,27 @@ def update_instance_cache_with_nw_info(api, context, instance,
LOG.exception(_('Failed storing info cache'), instance=instance)
+def wrap_check_policy(func):
+ """Check policy corresponding to the wrapped methods prior to execution."""
+
+ @functools.wraps(func)
+ def wrapped(self, context, *args, **kwargs):
+ action = func.__name__
+ check_policy(context, action)
+ return func(self, context, *args, **kwargs)
+
+ return wrapped
+
+
+def check_policy(context, action):
+ target = {
+ 'project_id': context.project_id,
+ 'user_id': context.user_id,
+ }
+ _action = 'network:%s' % action
+ policy.enforce(context, _action, target)
+
+
class API(base.Base):
"""API for doing networking via the nova-network network manager.
@@ -86,58 +108,75 @@ class API(base.Base):
self.network_rpcapi = network_rpcapi.NetworkAPI()
super(API, self).__init__(**kwargs)
+ @wrap_check_policy
def get_all(self, context):
return self.network_rpcapi.get_all_networks(context)
+ @wrap_check_policy
def get(self, context, network_uuid):
return self.network_rpcapi.get_network(context, network_uuid)
+ @wrap_check_policy
def create(self, context, **kwargs):
return self.network_rpcapi.create_networks(context, **kwargs)
+ @wrap_check_policy
def delete(self, context, network_uuid):
return self.network_rpcapi.delete_network(context, network_uuid, None)
+ @wrap_check_policy
def disassociate(self, context, network_uuid):
return self.network_rpcapi.disassociate_network(context, network_uuid)
+ @wrap_check_policy
def get_fixed_ip(self, context, id):
return self.network_rpcapi.get_fixed_ip(context, id)
+ @wrap_check_policy
def get_fixed_ip_by_address(self, context, address):
return self.network_rpcapi.get_fixed_ip_by_address(context, address)
+ @wrap_check_policy
def get_floating_ip(self, context, id):
return self.network_rpcapi.get_floating_ip(context, id)
+ @wrap_check_policy
def get_floating_ip_pools(self, context):
return self.network_rpcapi.get_floating_ip_pools(context)
+ @wrap_check_policy
def get_floating_ip_by_address(self, context, address):
return self.network_rpcapi.get_floating_ip_by_address(context, address)
+ @wrap_check_policy
def get_floating_ips_by_project(self, context):
return self.network_rpcapi.get_floating_ips_by_project(context)
+ @wrap_check_policy
def get_floating_ips_by_fixed_address(self, context, fixed_address):
return self.network_rpcapi.get_floating_ips_by_fixed_address(context,
fixed_address)
+ @wrap_check_policy
def get_backdoor_port(self, context, host):
return self.network_rpcapi.get_backdoor_port(context, host)
+ @wrap_check_policy
def get_instance_id_by_floating_address(self, context, address):
# NOTE(tr3buchet): i hate this
return self.network_rpcapi.get_instance_id_by_floating_address(context,
address)
+ @wrap_check_policy
def get_vifs_by_instance(self, context, instance):
return self.network_rpcapi.get_vifs_by_instance(context,
instance['id'])
+ @wrap_check_policy
def get_vif_by_mac_address(self, context, mac_address):
return self.network_rpcapi.get_vif_by_mac_address(context, mac_address)
+ @wrap_check_policy
def allocate_floating_ip(self, context, pool=None):
"""Adds (allocates) a floating ip to a project from a pool."""
# NOTE(vish): We don't know which network host should get the ip
@@ -147,12 +186,14 @@ class API(base.Base):
return self.network_rpcapi.allocate_floating_ip(context,
context.project_id, pool, False)
+ @wrap_check_policy
def release_floating_ip(self, context, address,
affect_auto_assigned=False):
"""Removes (deallocates) a floating ip with address from a project."""
return self.network_rpcapi.deallocate_floating_ip(context, address,
affect_auto_assigned)
+ @wrap_check_policy
@refresh_cache
def associate_floating_ip(self, context, instance,
floating_address, fixed_address,
@@ -175,6 +216,7 @@ class API(base.Base):
# purge cached nw info for the original instance
update_instance_cache_with_nw_info(self, context, orig_instance)
+ @wrap_check_policy
@refresh_cache
def disassociate_floating_ip(self, context, instance, address,
affect_auto_assigned=False):
@@ -182,6 +224,7 @@ class API(base.Base):
self.network_rpcapi.disassociate_floating_ip(context, address,
affect_auto_assigned)
+ @wrap_check_policy
@refresh_cache
def allocate_for_instance(self, context, instance, vpn,
requested_networks, macs=None):
@@ -207,6 +250,7 @@ class API(base.Base):
return network_model.NetworkInfo.hydrate(nw_info)
+ @wrap_check_policy
def deallocate_for_instance(self, context, instance):
"""Deallocates all network structures related to instance."""
@@ -216,6 +260,7 @@ class API(base.Base):
args['host'] = instance['host']
self.network_rpcapi.deallocate_for_instance(context, **args)
+ @wrap_check_policy
@refresh_cache
def add_fixed_ip_to_instance(self, context, instance, network_id):
"""Adds a fixed ip to instance from specified network."""
@@ -224,6 +269,7 @@ class API(base.Base):
'network_id': network_id}
self.network_rpcapi.add_fixed_ip_to_instance(context, **args)
+ @wrap_check_policy
@refresh_cache
def remove_fixed_ip_from_instance(self, context, instance, address):
"""Removes a fixed ip from instance from specified network."""
@@ -233,11 +279,13 @@ class API(base.Base):
'address': address}
self.network_rpcapi.remove_fixed_ip_from_instance(context, **args)
+ @wrap_check_policy
def add_network_to_project(self, context, project_id, network_uuid=None):
"""Force adds another network to a project."""
self.network_rpcapi.add_network_to_project(context, project_id,
network_uuid)
+ @wrap_check_policy
def associate(self, context, network_uuid, host=_sentinel,
project=_sentinel):
"""Associate or disassociate host or project to network."""
@@ -248,6 +296,7 @@ class API(base.Base):
associations['project'] = project
self.network_rpcapi.associate(context, network_uuid, associations)
+ @wrap_check_policy
def get_instance_nw_info(self, context, instance, update_cache=True):
"""Returns all network info related to an instance."""
result = self._get_instance_nw_info(context, instance)
@@ -267,6 +316,7 @@ class API(base.Base):
return network_model.NetworkInfo.hydrate(nw_info)
+ @wrap_check_policy
def validate_networks(self, context, requested_networks):
"""validate the networks passed at the time of creating
the server
@@ -274,6 +324,7 @@ class API(base.Base):
return self.network_rpcapi.validate_networks(context,
requested_networks)
+ @wrap_check_policy
def get_instance_uuids_by_ip_filter(self, context, filters):
"""Returns a list of dicts in the form of
{'instance_uuid': uuid, 'ip': ip} that matched the ip_filter
@@ -281,12 +332,14 @@ class API(base.Base):
return self.network_rpcapi.get_instance_uuids_by_ip_filter(context,
filters)
+ @wrap_check_policy
def get_dns_domains(self, context):
"""Returns a list of available dns domains.
These can be used to create DNS entries for floating ips.
"""
return self.network_rpcapi.get_dns_domains(context)
+ @wrap_check_policy
def add_dns_entry(self, context, address, name, dns_type, domain):
"""Create specified DNS entry for address."""
args = {'address': address,
@@ -295,6 +348,7 @@ class API(base.Base):
'domain': domain}
return self.network_rpcapi.add_dns_entry(context, **args)
+ @wrap_check_policy
def modify_dns_entry(self, context, name, address, domain):
"""Create specified DNS entry for address."""
args = {'address': address,
@@ -302,35 +356,42 @@ class API(base.Base):
'domain': domain}
return self.network_rpcapi.modify_dns_entry(context, **args)
+ @wrap_check_policy
def delete_dns_entry(self, context, name, domain):
"""Delete the specified dns entry."""
args = {'name': name, 'domain': domain}
return self.network_rpcapi.delete_dns_entry(context, **args)
+ @wrap_check_policy
def delete_dns_domain(self, context, domain):
"""Delete the specified dns domain."""
return self.network_rpcapi.delete_dns_domain(context, domain=domain)
+ @wrap_check_policy
def get_dns_entries_by_address(self, context, address, domain):
"""Get entries for address and domain."""
args = {'address': address, 'domain': domain}
return self.network_rpcapi.get_dns_entries_by_address(context, **args)
+ @wrap_check_policy
def get_dns_entries_by_name(self, context, name, domain):
"""Get entries for name and domain."""
args = {'name': name, 'domain': domain}
return self.network_rpcapi.get_dns_entries_by_name(context, **args)
+ @wrap_check_policy
def create_private_dns_domain(self, context, domain, availability_zone):
"""Create a private DNS domain with nova availability zone."""
args = {'domain': domain, 'av_zone': availability_zone}
return self.network_rpcapi.create_private_dns_domain(context, **args)
+ @wrap_check_policy
def create_public_dns_domain(self, context, domain, project=None):
"""Create a public DNS domain with optional nova project."""
args = {'domain': domain, 'project': project}
return self.network_rpcapi.create_public_dns_domain(context, **args)
+ @wrap_check_policy
def setup_networks_on_host(self, context, instance, host=None,
teardown=False):
"""Setup or teardown the network structures on hosts related to
@@ -360,6 +421,7 @@ class API(base.Base):
instance['uuid'])
return [floating_ip['address'] for floating_ip in floating_ips]
+ @wrap_check_policy
def migrate_instance_start(self, context, instance, migration):
"""Start to migrate the network of an instance."""
args = dict(
@@ -378,6 +440,7 @@ class API(base.Base):
self.network_rpcapi.migrate_instance_start(context, **args)
+ @wrap_check_policy
def migrate_instance_finish(self, context, instance, migration):
"""Finish migrating the network of an instance."""
args = dict(
diff --git a/nova/network/l3.py b/nova/network/l3.py
index baf77c112..14abf41eb 100644
--- a/nova/network/l3.py
+++ b/nova/network/l3.py
@@ -48,13 +48,16 @@ class L3Driver(object):
""":returns: True/False (whether the driver is initialized)."""
raise NotImplementedError()
- def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id):
+ def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
+ network=None):
"""Add a floating IP bound to the fixed IP with an optional
l3_interface_id. Some drivers won't care about the
- l3_interface_id so just pass None in that case"""
+ l3_interface_id so just pass None in that case. Network
+ is also an optional parameter."""
raise NotImplementedError()
- def remove_floating_ip(self, floating_ip, fixed_ip, l3_interface_id):
+ def remove_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
+ network=None):
raise NotImplementedError()
def add_vpn(self, public_ip, port, private_ip):
@@ -96,15 +99,17 @@ class LinuxNetL3(L3Driver):
def remove_gateway(self, network_ref):
linux_net.unplug(network_ref)
- def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id):
+ def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
+ network=None):
linux_net.bind_floating_ip(floating_ip, l3_interface_id)
linux_net.ensure_floating_forward(floating_ip, fixed_ip,
- l3_interface_id)
+ l3_interface_id, network)
- def remove_floating_ip(self, floating_ip, fixed_ip, l3_interface_id):
+ def remove_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
+ network=None):
linux_net.unbind_floating_ip(floating_ip, l3_interface_id)
linux_net.remove_floating_forward(floating_ip, fixed_ip,
- l3_interface_id)
+ l3_interface_id, network)
def add_vpn(self, public_ip, port, private_ip):
linux_net.ensure_vpn_forward(public_ip, port, private_ip)
@@ -140,10 +145,12 @@ class NullL3(L3Driver):
def remove_gateway(self, network_ref):
pass
- def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id):
+ def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
+ network=None):
pass
- def remove_floating_ip(self, floating_ip, fixed_ip, l3_interface_id):
+ def remove_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
+ network=None):
pass
def add_vpn(self, public_ip, port, private_ip):
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 4fefb2db4..49afc65c4 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -378,7 +378,7 @@ class IptablesManager(object):
for table in tables:
start, end = self._find_table(all_lines, table)
all_lines[start:end] = self._modify_rules(
- all_lines[start:end], tables[table])
+ all_lines[start:end], tables[table], table_name=table)
self.execute('%s-restore' % (cmd,), '-c', run_as_root=True,
process_input='\n'.join(all_lines),
attempts=5)
@@ -392,18 +392,24 @@ class IptablesManager(object):
start = lines.index('*%s' % table_name) - 1
except ValueError:
# Couldn't find table_name
- # For Unit Tests
return (0, 0)
end = lines[start:].index('COMMIT') + start + 2
return (start, end)
- def _modify_rules(self, current_lines, table, binary=None):
+ def _modify_rules(self, current_lines, table, binary=None,
+ table_name=None):
unwrapped_chains = table.unwrapped_chains
chains = table.chains
remove_chains = table.remove_chains
rules = table.rules
remove_rules = table.remove_rules
+ if not current_lines:
+ fake_table = ['#Generated by nova',
+ '*' + table_name, 'COMMIT',
+ '#Completed by nova']
+ current_lines = fake_table
+
# Remove any trace of our rules
new_filter = filter(lambda line: binary_name not in line,
current_lines)
@@ -418,6 +424,9 @@ class IptablesManager(object):
if not rule.startswith(':'):
break
+ if not seen_chains:
+ rules_index = 2
+
our_rules = []
bot_rules = []
for rule in rules:
@@ -645,18 +654,29 @@ def ensure_vpn_forward(public_ip, port, private_ip):
iptables_manager.apply()
-def ensure_floating_forward(floating_ip, fixed_ip, device):
+def ensure_floating_forward(floating_ip, fixed_ip, device, network):
"""Ensure floating ip forwarding rule."""
for chain, rule in floating_forward_rules(floating_ip, fixed_ip, device):
iptables_manager.ipv4['nat'].add_rule(chain, rule)
iptables_manager.apply()
+ if device != network['bridge']:
+ ensure_ebtables_rules(*floating_ebtables_rules(fixed_ip, network))
-def remove_floating_forward(floating_ip, fixed_ip, device):
+def remove_floating_forward(floating_ip, fixed_ip, device, network):
"""Remove forwarding for floating ip."""
for chain, rule in floating_forward_rules(floating_ip, fixed_ip, device):
iptables_manager.ipv4['nat'].remove_rule(chain, rule)
iptables_manager.apply()
+ if device != network['bridge']:
+ remove_ebtables_rules(*floating_ebtables_rules(fixed_ip, network))
+
+
+def floating_ebtables_rules(fixed_ip, network):
+ """Makes sure only in-network traffic is bridged."""
+ return (['PREROUTING --logical-in %s -p ipv4 --ip-src %s '
+ '! --ip-dst %s -j redirect --redirect-target ACCEPT' %
+ (network['bridge'], fixed_ip, network['cidr'])], 'nat')
def floating_forward_rules(floating_ip, fixed_ip, device):
@@ -1124,6 +1144,40 @@ def _create_veth_pair(dev1_name, dev2_name):
run_as_root=True)
+def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id):
+ utils.execute('ovs-vsctl', '--', '--may-exist', 'add-port',
+ bridge, dev,
+ '--', 'set', 'Interface', dev,
+ 'external-ids:iface-id=%s' % iface_id,
+ 'external-ids:iface-status=active',
+ 'external-ids:attached-mac=%s' % mac,
+ 'external-ids:vm-uuid=%s' % instance_id,
+ run_as_root=True)
+
+
+def delete_ovs_vif_port(bridge, dev):
+ utils.execute('ovs-vsctl', 'del-port', bridge, dev,
+ run_as_root=True)
+ utils.execute('ip', 'link', 'delete', dev,
+ run_as_root=True)
+
+
+def create_tap_dev(dev, mac_address=None):
+ if not device_exists(dev):
+ try:
+ # First, try with 'ip'
+ utils.execute('ip', 'tuntap', 'add', dev, 'mode', 'tap',
+ run_as_root=True, check_exit_code=[0, 2, 254])
+ except exception.ProcessExecutionError:
+ # Second option: tunctl
+ utils.execute('tunctl', '-b', '-t', dev, run_as_root=True)
+ if mac_address:
+ utils.execute('ip', 'link', 'set', dev, 'address', mac_address,
+ run_as_root=True, check_exit_code=[0, 2, 254])
+ utils.execute('ip', 'link', 'set', dev, 'up', run_as_root=True,
+ check_exit_code=[0, 2, 254])
+
+
# Similar to compute virt layers, the Linux network node
# code uses a flexible driver model to support different ways
# of creating ethernet interfaces and attaching them to the network.
@@ -1387,18 +1441,18 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
@lockutils.synchronized('ebtables', 'nova-', external=True)
-def ensure_ebtables_rules(rules):
+def ensure_ebtables_rules(rules, table='filter'):
for rule in rules:
- cmd = ['ebtables', '-D'] + rule.split()
+ cmd = ['ebtables', '-t', table, '-D'] + rule.split()
_execute(*cmd, check_exit_code=False, run_as_root=True)
- cmd[1] = '-I'
+ cmd[3] = '-I'
_execute(*cmd, run_as_root=True)
@lockutils.synchronized('ebtables', 'nova-', external=True)
-def remove_ebtables_rules(rules):
+def remove_ebtables_rules(rules, table='filter'):
for rule in rules:
- cmd = ['ebtables', '-D'] + rule.split()
+ cmd = ['ebtables', '-t', table, '-D'] + rule.split()
_execute(*cmd, check_exit_code=False, run_as_root=True)
@@ -1535,7 +1589,7 @@ class QuantumLinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
iptables_manager.ipv4['filter'].add_rule('FORWARD',
'--out-interface %s -j ACCEPT' % bridge)
- QuantumLinuxBridgeInterfaceDriver.create_tap_dev(dev, mac_address)
+ create_tap_dev(dev, mac_address)
if not device_exists(bridge):
LOG.debug(_("Starting bridge %s "), bridge)
@@ -1570,22 +1624,6 @@ class QuantumLinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver):
LOG.debug(_("Unplugged gateway interface '%s'"), dev)
return dev
- @classmethod
- def create_tap_dev(_self, dev, mac_address=None):
- if not device_exists(dev):
- try:
- # First, try with 'ip'
- utils.execute('ip', 'tuntap', 'add', dev, 'mode', 'tap',
- run_as_root=True, check_exit_code=[0, 2, 254])
- except exception.ProcessExecutionError:
- # Second option: tunctl
- utils.execute('tunctl', '-b', '-t', dev, run_as_root=True)
- if mac_address:
- utils.execute('ip', 'link', 'set', dev, 'address', mac_address,
- run_as_root=True, check_exit_code=[0, 2, 254])
- utils.execute('ip', 'link', 'set', dev, 'up', run_as_root=True,
- check_exit_code=[0, 2, 254])
-
def get_dev(self, network):
dev = self.GATEWAY_INTERFACE_PREFIX + str(network['uuid'][0:11])
return dev
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 9ca7680a5..d1dabdfd9 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -44,11 +44,9 @@ topologies. All of the network commands are issued to a subclass of
"""
import datetime
-import functools
import itertools
import math
import re
-import socket
import uuid
from eventlet import greenpool
@@ -73,7 +71,6 @@ from nova.openstack.common.notifier import api as notifier
from nova.openstack.common.rpc import common as rpc_common
from nova.openstack.common import timeutils
from nova.openstack.common import uuidutils
-import nova.policy
from nova import quota
from nova import servicegroup
from nova import utils
@@ -277,27 +274,6 @@ class RPCAllocateFixedIP(object):
self.network_rpcapi.deallocate_fixed_ip(context, address, host)
-def wrap_check_policy(func):
- """Check policy corresponding to the wrapped methods prior to execution."""
-
- @functools.wraps(func)
- def wrapped(self, context, *args, **kwargs):
- action = func.__name__
- check_policy(context, action)
- return func(self, context, *args, **kwargs)
-
- return wrapped
-
-
-def check_policy(context, action):
- target = {
- 'project_id': context.project_id,
- 'user_id': context.user_id,
- }
- _action = 'network:%s' % action
- nova.policy.enforce(context, _action, target)
-
-
class FloatingIP(object):
"""Mixin class for adding floating IP functionality to a manager."""
@@ -317,22 +293,23 @@ class FloatingIP(object):
fixed_ip_id = floating_ip.get('fixed_ip_id')
if fixed_ip_id:
try:
- fixed_ip_ref = self.db.fixed_ip_get(admin_context,
- fixed_ip_id)
+ fixed_ip = self.db.fixed_ip_get(admin_context,
+ fixed_ip_id,
+ get_network=True)
except exception.FixedIpNotFound:
msg = _('Fixed ip %(fixed_ip_id)s not found') % locals()
LOG.debug(msg)
continue
- fixed_address = fixed_ip_ref['address']
interface = CONF.public_interface or floating_ip['interface']
try:
self.l3driver.add_floating_ip(floating_ip['address'],
- fixed_address, interface)
+ fixed_ip['address'],
+ interface,
+ fixed_ip['network'])
except exception.ProcessExecutionError:
LOG.debug(_('Interface %(interface)s not found'), locals())
raise exception.NoFloatingIpInterface(interface=interface)
- @wrap_check_policy
def allocate_for_instance(self, context, **kwargs):
"""Handles allocating the floating IP resources for an instance.
@@ -373,7 +350,6 @@ class FloatingIP(object):
return nw_info
- @wrap_check_policy
def deallocate_for_instance(self, context, **kwargs):
"""Handles deallocating floating IP resources for an instance.
@@ -436,7 +412,6 @@ class FloatingIP(object):
'project': context.project_id})
raise exception.NotAuthorized()
- @wrap_check_policy
def allocate_floating_ip(self, context, project_id, auto_assigned=False,
pool=None):
"""Gets a floating ip from the pool."""
@@ -476,7 +451,6 @@ class FloatingIP(object):
return floating_ip
@rpc_common.client_exceptions(exception.FloatingIpNotFoundForAddress)
- @wrap_check_policy
def deallocate_floating_ip(self, context, address,
affect_auto_assigned=False):
"""Returns a floating ip to the pool."""
@@ -523,7 +497,6 @@ class FloatingIP(object):
QUOTAS.commit(context, reservations)
@rpc_common.client_exceptions(exception.FloatingIpNotFoundForAddress)
- @wrap_check_policy
def associate_floating_ip(self, context, floating_address, fixed_address,
affect_auto_assigned=False):
"""Associates a floating ip with a fixed ip.
@@ -587,17 +560,17 @@ class FloatingIP(object):
@lockutils.synchronized(unicode(floating_address), 'nova-')
def do_associate():
# associate floating ip
- res = self.db.floating_ip_fixed_ip_associate(context,
- floating_address,
- fixed_address,
- self.host)
- if not res:
+ fixed = self.db.floating_ip_fixed_ip_associate(context,
+ floating_address,
+ fixed_address,
+ self.host)
+ if not fixed:
# NOTE(vish): ip was already associated
return
try:
# gogo driver time
self.l3driver.add_floating_ip(floating_address, fixed_address,
- interface)
+ interface, fixed['network'])
except exception.ProcessExecutionError as e:
self.db.floating_ip_disassociate(context, floating_address)
if "Cannot find device" in str(e):
@@ -614,7 +587,6 @@ class FloatingIP(object):
do_associate()
@rpc_common.client_exceptions(exception.FloatingIpNotFoundForAddress)
- @wrap_check_policy
def disassociate_floating_ip(self, context, address,
affect_auto_assigned=False):
"""Disassociates a floating ip from its fixed ip.
@@ -681,15 +653,15 @@ class FloatingIP(object):
# don't worry about this case because the minuscule
# window where the ip is on both hosts shouldn't cause
# any problems.
- fixed_address = self.db.floating_ip_disassociate(context, address)
+ fixed = self.db.floating_ip_disassociate(context, address)
- if not fixed_address:
+ if not fixed:
# NOTE(vish): ip was already disassociated
return
if interface:
# go go driver time
- self.l3driver.remove_floating_ip(address, fixed_address,
- interface)
+ self.l3driver.remove_floating_ip(address, fixed['address'],
+ interface, fixed['network'])
payload = dict(project_id=context.project_id,
instance_id=instance_uuid,
floating_ip=address)
@@ -700,38 +672,32 @@ class FloatingIP(object):
do_disassociate()
@rpc_common.client_exceptions(exception.FloatingIpNotFound)
- @wrap_check_policy
def get_floating_ip(self, context, id):
"""Returns a floating IP as a dict."""
return dict(self.db.floating_ip_get(context, id).iteritems())
- @wrap_check_policy
def get_floating_pools(self, context):
"""Returns list of floating pools."""
# NOTE(maurosr) This method should be removed in future, replaced by
# get_floating_ip_pools. See bug #1091668
return self.get_floating_ip_pools(context)
- @wrap_check_policy
def get_floating_ip_pools(self, context):
"""Returns list of floating ip pools."""
pools = self.db.floating_ip_get_pools(context)
return [dict(pool.iteritems()) for pool in pools]
- @wrap_check_policy
def get_floating_ip_by_address(self, context, address):
"""Returns a floating IP as a dict."""
return dict(self.db.floating_ip_get_by_address(context,
address).iteritems())
- @wrap_check_policy
def get_floating_ips_by_project(self, context):
"""Returns the floating IPs allocated to a project."""
ips = self.db.floating_ip_get_all_by_project(context,
context.project_id)
return [dict(ip.iteritems()) for ip in ips]
- @wrap_check_policy
def get_floating_ips_by_fixed_address(self, context, fixed_address):
"""Returns the floating IPs associated with a fixed_address."""
floating_ips = self.db.floating_ip_get_by_fixed_address(context,
@@ -745,7 +711,6 @@ class FloatingIP(object):
return True
return False if floating_ip.get('fixed_ip_id') else True
- @wrap_check_policy
def migrate_instance_start(self, context, instance_uuid,
floating_addresses,
rxtx_factor=None, project_id=None,
@@ -769,10 +734,12 @@ class FloatingIP(object):
interface = CONF.public_interface or floating_ip['interface']
fixed_ip = self.db.fixed_ip_get(context,
- floating_ip['fixed_ip_id'])
+ floating_ip['fixed_ip_id'],
+ get_network=True)
self.l3driver.remove_floating_ip(floating_ip['address'],
fixed_ip['address'],
- interface)
+ interface,
+ fixed_ip['network'])
# NOTE(wenjianhn): Make this address will not be bound to public
# interface when restarts nova-network on dest compute node
@@ -780,7 +747,6 @@ class FloatingIP(object):
floating_ip['address'],
{'host': None})
- @wrap_check_policy
def migrate_instance_finish(self, context, instance_uuid,
floating_addresses, host=None,
rxtx_factor=None, project_id=None,
@@ -811,10 +777,12 @@ class FloatingIP(object):
interface = CONF.public_interface or floating_ip['interface']
fixed_ip = self.db.fixed_ip_get(context,
- floating_ip['fixed_ip_id'])
+ floating_ip['fixed_ip_id'],
+ get_network=True)
self.l3driver.add_floating_ip(floating_ip['address'],
fixed_ip['address'],
- interface)
+ interface,
+ fixed_ip['network'])
def _prepare_domain_entry(self, context, domain):
domainref = self.db.dnsdomain_get(context, domain)
@@ -831,7 +799,6 @@ class FloatingIP(object):
'project': project}
return this_domain
- @wrap_check_policy
def get_dns_domains(self, context):
domains = []
@@ -854,17 +821,14 @@ class FloatingIP(object):
return domains
- @wrap_check_policy
def add_dns_entry(self, context, address, name, dns_type, domain):
self.floating_dns_manager.create_entry(name, address,
dns_type, domain)
- @wrap_check_policy
def modify_dns_entry(self, context, address, name, domain):
self.floating_dns_manager.modify_address(name, address,
domain)
- @wrap_check_policy
def delete_dns_entry(self, context, name, domain):
self.floating_dns_manager.delete_entry(name, domain)
@@ -877,17 +841,14 @@ class FloatingIP(object):
for name in names:
self.delete_dns_entry(context, name, domain['domain'])
- @wrap_check_policy
def get_dns_entries_by_address(self, context, address, domain):
return self.floating_dns_manager.get_entries_by_address(address,
domain)
- @wrap_check_policy
def get_dns_entries_by_name(self, context, name, domain):
return self.floating_dns_manager.get_entries_by_name(name,
domain)
- @wrap_check_policy
def create_private_dns_domain(self, context, domain, av_zone):
self.db.dnsdomain_register_for_zone(context, domain, av_zone)
try:
@@ -897,7 +858,6 @@ class FloatingIP(object):
'changing zone to |%(av_zone)s|.'),
{'domain': domain, 'av_zone': av_zone})
- @wrap_check_policy
def create_public_dns_domain(self, context, domain, project):
self.db.dnsdomain_register_for_project(context, domain, project)
try:
@@ -907,7 +867,6 @@ class FloatingIP(object):
'changing project to |%(project)s|.'),
{'domain': domain, 'project': project})
- @wrap_check_policy
def delete_dns_domain(self, context, domain):
self.db.dnsdomain_unregister(context, domain)
self.floating_dns_manager.delete_domain(domain)
@@ -929,7 +888,7 @@ class NetworkManager(manager.SchedulerDependentManager):
The one at a time part is to flatten the layout to help scale
"""
- RPC_API_VERSION = '1.6'
+ RPC_API_VERSION = '1.7'
# If True, this manager requires VIF to create a bridge.
SHOULD_CREATE_BRIDGE = False
@@ -1066,7 +1025,6 @@ class NetworkManager(manager.SchedulerDependentManager):
# floating ips MUST override this or use the Mixin
return []
- @wrap_check_policy
def get_instance_uuids_by_ip_filter(self, context, filters):
fixed_ip_filter = filters.get('fixed_ip')
ip_filter = re.compile(str(filters.get('ip')))
@@ -1136,7 +1094,6 @@ class NetworkManager(manager.SchedulerDependentManager):
return [network for network in networks if
not network['vlan']]
- @wrap_check_policy
def allocate_for_instance(self, context, **kwargs):
"""Handles allocating the various network resources for an instance.
@@ -1169,7 +1126,6 @@ class NetworkManager(manager.SchedulerDependentManager):
return self.get_instance_nw_info(context, instance_id, instance_uuid,
rxtx_factor, host)
- @wrap_check_policy
def deallocate_for_instance(self, context, **kwargs):
"""Handles deallocating various network resources for an instance.
@@ -1205,7 +1161,6 @@ class NetworkManager(manager.SchedulerDependentManager):
self.db.virtual_interface_delete_by_instance(read_deleted_context,
instance['uuid'])
- @wrap_check_policy
def get_instance_nw_info(self, context, instance_id, instance_uuid,
rxtx_factor, host, **kwargs):
"""Creates network info list for instance.
@@ -1388,7 +1343,6 @@ class NetworkManager(manager.SchedulerDependentManager):
instance_uuid)
raise exception.VirtualInterfaceMacAddressException()
- @wrap_check_policy
def add_fixed_ip_to_instance(self, context, instance_id, host, network_id):
"""Adds a fixed ip to an instance from specified network."""
if uuidutils.is_uuid_like(network_id):
@@ -1401,7 +1355,6 @@ class NetworkManager(manager.SchedulerDependentManager):
"""Return backdoor port for eventlet_backdoor."""
return self.backdoor_port
- @wrap_check_policy
def remove_fixed_ip_from_instance(self, context, instance_id, host,
address):
"""Removes a fixed ip from an instance from specified network."""
@@ -1776,7 +1729,6 @@ class NetworkManager(manager.SchedulerDependentManager):
self._create_fixed_ips(context, network['id'], fixed_cidr)
return networks
- @wrap_check_policy
def delete_network(self, context, fixed_range, uuid,
require_disassociated=True):
@@ -1881,7 +1833,6 @@ class NetworkManager(manager.SchedulerDependentManager):
"""Sets up network on this host."""
raise NotImplementedError()
- @wrap_check_policy
def validate_networks(self, context, networks):
"""check if the networks exists and host
is set to each network.
@@ -1920,7 +1871,6 @@ class NetworkManager(manager.SchedulerDependentManager):
return self.db.network_get_all_by_uuids(context, network_uuids,
project_only="allow_none")
- @wrap_check_policy
def get_vifs_by_instance(self, context, instance_id):
"""Returns the vifs associated with an instance."""
instance = self.db.instance_get(context, instance_id)
@@ -1936,12 +1886,10 @@ class NetworkManager(manager.SchedulerDependentManager):
else:
return fixed_ip['instance_uuid']
- @wrap_check_policy
def get_network(self, context, network_uuid):
network = self.db.network_get_by_uuid(context.elevated(), network_uuid)
return jsonutils.to_primitive(network)
- @wrap_check_policy
def get_all_networks(self, context):
try:
networks = self.db.network_get_all(context)
@@ -1949,18 +1897,15 @@ class NetworkManager(manager.SchedulerDependentManager):
return []
return [jsonutils.to_primitive(network) for network in networks]
- @wrap_check_policy
def disassociate_network(self, context, network_uuid):
network = self.get_network(context, network_uuid)
self.db.network_disassociate(context, network['id'])
- @wrap_check_policy
def get_fixed_ip(self, context, id):
"""Return a fixed ip."""
fixed = self.db.fixed_ip_get(context, id)
return jsonutils.to_primitive(fixed)
- @wrap_check_policy
def get_fixed_ip_by_address(self, context, address):
fixed = self.db.fixed_ip_get_by_address(context, address)
return jsonutils.to_primitive(fixed)
@@ -2064,34 +2009,28 @@ class FlatManager(NetworkManager):
# We were throwing an exception, but this was messing up horizon.
# Timing makes it difficult to implement floating ips here, in Essex.
- @wrap_check_policy
def get_floating_ip(self, context, id):
"""Returns a floating IP as a dict."""
return None
- @wrap_check_policy
def get_floating_pools(self, context):
"""Returns list of floating pools."""
# NOTE(maurosr) This method should be removed in future, replaced by
# get_floating_ip_pools. See bug #1091668
return {}
- @wrap_check_policy
def get_floating_ip_pools(self, context):
"""Returns list of floating ip pools."""
return {}
- @wrap_check_policy
def get_floating_ip_by_address(self, context, address):
"""Returns a floating IP as a dict."""
return None
- @wrap_check_policy
def get_floating_ips_by_project(self, context):
"""Returns the floating IPs allocated to a project."""
return []
- @wrap_check_policy
def get_floating_ips_by_fixed_address(self, context, fixed_address):
"""Returns the floating IPs associated with a fixed_address."""
return []
@@ -2248,7 +2187,6 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
self._setup_network_on_host(context, network)
return address
- @wrap_check_policy
def add_network_to_project(self, context, project_id, network_uuid=None):
"""Force adds another network to a project."""
if network_uuid is not None:
@@ -2257,7 +2195,6 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager):
network_id = None
self.db.network_associate(context, project_id, network_id, force=True)
- @wrap_check_policy
def associate(self, context, network_uuid, associations):
"""Associate or disassociate host or project to network."""
network_id = self.get_network(context, network_uuid)['id']
diff --git a/nova/network/model.py b/nova/network/model.py
index 0771156c1..9accb883e 100644
--- a/nova/network/model.py
+++ b/nova/network/model.py
@@ -207,7 +207,7 @@ class Network(Model):
class VIF(Model):
"""Represents a Virtual Interface in Nova."""
def __init__(self, id=None, address=None, network=None, type=None,
- devname=None, **kwargs):
+ devname=None, ovs_interfaceid=None, **kwargs):
super(VIF, self).__init__()
self['id'] = id
@@ -216,6 +216,8 @@ class VIF(Model):
self['type'] = type
self['devname'] = devname
+ self['ovs_interfaceid'] = ovs_interfaceid
+
self._set_meta(kwargs)
def __eq__(self, other):
@@ -381,6 +383,7 @@ class NetworkInfo(list):
'vif_type': vif['type'],
'vif_devname': vif.get('devname'),
'vif_uuid': vif['id'],
+ 'ovs_interfaceid': vif.get('ovs_interfaceid'),
'rxtx_cap': vif.get_meta('rxtx_cap', 0),
'dns': [get_ip(ip) for ip in subnet_v4['dns']],
'ips': [fixed_ip_dict(ip, subnet)
diff --git a/nova/network/quantumv2/__init__.py b/nova/network/quantumv2/__init__.py
index 914600ed8..13ce8b794 100644
--- a/nova/network/quantumv2/__init__.py
+++ b/nova/network/quantumv2/__init__.py
@@ -30,6 +30,7 @@ def _get_auth_token():
httpclient = client.HTTPClient(
username=CONF.quantum_admin_username,
tenant_name=CONF.quantum_admin_tenant_name,
+ region_name=CONF.quantum_region_name,
password=CONF.quantum_admin_password,
auth_url=CONF.quantum_admin_auth_url,
timeout=CONF.quantum_url_timeout,
diff --git a/nova/network/quantumv2/api.py b/nova/network/quantumv2/api.py
index 29e5e2f06..b2c20e225 100644
--- a/nova/network/quantumv2/api.py
+++ b/nova/network/quantumv2/api.py
@@ -41,6 +41,8 @@ quantum_opts = [
help='password for connecting to quantum in admin context'),
cfg.StrOpt('quantum_admin_tenant_name',
help='tenant name for connecting to quantum in admin context'),
+ cfg.StrOpt('quantum_region_name',
+ help='region name for connecting to quantum in admin context'),
cfg.StrOpt('quantum_admin_auth_url',
default='http://localhost:5000/v2.0',
help='auth url for connecting to quantum in admin context'),
@@ -661,11 +663,13 @@ class API(base.Base):
if fixed_ip.is_in_subnet(subnet)]
bridge = None
+ ovs_interfaceid = None
vif_type = port.get('binding:vif_type')
# TODO(berrange) Quantum should pass the bridge name
# in another binding metadata field
if vif_type == network_model.VIF_TYPE_OVS:
bridge = CONF.quantum_ovs_bridge
+ ovs_interfaceid = port['id']
elif vif_type == network_model.VIF_TYPE_BRIDGE:
bridge = "brq" + port['network_id']
@@ -688,6 +692,7 @@ class API(base.Base):
address=port['mac_address'],
network=network,
type=port.get('binding:vif_type'),
+ ovs_interfaceid=ovs_interfaceid,
devname=devname))
return nw_info
diff --git a/nova/notifications.py b/nova/notifications.py
index f399ac55d..f40fff7f2 100644
--- a/nova/notifications.py
+++ b/nova/notifications.py
@@ -21,7 +21,6 @@ the system.
import nova.context
from nova import db
-from nova import exception
from nova.image import glance
from nova import network
from nova.network import model as network_model
@@ -283,12 +282,8 @@ def info_from_instance(context, instance_ref, network_info,
instance_type_name = instance_ref.get('instance_type', {}).get('name', '')
if system_metadata is None:
- try:
- system_metadata = db.instance_system_metadata_get(
- context, instance_ref['uuid'])
-
- except exception.NotFound:
- system_metadata = {}
+ system_metadata = utils.metadata_to_dict(
+ instance_ref['system_metadata'])
instance_info = dict(
# Owner properties
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index 09de10388..16714a5ff 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -23,7 +23,6 @@ Scheduler base class that all Schedulers should inherit from
import sys
-from nova.compute import api as compute_api
from nova.compute import power_state
from nova.compute import rpcapi as compute_rpcapi
from nova.compute import utils as compute_utils
@@ -115,7 +114,6 @@ class Scheduler(object):
def __init__(self):
self.host_manager = importutils.import_object(
CONF.scheduler_host_manager)
- self.compute_api = compute_api.API()
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
self.servicegroup_api = servicegroup.API()
diff --git a/nova/scheduler/filter_scheduler.py b/nova/scheduler/filter_scheduler.py
index 07a3f578a..9384e1495 100644
--- a/nova/scheduler/filter_scheduler.py
+++ b/nova/scheduler/filter_scheduler.py
@@ -47,15 +47,15 @@ class FilterScheduler(driver.Scheduler):
Returns a list of the instances created.
"""
- instance_uuids = request_spec.get('instance_uuids')
- num_instances = len(instance_uuids)
- LOG.debug(_("Attempting to build %(num_instances)d instance(s)") %
- locals())
-
payload = dict(request_spec=request_spec)
notifier.notify(context, notifier.publisher_id("scheduler"),
'scheduler.run_instance.start', notifier.INFO, payload)
+ instance_uuids = request_spec.pop('instance_uuids')
+ num_instances = len(instance_uuids)
+ LOG.debug(_("Attempting to build %(num_instances)d instance(s)") %
+ locals())
+
weighed_hosts = self._schedule(context, request_spec,
filter_properties, instance_uuids)
@@ -124,6 +124,8 @@ class FilterScheduler(driver.Scheduler):
filter_properties, requested_networks, injected_files,
admin_password, is_first_time, instance_uuid=None):
"""Create the requested resource in this Zone."""
+ # NOTE(vish): add our current instance back into the request spec
+ request_spec['instance_uuids'] = [instance_uuid]
payload = dict(request_spec=request_spec,
weighted_host=weighed_host.to_dict(),
instance_id=instance_uuid)
diff --git a/nova/scheduler/filters/affinity_filter.py b/nova/scheduler/filters/affinity_filter.py
index 03bf0dd6e..7e51a15f2 100644
--- a/nova/scheduler/filters/affinity_filter.py
+++ b/nova/scheduler/filters/affinity_filter.py
@@ -25,12 +25,6 @@ class AffinityFilter(filters.BaseHostFilter):
def __init__(self):
self.compute_api = compute.API()
- def _all_hosts(self, context):
- all_hosts = {}
- for instance in self.compute_api.get_all(context):
- all_hosts[instance['uuid']] = instance['host']
- return all_hosts
-
class DifferentHostFilter(AffinityFilter):
'''Schedule the instance on a different host from a set of instances.'''
@@ -38,15 +32,15 @@ class DifferentHostFilter(AffinityFilter):
def host_passes(self, host_state, filter_properties):
context = filter_properties['context']
scheduler_hints = filter_properties.get('scheduler_hints') or {}
- me = host_state.host
affinity_uuids = scheduler_hints.get('different_host', [])
if isinstance(affinity_uuids, basestring):
affinity_uuids = [affinity_uuids]
if affinity_uuids:
- all_hosts = self._all_hosts(context)
- return not any([i for i in affinity_uuids
- if all_hosts.get(i) == me])
+ return not self.compute_api.get_all(context,
+ {'host': host_state.host,
+ 'uuid': affinity_uuids,
+ 'deleted': False})
# With no different_host key
return True
@@ -59,16 +53,14 @@ class SameHostFilter(AffinityFilter):
def host_passes(self, host_state, filter_properties):
context = filter_properties['context']
scheduler_hints = filter_properties.get('scheduler_hints') or {}
- me = host_state.host
affinity_uuids = scheduler_hints.get('same_host', [])
if isinstance(affinity_uuids, basestring):
affinity_uuids = [affinity_uuids]
if affinity_uuids:
- all_hosts = self._all_hosts(context)
- return any([i for i
- in affinity_uuids
- if all_hosts.get(i) == me])
+ return self.compute_api.get_all(context, {'host': host_state.host,
+ 'uuid': affinity_uuids,
+ 'deleted': False})
# With no same_host key
return True
diff --git a/nova/scheduler/filters/availability_zone_filter.py b/nova/scheduler/filters/availability_zone_filter.py
index 585acbaf8..390276ea3 100644
--- a/nova/scheduler/filters/availability_zone_filter.py
+++ b/nova/scheduler/filters/availability_zone_filter.py
@@ -14,7 +14,6 @@
# under the License.
-from nova import availability_zones
from nova import db
from nova.openstack.common import cfg
from nova.scheduler import filters
diff --git a/nova/service.py b/nova/service.py
index df8cf020f..2daceba80 100644
--- a/nova/service.py
+++ b/nova/service.py
@@ -38,7 +38,6 @@ from nova.openstack.common import eventlet_backdoor
from nova.openstack.common import importutils
from nova.openstack.common import log as logging
from nova.openstack.common import rpc
-from nova.openstack.common.rpc import common as rpc_common
from nova import servicegroup
from nova import utils
from nova import version
@@ -61,6 +60,9 @@ service_opts = [
cfg.ListOpt('enabled_apis',
default=['ec2', 'osapi_compute', 'metadata'],
help='a list of APIs to enable by default'),
+ cfg.ListOpt('enabled_ssl_apis',
+ default=[],
+ help='a list of APIs with enabled SSL'),
cfg.StrOpt('ec2_listen',
default="0.0.0.0",
help='IP address for EC2 API to listen'),
@@ -399,6 +401,14 @@ class Service(object):
self.binary = binary
self.topic = topic
self.manager_class_name = manager
+ # NOTE(russellb) We want to make sure to create the servicegroup API
+ # instance early, before creating other things such as the manager,
+ # that will also create a servicegroup API instance. Internally, the
+ # servicegroup only allocates a single instance of the driver API and
+ # we want to make sure that our value of db_allowed is there when it
+ # gets created. For that to happen, this has to be the first instance
+ # of the servicegroup API.
+ self.servicegroup_api = servicegroup.API(db_allowed=db_allowed)
manager_class = importutils.import_class(self.manager_class_name)
self.manager = manager_class(host=self.host, *args, **kwargs)
self.report_interval = report_interval
@@ -408,15 +418,14 @@ class Service(object):
self.saved_args, self.saved_kwargs = args, kwargs
self.timers = []
self.backdoor_port = None
- self.db_allowed = db_allowed
self.conductor_api = conductor.API(use_local=db_allowed)
self.conductor_api.wait_until_ready(context.get_admin_context())
- self.servicegroup_api = servicegroup.API(db_allowed=db_allowed)
def start(self):
verstr = version.version_string_with_package()
LOG.audit(_('Starting %(topic)s node (version %(version)s)'),
{'topic': self.topic, 'version': verstr})
+ self.basic_config_check()
self.manager.init_host()
self.model_disconnected = False
ctxt = context.get_admin_context()
@@ -561,11 +570,21 @@ class Service(object):
ctxt = context.get_admin_context()
return self.manager.periodic_tasks(ctxt, raise_on_error=raise_on_error)
+ def basic_config_check(self):
+ """Perform basic config checks before starting processing."""
+ # Make sure the tempdir exists and is writable
+ try:
+ with utils.tempdir() as tmpdir:
+ pass
+ except Exception as e:
+ LOG.error(_('Temporary directory is invalid: %s'), e)
+ sys.exit(1)
+
class WSGIService(object):
"""Provides ability to launch API from a 'paste' configuration."""
- def __init__(self, name, loader=None):
+ def __init__(self, name, loader=None, use_ssl=False, max_url_len=None):
"""Initialize, but do not start the WSGI server.
:param name: The name of the WSGI server given to the loader.
@@ -580,10 +599,13 @@ class WSGIService(object):
self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")
self.port = getattr(CONF, '%s_listen_port' % name, 0)
self.workers = getattr(CONF, '%s_workers' % name, None)
+ self.use_ssl = use_ssl
self.server = wsgi.Server(name,
self.app,
host=self.host,
- port=self.port)
+ port=self.port,
+ use_ssl=self.use_ssl,
+ max_url_len=max_url_len)
# Pull back actual port used
self.port = self.server.port
self.backdoor_port = None
diff --git a/nova/servicegroup/__init__.py b/nova/servicegroup/__init__.py
index 318d30ff7..a804c62fa 100644
--- a/nova/servicegroup/__init__.py
+++ b/nova/servicegroup/__init__.py
@@ -19,4 +19,6 @@ The membership service for Nova. Different implementations can be plugged
according to the Nova configuration.
"""
-from nova.servicegroup.api import API
+from nova.servicegroup import api
+
+API = api.API
diff --git a/nova/servicegroup/api.py b/nova/servicegroup/api.py
index 358b7dcbc..0fb30cdf5 100644
--- a/nova/servicegroup/api.py
+++ b/nova/servicegroup/api.py
@@ -23,7 +23,7 @@ from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova import utils
-from random import choice
+import random
LOG = logging.getLogger(__name__)
@@ -144,4 +144,4 @@ class ServiceGroupDriver(object):
length = len(members)
if length == 0:
return None
- return choice(members)
+ return random.choice(members)
diff --git a/nova/servicegroup/drivers/db.py b/nova/servicegroup/drivers/db.py
index 686ee728b..18b4b74e5 100644
--- a/nova/servicegroup/drivers/db.py
+++ b/nova/servicegroup/drivers/db.py
@@ -16,7 +16,6 @@
from nova import conductor
from nova import context
-from nova import exception
from nova.openstack.common import cfg
from nova.openstack.common import log as logging
from nova.openstack.common import timeutils
diff --git a/nova/tests/api/openstack/compute/contrib/test_availability_zone.py b/nova/tests/api/openstack/compute/contrib/test_availability_zone.py
new file mode 100644
index 000000000..fb9a36ba9
--- /dev/null
+++ b/nova/tests/api/openstack/compute/contrib/test_availability_zone.py
@@ -0,0 +1,247 @@
+# Copyright 2012 IBM
+# 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.
+
+import datetime
+from lxml import etree
+import webob
+
+from nova.api.openstack.compute.contrib import availability_zone
+from nova import availability_zones
+from nova import context
+from nova import db
+from nova.openstack.common import jsonutils
+from nova import servicegroup
+from nova import test
+from nova.tests.api.openstack import fakes
+
+
+def fake_service_get_all(context, disabled=None):
+ def __fake_service(binary, availability_zone,
+ created_at, updated_at, host, disabled):
+ return {'binary': binary,
+ 'availability_zone': availability_zone,
+ 'available_zones': availability_zone,
+ 'created_at': created_at,
+ 'updated_at': updated_at,
+ 'host': host,
+ 'disabled': disabled}
+
+ if disabled:
+ return [__fake_service("nova-compute", "zone-2",
+ datetime.datetime(2012, 11, 14, 9, 53, 25, 0),
+ datetime.datetime(2012, 12, 26, 14, 45, 25, 0),
+ "fake_host-1", True),
+ __fake_service("nova-scheduler", "internal",
+ datetime.datetime(2012, 11, 14, 9, 57, 3, 0),
+ datetime.datetime(2012, 12, 26, 14, 45, 25, 0),
+ "fake_host-1", True),
+ __fake_service("nova-network", "internal",
+ datetime.datetime(2012, 11, 16, 7, 25, 46, 0),
+ datetime.datetime(2012, 12, 26, 14, 45, 24, 0),
+ "fake_host-2", True)]
+ else:
+ return [__fake_service("nova-compute", "zone-1",
+ datetime.datetime(2012, 11, 14, 9, 53, 25, 0),
+ datetime.datetime(2012, 12, 26, 14, 45, 25, 0),
+ "fake_host-1", False),
+ __fake_service("nova-sched", "internal",
+ datetime.datetime(2012, 11, 14, 9, 57, 03, 0),
+ datetime.datetime(2012, 12, 26, 14, 45, 25, 0),
+ "fake_host-1", False),
+ __fake_service("nova-network", "internal",
+ datetime.datetime(2012, 11, 16, 7, 25, 46, 0),
+ datetime.datetime(2012, 12, 26, 14, 45, 24, 0),
+ "fake_host-2", False)]
+
+
+def fake_service_is_up(self, service):
+ return service['binary'] != u"nova-network"
+
+
+def fake_set_availability_zones(context, services):
+ return services
+
+
+class AvailabilityZoneApiTest(test.TestCase):
+ def setUp(self):
+ super(AvailabilityZoneApiTest, self).setUp()
+ self.stubs.Set(db, 'service_get_all', fake_service_get_all)
+ self.stubs.Set(availability_zones, 'set_availability_zones',
+ fake_set_availability_zones)
+ self.stubs.Set(servicegroup.API, 'service_is_up', fake_service_is_up)
+
+ def test_availability_zone_index(self):
+ req = webob.Request.blank('/v2/fake/os-availability-zone')
+ resp = req.get_response(fakes.wsgi_app())
+ self.assertEqual(resp.status_int, 200)
+ resp_dict = jsonutils.loads(resp.body)
+
+ self.assertTrue('availabilityZoneInfo' in resp_dict)
+ zones = resp_dict['availabilityZoneInfo']
+ self.assertEqual(len(zones), 2)
+ self.assertEqual(zones[0]['zoneName'], u'zone-1')
+ self.assertTrue(zones[0]['zoneState']['available'])
+ self.assertIsNone(zones[0]['hosts'])
+ self.assertEqual(zones[1]['zoneName'], u'zone-2')
+ self.assertFalse(zones[1]['zoneState']['available'])
+ self.assertIsNone(zones[1]['hosts'])
+
+ def test_availability_zone_detail(self):
+ def _formatZone(zone_dict):
+ result = []
+
+ # Zone tree view item
+ result.append({'zoneName': zone_dict['zoneName'],
+ 'zoneState': u'available'
+ if zone_dict['zoneState']['available'] else
+ u'not available'})
+
+ if zone_dict['hosts'] is not None:
+ for (host, services) in zone_dict['hosts'].items():
+ # Host tree view item
+ result.append({'zoneName': u'|- %s' % host,
+ 'zoneState': u''})
+ for (svc, state) in services.items():
+ # Service tree view item
+ result.append({'zoneName': u'| |- %s' % svc,
+ 'zoneState': u'%s %s %s' % (
+ 'enabled' if state['active'] else
+ 'disabled',
+ ':-)' if state['available'] else
+ 'XXX',
+ jsonutils.to_primitive(
+ state['updated_at']))})
+ return result
+
+ def _assertZone(zone, name, status):
+ self.assertEqual(zone['zoneName'], name)
+ self.assertEqual(zone['zoneState'], status)
+
+ availabilityZone = availability_zone.AvailabilityZoneController()
+
+ req = webob.Request.blank('/v2/fake/os-availability-zone/detail')
+ req.method = 'GET'
+ req.environ['nova.context'] = context.get_admin_context()
+ resp_dict = availabilityZone.detail(req)
+
+ self.assertTrue('availabilityZoneInfo' in resp_dict)
+ zones = resp_dict['availabilityZoneInfo']
+ self.assertEqual(len(zones), 3)
+
+ ''' availabilityZoneInfo field content in response body:
+ [{'zoneName': 'zone-1',
+ 'zoneState': {'available': True},
+ 'hosts': {'fake_host-1': {
+ 'nova-compute': {'active': True, 'available': True,
+ 'updated_at': datetime(2012, 12, 26, 14, 45, 25)}}}},
+ {'zoneName': 'internal',
+ 'zoneState': {'available': True},
+ 'hosts': {'fake_host-1': {
+ 'nova-sched': {'active': True, 'available': True,
+ 'updated_at': datetime(2012, 12, 26, 14, 45, 25)}},
+ 'fake_host-2': {
+ 'nova-network': {'active': True, 'available': False,
+ 'updated_at': datetime(2012, 12, 26, 14, 45, 24)}}}},
+ {'zoneName': 'zone-2',
+ 'zoneState': {'available': False},
+ 'hosts': None}]
+ '''
+
+ l0 = [u'zone-1', u'available']
+ l1 = [u'|- fake_host-1', u'']
+ l2 = [u'| |- nova-compute', u'enabled :-) 2012-12-26T14:45:25.000000']
+ l3 = [u'internal', u'available']
+ l4 = [u'|- fake_host-1', u'']
+ l5 = [u'| |- nova-sched', u'enabled :-) 2012-12-26T14:45:25.000000']
+ l6 = [u'|- fake_host-2', u'']
+ l7 = [u'| |- nova-network', u'enabled XXX 2012-12-26T14:45:24.000000']
+ l8 = [u'zone-2', u'not available']
+
+ z0 = _formatZone(zones[0])
+ z1 = _formatZone(zones[1])
+ z2 = _formatZone(zones[2])
+
+ self.assertEqual(len(z0), 3)
+ self.assertEqual(len(z1), 5)
+ self.assertEqual(len(z2), 1)
+
+ _assertZone(z0[0], l0[0], l0[1])
+ _assertZone(z0[1], l1[0], l1[1])
+ _assertZone(z0[2], l2[0], l2[1])
+ _assertZone(z1[0], l3[0], l3[1])
+ _assertZone(z1[1], l4[0], l4[1])
+ _assertZone(z1[2], l5[0], l5[1])
+ _assertZone(z1[3], l6[0], l6[1])
+ _assertZone(z1[4], l7[0], l7[1])
+ _assertZone(z2[0], l8[0], l8[1])
+
+
+class AvailabilityZoneSerializerTest(test.TestCase):
+ def test_availability_zone_index_detail_serializer(self):
+ def _verify_zone(zone_dict, tree):
+ self.assertEqual(tree.tag, 'availabilityZone')
+ self.assertEqual(zone_dict['zoneName'], tree.get('name'))
+ self.assertEqual(str(zone_dict['zoneState']['available']),
+ tree[0].get('available'))
+
+ for _idx, host_child in enumerate(tree[1]):
+ self.assertTrue(host_child.get('name') in zone_dict['hosts'])
+ svcs = zone_dict['hosts'][host_child.get('name')]
+ for _idx, svc_child in enumerate(host_child[0]):
+ self.assertTrue(svc_child.get('name') in svcs)
+ svc = svcs[svc_child.get('name')]
+ self.assertEqual(len(svc_child), 1)
+
+ self.assertEqual(str(svc['available']),
+ svc_child[0].get('available'))
+ self.assertEqual(str(svc['active']),
+ svc_child[0].get('active'))
+ self.assertEqual(str(svc['updated_at']),
+ svc_child[0].get('updated_at'))
+
+ serializer = availability_zone.AvailabilityZonesTemplate()
+ raw_availability_zones = \
+ [{'zoneName': 'zone-1',
+ 'zoneState': {'available': True},
+ 'hosts': {'fake_host-1': {
+ 'nova-compute': {'active': True, 'available': True,
+ 'updated_at':
+ datetime.datetime(
+ 2012, 12, 26, 14, 45, 25)}}}},
+ {'zoneName': 'internal',
+ 'zoneState': {'available': True},
+ 'hosts': {'fake_host-1': {
+ 'nova-sched': {'active': True, 'available': True,
+ 'updated_at':
+ datetime.datetime(
+ 2012, 12, 26, 14, 45, 25)}},
+ 'fake_host-2': {
+ 'nova-network': {'active': True,
+ 'available': False,
+ 'updated_at':
+ datetime.datetime(
+ 2012, 12, 26, 14, 45, 24)}}}},
+ {'zoneName': 'zone-2',
+ 'zoneState': {'available': False},
+ 'hosts': None}]
+
+ text = serializer.serialize(
+ dict(availabilityZoneInfo=raw_availability_zones))
+ tree = etree.fromstring(text)
+
+ self.assertEqual('availabilityZones', tree.tag)
+ self.assertEqual(len(raw_availability_zones), len(tree))
+ for idx, child in enumerate(tree):
+ _verify_zone(raw_availability_zones[idx], child)
diff --git a/nova/tests/api/openstack/compute/contrib/test_baremetal_nodes.py b/nova/tests/api/openstack/compute/contrib/test_baremetal_nodes.py
new file mode 100644
index 000000000..381d452a7
--- /dev/null
+++ b/nova/tests/api/openstack/compute/contrib/test_baremetal_nodes.py
@@ -0,0 +1,197 @@
+# Copyright (c) 2013 NTT DOCOMO, INC.
+# 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.
+
+from webob import exc
+
+from nova.api.openstack.compute.contrib import baremetal_nodes
+from nova import context
+from nova import exception
+from nova import test
+from nova.virt.baremetal import db
+
+
+class FakeRequest(object):
+
+ def __init__(self, context):
+ self.environ = {"nova.context": context}
+
+
+class BareMetalNodesTest(test.TestCase):
+
+ def setUp(self):
+ super(BareMetalNodesTest, self).setUp()
+
+ self.context = context.get_admin_context()
+ self.controller = baremetal_nodes.BareMetalNodeController()
+ self.request = FakeRequest(self.context)
+
+ def test_create(self):
+ node = {
+ 'service_host': "host",
+ 'cpus': 8,
+ 'memory_mb': 8192,
+ 'local_gb': 128,
+ 'pm_address': "10.1.2.3",
+ 'pm_user': "pm_user",
+ 'pm_password': "pm_pass",
+ 'prov_mac_address': "12:34:56:78:90:ab",
+ 'prov_vlan_id': 1234,
+ 'terminal_port': 8000,
+ 'interfaces': [],
+ }
+ response = node.copy()
+ response['id'] = 100
+ del response['pm_password']
+ response['instance_uuid'] = None
+ self.mox.StubOutWithMock(db, 'bm_node_create')
+ db.bm_node_create(self.context, node).AndReturn(response)
+ self.mox.ReplayAll()
+ res_dict = self.controller.create(self.request, {'node': node})
+ self.assertEqual({'node': response}, res_dict)
+
+ def test_delete(self):
+ self.mox.StubOutWithMock(db, 'bm_node_destroy')
+ db.bm_node_destroy(self.context, 1)
+ self.mox.ReplayAll()
+ self.controller.delete(self.request, 1)
+
+ def test_index(self):
+ nodes = [{'id': 1},
+ {'id': 2},
+ ]
+ interfaces = [{'id': 1, 'address': '11:11:11:11:11:11'},
+ {'id': 2, 'address': '22:22:22:22:22:22'},
+ ]
+ self.mox.StubOutWithMock(db, 'bm_node_get_all')
+ self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
+ db.bm_node_get_all(self.context).AndReturn(nodes)
+ db.bm_interface_get_all_by_bm_node_id(self.context, 1).\
+ AndRaise(exception.InstanceNotFound(instance_id=1))
+ db.bm_interface_get_all_by_bm_node_id(self.context, 2).\
+ AndReturn(interfaces)
+ self.mox.ReplayAll()
+ res_dict = self.controller.index(self.request)
+ self.assertEqual(2, len(res_dict['nodes']))
+ self.assertEqual([], res_dict['nodes'][0]['interfaces'])
+ self.assertEqual(2, len(res_dict['nodes'][1]['interfaces']))
+
+ def test_show(self):
+ node_id = 1
+ node = {'id': node_id}
+ interfaces = [{'id': 1, 'address': '11:11:11:11:11:11'},
+ {'id': 2, 'address': '22:22:22:22:22:22'},
+ ]
+ self.mox.StubOutWithMock(db, 'bm_node_get')
+ self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
+ db.bm_node_get(self.context, node_id).AndReturn(node)
+ db.bm_interface_get_all_by_bm_node_id(self.context, node_id).\
+ AndReturn(interfaces)
+ self.mox.ReplayAll()
+ res_dict = self.controller.show(self.request, node_id)
+ self.assertEqual(node_id, res_dict['node']['id'])
+ self.assertEqual(2, len(res_dict['node']['interfaces']))
+
+ def test_add_interface(self):
+ node_id = 1
+ address = '11:22:33:44:55:66'
+ body = {'add_interface': {'address': address}}
+ self.mox.StubOutWithMock(db, 'bm_node_get')
+ self.mox.StubOutWithMock(db, 'bm_interface_create')
+ self.mox.StubOutWithMock(db, 'bm_interface_get')
+ db.bm_node_get(self.context, node_id)
+ db.bm_interface_create(self.context,
+ bm_node_id=node_id,
+ address=address,
+ datapath_id=None,
+ port_no=None).\
+ AndReturn(12345)
+ db.bm_interface_get(self.context, 12345).\
+ AndReturn({'id': 12345, 'address': address})
+ self.mox.ReplayAll()
+ res_dict = self.controller._add_interface(self.request, node_id, body)
+ self.assertEqual(12345, res_dict['interface']['id'])
+ self.assertEqual(address, res_dict['interface']['address'])
+
+ def test_remove_interface(self):
+ node_id = 1
+ interfaces = [{'id': 1},
+ {'id': 2},
+ {'id': 3},
+ ]
+ body = {'remove_interface': {'id': 2}}
+ self.mox.StubOutWithMock(db, 'bm_node_get')
+ self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
+ self.mox.StubOutWithMock(db, 'bm_interface_destroy')
+ db.bm_node_get(self.context, node_id)
+ db.bm_interface_get_all_by_bm_node_id(self.context, node_id).\
+ AndReturn(interfaces)
+ db.bm_interface_destroy(self.context, 2)
+ self.mox.ReplayAll()
+ self.controller._remove_interface(self.request, node_id, body)
+
+ def test_remove_interface_by_address(self):
+ node_id = 1
+ interfaces = [{'id': 1, 'address': '11:11:11:11:11:11'},
+ {'id': 2, 'address': '22:22:22:22:22:22'},
+ {'id': 3, 'address': '33:33:33:33:33:33'},
+ ]
+ self.mox.StubOutWithMock(db, 'bm_node_get')
+ self.mox.StubOutWithMock(db, 'bm_interface_get_all_by_bm_node_id')
+ self.mox.StubOutWithMock(db, 'bm_interface_destroy')
+ db.bm_node_get(self.context, node_id)
+ db.bm_interface_get_all_by_bm_node_id(self.context, node_id).\
+ AndReturn(interfaces)
+ db.bm_interface_destroy(self.context, 2)
+ self.mox.ReplayAll()
+ body = {'remove_interface': {'address': '22:22:22:22:22:22'}}
+ self.controller._remove_interface(self.request, node_id, body)
+
+ def test_remove_interface_no_id_no_address(self):
+ node_id = 1
+ self.mox.StubOutWithMock(db, 'bm_node_get')
+ db.bm_node_get(self.context, node_id)
+ self.mox.ReplayAll()
+ body = {'remove_interface': {}}
+ self.assertRaises(exc.HTTPBadRequest,
+ self.controller._remove_interface,
+ self.request,
+ node_id,
+ body)
+
+ def test_add_interface_node_not_found(self):
+ node_id = 1
+ self.mox.StubOutWithMock(db, 'bm_node_get')
+ db.bm_node_get(self.context, node_id).\
+ AndRaise(exception.InstanceNotFound(instance_id=node_id))
+ self.mox.ReplayAll()
+ body = {'add_interface': {'address': '11:11:11:11:11:11'}}
+ self.assertRaises(exc.HTTPNotFound,
+ self.controller._add_interface,
+ self.request,
+ node_id,
+ body)
+
+ def test_remove_interface_node_not_found(self):
+ node_id = 1
+ self.mox.StubOutWithMock(db, 'bm_node_get')
+ db.bm_node_get(self.context, node_id).\
+ AndRaise(exception.InstanceNotFound(instance_id=node_id))
+ self.mox.ReplayAll()
+ body = {'remove_interface': {'address': '11:11:11:11:11:11'}}
+ self.assertRaises(exc.HTTPNotFound,
+ self.controller._remove_interface,
+ self.request,
+ node_id,
+ body)
diff --git a/nova/tests/api/openstack/compute/contrib/test_coverage_ext.py b/nova/tests/api/openstack/compute/contrib/test_coverage_ext.py
index 39a883049..66a8a8f82 100644
--- a/nova/tests/api/openstack/compute/contrib/test_coverage_ext.py
+++ b/nova/tests/api/openstack/compute/contrib/test_coverage_ext.py
@@ -16,7 +16,7 @@
import telnetlib
-from coverage import coverage
+import coverage
import webob
from nova.api.openstack.compute.contrib import coverage_ext
@@ -48,8 +48,8 @@ class CoverageExtensionTest(test.TestCase):
super(CoverageExtensionTest, self).setUp()
self.stubs.Set(telnetlib.Telnet, 'write', fake_telnet)
self.stubs.Set(telnetlib.Telnet, 'expect', fake_telnet)
- self.stubs.Set(coverage, 'report', fake_report)
- self.stubs.Set(coverage, 'xml_report', fake_xml_report)
+ self.stubs.Set(coverage.coverage, 'report', fake_report)
+ self.stubs.Set(coverage.coverage, 'xml_report', fake_xml_report)
self.admin_context = context.RequestContext('fakeadmin_0',
'fake',
is_admin=True)
diff --git a/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py b/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py
index bc9f66eb2..a3745d573 100644
--- a/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py
+++ b/nova/tests/api/openstack/compute/contrib/test_flavors_extra_specs.py
@@ -18,10 +18,9 @@
import webob
from nova.api.openstack.compute.contrib import flavorextraspecs
-from nova.api.openstack import wsgi
+import nova.db
from nova import test
from nova.tests.api.openstack import fakes
-import nova.wsgi
def return_create_flavor_extra_specs(context, flavor_id, extra_specs):
@@ -172,13 +171,11 @@ class FlavorsExtraSpecsXMLSerializerTest(test.TestCase):
expected = ("<?xml version='1.0' encoding='UTF-8'?>\n"
'<extra_specs><key1>value1</key1></extra_specs>')
text = serializer.serialize(dict(extra_specs={"key1": "value1"}))
- print text
self.assertEqual(text, expected)
- def test_deserializer(self):
- deserializer = wsgi.XMLDeserializer()
- expected = dict(extra_specs={"key1": "value1"})
- intext = ("<?xml version='1.0' encoding='UTF-8'?>\n"
- '<extra_specs><key1>value1</key1></extra_specs>')
- result = deserializer.deserialize(intext)['body']
- self.assertEqual(result, expected)
+ def test_show_update_serializer(self):
+ serializer = flavorextraspecs.ExtraSpecTemplate()
+ expected = ("<?xml version='1.0' encoding='UTF-8'?>\n"
+ '<extra_spec key="key1">value1</extra_spec>')
+ text = serializer.serialize(dict({"key1": "value1"}))
+ self.assertEqual(text, expected)
diff --git a/nova/tests/api/openstack/compute/contrib/test_quota_classes.py b/nova/tests/api/openstack/compute/contrib/test_quota_classes.py
index a72f5bf0f..0c1378a67 100644
--- a/nova/tests/api/openstack/compute/contrib/test_quota_classes.py
+++ b/nova/tests/api/openstack/compute/contrib/test_quota_classes.py
@@ -138,7 +138,6 @@ class QuotaTemplateXMLSerializerTest(test.TestCase):
cores=90))
text = self.serializer.serialize(exemplar)
- print text
tree = etree.fromstring(text)
self.assertEqual('quota_class_set', tree.tag)
diff --git a/nova/tests/api/openstack/compute/contrib/test_quotas.py b/nova/tests/api/openstack/compute/contrib/test_quotas.py
index dab8c136e..8d518b815 100644
--- a/nova/tests/api/openstack/compute/contrib/test_quotas.py
+++ b/nova/tests/api/openstack/compute/contrib/test_quotas.py
@@ -166,7 +166,6 @@ class QuotaXMLSerializerTest(test.TestCase):
cores=90))
text = self.serializer.serialize(exemplar)
- print text
tree = etree.fromstring(text)
self.assertEqual('quota_set', tree.tag)
diff --git a/nova/tests/api/openstack/compute/contrib/test_security_groups.py b/nova/tests/api/openstack/compute/contrib/test_security_groups.py
index ccb58f858..231923e6d 100644
--- a/nova/tests/api/openstack/compute/contrib/test_security_groups.py
+++ b/nova/tests/api/openstack/compute/contrib/test_security_groups.py
@@ -1180,7 +1180,6 @@ class TestSecurityGroupXMLSerializer(test.TestCase):
rule = dict(security_group_rule=raw_rule)
text = self.rule_serializer.serialize(rule)
- print text
tree = etree.fromstring(text)
self.assertEqual('security_group_rule', self._tag(tree))
@@ -1212,7 +1211,6 @@ class TestSecurityGroupXMLSerializer(test.TestCase):
sg_group = dict(security_group=raw_group)
text = self.default_serializer.serialize(sg_group)
- print text
tree = etree.fromstring(text)
self._verify_security_group(raw_group, tree)
@@ -1265,7 +1263,6 @@ class TestSecurityGroupXMLSerializer(test.TestCase):
sg_groups = dict(security_groups=groups)
text = self.index_serializer.serialize(sg_groups)
- print text
tree = etree.fromstring(text)
self.assertEqual('security_groups', self._tag(tree))
diff --git a/nova/tests/api/openstack/compute/contrib/test_server_diagnostics.py b/nova/tests/api/openstack/compute/contrib/test_server_diagnostics.py
index ea4565e14..783275ea2 100644
--- a/nova/tests/api/openstack/compute/contrib/test_server_diagnostics.py
+++ b/nova/tests/api/openstack/compute/contrib/test_server_diagnostics.py
@@ -74,7 +74,6 @@ class TestServerDiagnosticsXMLSerializer(test.TestCase):
exemplar = dict(diag1='foo', diag2='bar')
text = serializer.serialize(exemplar)
- print text
tree = etree.fromstring(text)
self.assertEqual('diagnostics', self._tag(tree))
diff --git a/nova/tests/api/openstack/compute/contrib/test_services.py b/nova/tests/api/openstack/compute/contrib/test_services.py
index 3a6e5db7c..aba1b92c1 100644
--- a/nova/tests/api/openstack/compute/contrib/test_services.py
+++ b/nova/tests/api/openstack/compute/contrib/test_services.py
@@ -14,7 +14,8 @@
# under the License.
-from datetime import datetime
+import datetime
+
from nova.api.openstack.compute.contrib import services
from nova import context
from nova import db
@@ -24,35 +25,36 @@ from nova import test
from nova.tests.api.openstack import fakes
-fake_services_list = [{'binary': 'nova-scheduler',
- 'host': 'host1',
- 'id': 1,
- 'disabled': True,
- 'topic': 'scheduler',
- 'updated_at': datetime(2012, 10, 29, 13, 42, 2),
- 'created_at': datetime(2012, 9, 18, 2, 46, 27)},
- {'binary': 'nova-compute',
- 'host': 'host1',
- 'id': 2,
- 'disabled': True,
- 'topic': 'compute',
- 'updated_at': datetime(2012, 10, 29, 13, 42, 5),
- 'created_at': datetime(2012, 9, 18, 2, 46, 27)},
- {'binary': 'nova-scheduler',
- 'host': 'host2',
- 'id': 3,
- 'disabled': False,
- 'topic': 'scheduler',
- 'updated_at': datetime(2012, 9, 19, 6, 55, 34),
- 'created_at': datetime(2012, 9, 18, 2, 46, 28)},
- {'binary': 'nova-compute',
- 'host': 'host2',
- 'id': 4,
- 'disabled': True,
- 'topic': 'compute',
- 'updated_at': datetime(2012, 9, 18, 8, 3, 38),
- 'created_at': datetime(2012, 9, 18, 2, 46, 28)},
- ]
+fake_services_list = [
+ {'binary': 'nova-scheduler',
+ 'host': 'host1',
+ 'id': 1,
+ 'disabled': True,
+ 'topic': 'scheduler',
+ 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2),
+ 'created_at': datetime.datetime(2012, 9, 18, 2, 46, 27)},
+ {'binary': 'nova-compute',
+ 'host': 'host1',
+ 'id': 2,
+ 'disabled': True,
+ 'topic': 'compute',
+ 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5),
+ 'created_at': datetime.datetime(2012, 9, 18, 2, 46, 27)},
+ {'binary': 'nova-scheduler',
+ 'host': 'host2',
+ 'id': 3,
+ 'disabled': False,
+ 'topic': 'scheduler',
+ 'updated_at': datetime.datetime(2012, 9, 19, 6, 55, 34),
+ 'created_at': datetime.datetime(2012, 9, 18, 2, 46, 28)},
+ {'binary': 'nova-compute',
+ 'host': 'host2',
+ 'id': 4,
+ 'disabled': True,
+ 'topic': 'compute',
+ 'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38),
+ 'created_at': datetime.datetime(2012, 9, 18, 2, 46, 28)},
+ ]
class FakeRequest(object):
@@ -103,7 +105,7 @@ def fake_service_update(context, service_id, values):
def fake_utcnow():
- return datetime(2012, 10, 29, 13, 42, 11)
+ return datetime.datetime(2012, 10, 29, 13, 42, 11)
class ServicesTest(test.TestCase):
@@ -130,19 +132,19 @@ class ServicesTest(test.TestCase):
response = {'services': [{'binary': 'nova-scheduler',
'host': 'host1', 'zone': 'internal',
'status': 'disabled', 'state': 'up',
- 'updated_at': datetime(2012, 10, 29, 13, 42, 2)},
+ 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2)},
{'binary': 'nova-compute',
'host': 'host1', 'zone': 'nova',
'status': 'disabled', 'state': 'up',
- 'updated_at': datetime(2012, 10, 29, 13, 42, 5)},
+ 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5)},
{'binary': 'nova-scheduler', 'host': 'host2',
'zone': 'internal',
'status': 'enabled', 'state': 'down',
- 'updated_at': datetime(2012, 9, 19, 6, 55, 34)},
+ 'updated_at': datetime.datetime(2012, 9, 19, 6, 55, 34)},
{'binary': 'nova-compute', 'host': 'host2',
'zone': 'nova',
'status': 'disabled', 'state': 'down',
- 'updated_at': datetime(2012, 9, 18, 8, 3, 38)}]}
+ 'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38)}]}
self.assertEqual(res_dict, response)
def test_services_list_with_host(self):
@@ -152,11 +154,11 @@ class ServicesTest(test.TestCase):
response = {'services': [{'binary': 'nova-scheduler', 'host': 'host1',
'zone': 'internal',
'status': 'disabled', 'state': 'up',
- 'updated_at': datetime(2012, 10, 29, 13, 42, 2)},
+ 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2)},
{'binary': 'nova-compute', 'host': 'host1',
'zone': 'nova',
'status': 'disabled', 'state': 'up',
- 'updated_at': datetime(2012, 10, 29, 13, 42, 5)}]}
+ 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5)}]}
self.assertEqual(res_dict, response)
def test_services_list_with_service(self):
@@ -166,11 +168,11 @@ class ServicesTest(test.TestCase):
response = {'services': [{'binary': 'nova-compute', 'host': 'host1',
'zone': 'nova',
'status': 'disabled', 'state': 'up',
- 'updated_at': datetime(2012, 10, 29, 13, 42, 5)},
+ 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5)},
{'binary': 'nova-compute', 'host': 'host2',
'zone': 'nova',
'status': 'disabled', 'state': 'down',
- 'updated_at': datetime(2012, 9, 18, 8, 3, 38)}]}
+ 'updated_at': datetime.datetime(2012, 9, 18, 8, 3, 38)}]}
self.assertEqual(res_dict, response)
def test_services_list_with_host_service(self):
@@ -180,7 +182,7 @@ class ServicesTest(test.TestCase):
response = {'services': [{'binary': 'nova-compute', 'host': 'host1',
'zone': 'nova',
'status': 'disabled', 'state': 'up',
- 'updated_at': datetime(2012, 10, 29, 13, 42, 5)}]}
+ 'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 5)}]}
self.assertEqual(res_dict, response)
def test_services_enable(self):
diff --git a/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py b/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py
index b49a1feb4..13a4e9d61 100644
--- a/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py
+++ b/nova/tests/api/openstack/compute/contrib/test_simple_tenant_usage.py
@@ -293,7 +293,6 @@ class SimpleTenantUsageSerializerTest(test.TestCase):
tenant_usage = dict(tenant_usage=raw_usage)
text = serializer.serialize(tenant_usage)
- print text
tree = etree.fromstring(text)
self._verify_tenant_usage(raw_usage, tree)
@@ -378,7 +377,6 @@ class SimpleTenantUsageSerializerTest(test.TestCase):
tenant_usages = dict(tenant_usages=raw_usages)
text = serializer.serialize(tenant_usages)
- print text
tree = etree.fromstring(text)
self.assertEqual('tenant_usages', tree.tag)
diff --git a/nova/tests/api/openstack/compute/contrib/test_snapshots.py b/nova/tests/api/openstack/compute/contrib/test_snapshots.py
index a223178fb..fa0c521fe 100644
--- a/nova/tests/api/openstack/compute/contrib/test_snapshots.py
+++ b/nova/tests/api/openstack/compute/contrib/test_snapshots.py
@@ -271,7 +271,6 @@ class SnapshotSerializerTest(test.TestCase):
)
text = serializer.serialize(dict(snapshot=raw_snapshot))
- print text
tree = etree.fromstring(text)
self._verify_snapshot(raw_snapshot, tree)
@@ -298,7 +297,6 @@ class SnapshotSerializerTest(test.TestCase):
)]
text = serializer.serialize(dict(snapshots=raw_snapshots))
- print text
tree = etree.fromstring(text)
self.assertEqual('snapshots', tree.tag)
diff --git a/nova/tests/api/openstack/compute/contrib/test_virtual_interfaces.py b/nova/tests/api/openstack/compute/contrib/test_virtual_interfaces.py
index 7c61cd51b..cf1c1593f 100644
--- a/nova/tests/api/openstack/compute/contrib/test_virtual_interfaces.py
+++ b/nova/tests/api/openstack/compute/contrib/test_virtual_interfaces.py
@@ -91,7 +91,6 @@ class ServerVirtualInterfaceSerializerTest(test.TestCase):
vifs = dict(virtual_interfaces=raw_vifs)
text = self.serializer.serialize(vifs)
- print text
tree = etree.fromstring(text)
self.assertEqual('virtual_interfaces', self._tag(tree))
diff --git a/nova/tests/api/openstack/compute/contrib/test_volumes.py b/nova/tests/api/openstack/compute/contrib/test_volumes.py
index 3119f55e8..1a8a570e8 100644
--- a/nova/tests/api/openstack/compute/contrib/test_volumes.py
+++ b/nova/tests/api/openstack/compute/contrib/test_volumes.py
@@ -348,7 +348,6 @@ class VolumeSerializerTest(test.TestCase):
device='/foo')
text = serializer.serialize(dict(volumeAttachment=raw_attach))
- print text
tree = etree.fromstring(text)
self.assertEqual('volumeAttachment', tree.tag)
@@ -368,7 +367,6 @@ class VolumeSerializerTest(test.TestCase):
device='/foo2')]
text = serializer.serialize(dict(volumeAttachments=raw_attaches))
- print text
tree = etree.fromstring(text)
self.assertEqual('volumeAttachments', tree.tag)
@@ -401,7 +399,6 @@ class VolumeSerializerTest(test.TestCase):
)
text = serializer.serialize(dict(volume=raw_volume))
- print text
tree = etree.fromstring(text)
self._verify_volume(raw_volume, tree)
@@ -450,7 +447,6 @@ class VolumeSerializerTest(test.TestCase):
)]
text = serializer.serialize(dict(volumes=raw_volumes))
- print text
tree = etree.fromstring(text)
self.assertEqual('volumes', tree.tag)
diff --git a/nova/tests/api/openstack/compute/test_limits.py b/nova/tests/api/openstack/compute/test_limits.py
index 375355a70..e3fff380d 100644
--- a/nova/tests/api/openstack/compute/test_limits.py
+++ b/nova/tests/api/openstack/compute/test_limits.py
@@ -874,7 +874,6 @@ class LimitsXMLSerializationTest(test.TestCase):
"absolute": {}}}
output = serializer.serialize(fixture)
- print output
has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>")
self.assertTrue(has_dec)
@@ -905,7 +904,6 @@ class LimitsXMLSerializationTest(test.TestCase):
"maxPersonalitySize": 10240}}}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'limits')
@@ -940,7 +938,6 @@ class LimitsXMLSerializationTest(test.TestCase):
"absolute": {}}}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'limits')
diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py
index af769a6ca..4bfb1c1e3 100644
--- a/nova/tests/api/openstack/compute/test_servers.py
+++ b/nova/tests/api/openstack/compute/test_servers.py
@@ -2008,6 +2008,55 @@ class ServersControllerCreateTest(test.TestCase):
self.assertNotEqual(reservation_id, None)
self.assertTrue(len(reservation_id) > 1)
+ def test_create_multiple_instances_with_multiple_volume_bdm(self):
+ """
+ Test that a BadRequest is raised if multiple instances
+ are requested with a list of block device mappings for volumes.
+ """
+ self.ext_mgr.extensions = {'os-multiple-create': 'fake'}
+ min_count = 2
+ bdm = [{'device_name': 'foo1', 'volume_id': 'vol-xxxx'},
+ {'device_name': 'foo2', 'volume_id': 'vol-yyyy'}
+ ]
+ params = {
+ 'block_device_mapping': bdm,
+ 'min_count': min_count
+ }
+ old_create = compute_api.API.create
+
+ def create(*args, **kwargs):
+ self.assertEqual(kwargs['min_count'], 2)
+ self.assertEqual(len(kwargs['block_device_mapping']), 2)
+ return old_create(*args, **kwargs)
+
+ self.stubs.Set(compute_api.API, 'create', create)
+ self.assertRaises(webob.exc.HTTPBadRequest,
+ self._test_create_extra, params, no_image=True)
+
+ def test_create_multiple_instances_with_single_volume_bdm(self):
+ """
+ Test that a BadRequest is raised if multiple instances
+ are requested to boot from a single volume.
+ """
+ self.ext_mgr.extensions = {'os-multiple-create': 'fake'}
+ min_count = 2
+ bdm = [{'device_name': 'foo1', 'volume_id': 'vol-xxxx'}]
+ params = {
+ 'block_device_mapping': bdm,
+ 'min_count': min_count
+ }
+ old_create = compute_api.API.create
+
+ def create(*args, **kwargs):
+ self.assertEqual(kwargs['min_count'], 2)
+ self.assertEqual(kwargs['block_device_mapping']['volume_id'],
+ 'vol-xxxx')
+ return old_create(*args, **kwargs)
+
+ self.stubs.Set(compute_api.API, 'create', create)
+ self.assertRaises(webob.exc.HTTPBadRequest,
+ self._test_create_extra, params, no_image=True)
+
def test_create_instance_image_ref_is_bookmark(self):
image_uuid = '76fa36fc-c930-4bf3-8c8a-ea2a2420deb6'
image_href = 'http://localhost/fake/images/%s' % image_uuid
@@ -4459,7 +4508,6 @@ class ServerXMLSerializationTest(test.TestCase):
}
output = serializer.serialize(fixture)
- print output
has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>")
self.assertTrue(has_dec)
@@ -4537,7 +4585,6 @@ class ServerXMLSerializationTest(test.TestCase):
}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'server')
@@ -4668,7 +4715,6 @@ class ServerXMLSerializationTest(test.TestCase):
}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'server')
@@ -4765,7 +4811,6 @@ class ServerXMLSerializationTest(test.TestCase):
]}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'servers_index')
server_elems = root.findall('{0}server'.format(NS))
@@ -4829,7 +4874,6 @@ class ServerXMLSerializationTest(test.TestCase):
]}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'servers_index')
server_elems = root.findall('{0}server'.format(NS))
@@ -5116,7 +5160,6 @@ class ServerXMLSerializationTest(test.TestCase):
}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'server')
diff --git a/nova/tests/api/openstack/compute/test_versions.py b/nova/tests/api/openstack/compute/test_versions.py
index 28b109215..bd2e9fa7b 100644
--- a/nova/tests/api/openstack/compute/test_versions.py
+++ b/nova/tests/api/openstack/compute/test_versions.py
@@ -228,7 +228,6 @@ class VersionsTest(test.TestCase):
self.assertEqual(res.content_type, "application/xml")
root = etree.XML(res.body)
- print res.body
xmlutil.validate_schema(root, 'versions')
self.assertTrue(root.xpath('/ns:versions', namespaces=NS))
diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py
index 7e49e4ab8..68a5f0bf4 100644
--- a/nova/tests/api/openstack/test_common.py
+++ b/nova/tests/api/openstack/test_common.py
@@ -377,7 +377,6 @@ class MetadataXMLSerializationTest(test.TestCase):
}
output = serializer.serialize(fixture)
- print output
has_dec = output.startswith("<?xml version='1.0' encoding='UTF-8'?>")
self.assertTrue(has_dec)
@@ -390,7 +389,6 @@ class MetadataXMLSerializationTest(test.TestCase):
},
}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'metadata')
metadata_dict = fixture['metadata']
@@ -409,7 +407,6 @@ class MetadataXMLSerializationTest(test.TestCase):
},
}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'metadata')
metadata_dict = fixture['metadata']
@@ -428,7 +425,6 @@ class MetadataXMLSerializationTest(test.TestCase):
},
}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'metadata')
metadata_dict = fixture['metadata']
@@ -447,7 +443,6 @@ class MetadataXMLSerializationTest(test.TestCase):
},
}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
meta_dict = fixture['meta']
(meta_key, meta_value) = meta_dict.items()[0]
@@ -463,7 +458,6 @@ class MetadataXMLSerializationTest(test.TestCase):
},
}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'metadata')
metadata_dict = fixture['metadata']
@@ -482,7 +476,6 @@ class MetadataXMLSerializationTest(test.TestCase):
},
}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
meta_dict = fixture['meta']
(meta_key, meta_value) = meta_dict.items()[0]
@@ -499,7 +492,6 @@ class MetadataXMLSerializationTest(test.TestCase):
},
}
output = serializer.serialize(fixture)
- print output
root = etree.XML(output)
xmlutil.validate_schema(root, 'metadata')
metadata_dict = fixture['metadata']
diff --git a/nova/tests/baremetal/test_nova_baremetal_manage.py b/nova/tests/baremetal/test_nova_baremetal_manage.py
index 4d152a028..c4fdaac6b 100644
--- a/nova/tests/baremetal/test_nova_baremetal_manage.py
+++ b/nova/tests/baremetal/test_nova_baremetal_manage.py
@@ -20,10 +20,6 @@ import imp
import os
import sys
-from nova import context
-from nova import test
-from nova.virt.baremetal import db as bmdb
-
from nova.tests.baremetal.db import base as bm_db_base
TOPDIR = os.path.normpath(os.path.join(
diff --git a/nova/tests/baremetal/test_pxe.py b/nova/tests/baremetal/test_pxe.py
index 73ef8caa3..09f1079bf 100644
--- a/nova/tests/baremetal/test_pxe.py
+++ b/nova/tests/baremetal/test_pxe.py
@@ -21,15 +21,10 @@
import os
-import mox
-from testtools.matchers import Contains
-from testtools.matchers import MatchesAll
-from testtools.matchers import Not
-from testtools.matchers import StartsWith
+from testtools import matchers
from nova import exception
from nova.openstack.common import cfg
-from nova import test
from nova.tests.baremetal.db import base as bm_db_base
from nova.tests.baremetal.db import utils as bm_db_utils
from nova.tests.image import fake as fake_image
@@ -120,26 +115,26 @@ class PXEClassMethodsTestCase(BareMetalPXETestCase):
'ari_path': 'ggg',
}
config = pxe.build_pxe_config(**args)
- self.assertThat(config, StartsWith('default deploy'))
+ self.assertThat(config, matchers.StartsWith('default deploy'))
# deploy bits are in the deploy section
start = config.index('label deploy')
end = config.index('label boot')
- self.assertThat(config[start:end], MatchesAll(
- Contains('kernel ddd'),
- Contains('initrd=eee'),
- Contains('deployment_id=aaa'),
- Contains('deployment_key=bbb'),
- Contains('iscsi_target_iqn=ccc'),
- Not(Contains('kernel fff')),
+ self.assertThat(config[start:end], matchers.MatchesAll(
+ matchers.Contains('kernel ddd'),
+ matchers.Contains('initrd=eee'),
+ matchers.Contains('deployment_id=aaa'),
+ matchers.Contains('deployment_key=bbb'),
+ matchers.Contains('iscsi_target_iqn=ccc'),
+ matchers.Not(matchers.Contains('kernel fff')),
))
# boot bits are in the boot section
start = config.index('label boot')
- self.assertThat(config[start:], MatchesAll(
- Contains('kernel fff'),
- Contains('initrd=ggg'),
- Not(Contains('kernel ddd')),
+ self.assertThat(config[start:], matchers.MatchesAll(
+ matchers.Contains('kernel fff'),
+ matchers.Contains('initrd=ggg'),
+ matchers.Not(matchers.Contains('kernel ddd')),
))
def test_build_network_config(self):
@@ -414,10 +409,11 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase):
def test_destroy_images(self):
self._create_node()
- self.mox.StubOutWithMock(os, 'unlink')
+ self.mox.StubOutWithMock(bm_utils, 'unlink_without_raise')
+ self.mox.StubOutWithMock(bm_utils, 'rmtree_without_raise')
- os.unlink(pxe.get_image_file_path(self.instance))
- os.unlink(pxe.get_image_dir_path(self.instance))
+ bm_utils.unlink_without_raise(pxe.get_image_file_path(self.instance))
+ bm_utils.rmtree_without_raise(pxe.get_image_dir_path(self.instance))
self.mox.ReplayAll()
self.driver.destroy_images(self.context, self.node, self.instance)
@@ -482,6 +478,7 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase):
pxe_path = pxe.get_pxe_config_file_path(self.instance)
self.mox.StubOutWithMock(bm_utils, 'unlink_without_raise')
+ self.mox.StubOutWithMock(bm_utils, 'rmtree_without_raise')
self.mox.StubOutWithMock(pxe, 'get_tftp_image_info')
self.mox.StubOutWithMock(self.driver, '_collect_mac_addresses')
@@ -493,7 +490,7 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase):
AndReturn(macs)
for mac in macs:
bm_utils.unlink_without_raise(pxe.get_pxe_mac_path(mac))
- bm_utils.unlink_without_raise(
+ bm_utils.rmtree_without_raise(
os.path.join(CONF.baremetal.tftp_root, 'fake-uuid'))
self.mox.ReplayAll()
@@ -516,6 +513,7 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase):
pxe_path = pxe.get_pxe_config_file_path(self.instance)
self.mox.StubOutWithMock(bm_utils, 'unlink_without_raise')
+ self.mox.StubOutWithMock(bm_utils, 'rmtree_without_raise')
self.mox.StubOutWithMock(pxe, 'get_tftp_image_info')
self.mox.StubOutWithMock(self.driver, '_collect_mac_addresses')
@@ -524,7 +522,7 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase):
bm_utils.unlink_without_raise(pxe_path)
self.driver._collect_mac_addresses(self.context, self.node).\
AndRaise(exception.DBError)
- bm_utils.unlink_without_raise(
+ bm_utils.rmtree_without_raise(
os.path.join(CONF.baremetal.tftp_root, 'fake-uuid'))
self.mox.ReplayAll()
diff --git a/nova/tests/baremetal/test_utils.py b/nova/tests/baremetal/test_utils.py
index 827b1fcaf..df5112deb 100644
--- a/nova/tests/baremetal/test_utils.py
+++ b/nova/tests/baremetal/test_utils.py
@@ -18,9 +18,9 @@
"""Tests for baremetal utils."""
-import mox
+import errno
+import os
-from nova import exception
from nova import test
from nova.virt.baremetal import utils
@@ -32,3 +32,36 @@ class BareMetalUtilsTestCase(test.TestCase):
self.assertEqual(len(s), 10)
s = utils.random_alnum(100)
self.assertEqual(len(s), 100)
+
+ def test_unlink(self):
+ self.mox.StubOutWithMock(os, "unlink")
+ os.unlink("/fake/path")
+
+ self.mox.ReplayAll()
+ utils.unlink_without_raise("/fake/path")
+ self.mox.VerifyAll()
+
+ def test_unlink_ENOENT(self):
+ self.mox.StubOutWithMock(os, "unlink")
+ os.unlink("/fake/path").AndRaise(OSError(errno.ENOENT))
+
+ self.mox.ReplayAll()
+ utils.unlink_without_raise("/fake/path")
+ self.mox.VerifyAll()
+
+ def test_create_link(self):
+ self.mox.StubOutWithMock(os, "symlink")
+ os.symlink("/fake/source", "/fake/link")
+
+ self.mox.ReplayAll()
+ utils.create_link_without_raise("/fake/source", "/fake/link")
+ self.mox.VerifyAll()
+
+ def test_create_link_EEXIST(self):
+ self.mox.StubOutWithMock(os, "symlink")
+ os.symlink("/fake/source", "/fake/link").AndRaise(
+ OSError(errno.EEXIST))
+
+ self.mox.ReplayAll()
+ utils.create_link_without_raise("/fake/source", "/fake/link")
+ self.mox.VerifyAll()
diff --git a/nova/tests/cells/test_cells_messaging.py b/nova/tests/cells/test_cells_messaging.py
index da45721ed..1208368c2 100644
--- a/nova/tests/cells/test_cells_messaging.py
+++ b/nova/tests/cells/test_cells_messaging.py
@@ -14,8 +14,6 @@
"""
Tests For Cells Messaging module
"""
-import mox
-
from nova.cells import messaging
from nova.cells import utils as cells_utils
from nova import context
diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py
index b8212848c..6bd2c3cac 100644
--- a/nova/tests/compute/test_compute.py
+++ b/nova/tests/compute/test_compute.py
@@ -58,7 +58,7 @@ import nova.policy
from nova import quota
from nova import test
from nova.tests.compute import fake_resource_tracker
-from nova.tests.db.fakes import FakeModel
+from nova.tests.db import fakes as db_fakes
from nova.tests import fake_network
from nova.tests.image import fake as fake_image
from nova.tests import matchers
@@ -2067,7 +2067,7 @@ class ComputeTestCase(BaseTestCase):
self.compute.terminate_instance(self.context, instance=new_instance)
def test_prep_resize_instance_migration_error_on_none_host(self):
- """Ensure prep_resize raise a migration error if destination host is
+ """Ensure prep_resize raises a migration error if destination host is
not defined
"""
instance = jsonutils.to_primitive(self._create_fake_instance())
@@ -2532,7 +2532,7 @@ class ComputeTestCase(BaseTestCase):
db.instance_destroy(c, inst_uuid)
def test_post_live_migration_no_shared_storage_working_correctly(self):
- """Confirm post_live_migration() works as expected correctly
+ """Confirm post_live_migration() works correctly as expected
for non shared storage migration.
"""
# Create stubs
@@ -2722,8 +2722,11 @@ class ComputeTestCase(BaseTestCase):
self.stubs.Set(nova.db, 'instance_fault_create', fake_db_fault_create)
ctxt = context.get_admin_context()
- compute_utils.add_instance_fault_from_exc(ctxt, instance,
- NotImplementedError('test'), exc_info)
+ compute_utils.add_instance_fault_from_exc(ctxt,
+ self.compute.conductor_api,
+ instance,
+ NotImplementedError('test'),
+ exc_info)
def test_add_instance_fault_with_remote_error(self):
instance = self._create_fake_instance()
@@ -2751,8 +2754,8 @@ class ComputeTestCase(BaseTestCase):
self.stubs.Set(nova.db, 'instance_fault_create', fake_db_fault_create)
ctxt = context.get_admin_context()
- compute_utils.add_instance_fault_from_exc(ctxt, instance, exc,
- exc_info)
+ compute_utils.add_instance_fault_from_exc(ctxt,
+ self.compute.conductor_api, instance, exc, exc_info)
def test_add_instance_fault_user_error(self):
instance = self._create_fake_instance()
@@ -2779,8 +2782,8 @@ class ComputeTestCase(BaseTestCase):
self.stubs.Set(nova.db, 'instance_fault_create', fake_db_fault_create)
ctxt = context.get_admin_context()
- compute_utils.add_instance_fault_from_exc(ctxt, instance, user_exc,
- exc_info)
+ compute_utils.add_instance_fault_from_exc(ctxt,
+ self.compute.conductor_api, instance, user_exc, exc_info)
def test_add_instance_fault_no_exc_info(self):
instance = self._create_fake_instance()
@@ -2798,8 +2801,10 @@ class ComputeTestCase(BaseTestCase):
self.stubs.Set(nova.db, 'instance_fault_create', fake_db_fault_create)
ctxt = context.get_admin_context()
- compute_utils.add_instance_fault_from_exc(ctxt, instance,
- NotImplementedError('test'))
+ compute_utils.add_instance_fault_from_exc(ctxt,
+ self.compute.conductor_api,
+ instance,
+ NotImplementedError('test'))
def test_cleanup_running_deleted_instances(self):
admin_context = context.get_admin_context()
@@ -3426,7 +3431,7 @@ class ComputeTestCase(BaseTestCase):
db.instance_destroy(c, inst_uuid)
def test_rebuild_with_wrong_shared_storage(self):
- """Confirm evacuate scenario updates host."""
+ """Confirm evacuate scenario does not update host."""
# creating testdata
c = self.context.elevated()
@@ -3780,6 +3785,28 @@ class ComputeAPITestCase(BaseTestCase):
finally:
db.instance_destroy(self.context, ref[0]['uuid'])
+ def test_create_saves_type_in_system_metadata(self):
+ instance_type = instance_types.get_default_instance_type()
+ (ref, resv_id) = self.compute_api.create(
+ self.context,
+ instance_type=instance_type,
+ image_href=None)
+ try:
+ sys_metadata = db.instance_system_metadata_get(self.context,
+ ref[0]['uuid'])
+
+ instance_type_props = ['name', 'memory_mb', 'vcpus', 'root_gb',
+ 'ephemeral_gb', 'flavorid', 'swap',
+ 'rxtx_factor', 'vcpu_weight']
+ for key in instance_type_props:
+ sys_meta_key = "instance_type_%s" % key
+ self.assertTrue(sys_meta_key in sys_metadata)
+ self.assertEqual(str(instance_type[key]),
+ str(sys_metadata[sys_meta_key]))
+
+ finally:
+ db.instance_destroy(self.context, ref[0]['uuid'])
+
def test_create_instance_associates_security_groups(self):
# Make sure create associates security groups.
group = self._create_group()
@@ -4688,6 +4715,60 @@ class ComputeAPITestCase(BaseTestCase):
self.assertEqual(properties['d'], 'd')
self.assertFalse('spam' in properties)
+ def _do_test_snapshot_image_service_fails(self, method, image_id):
+ # Ensure task_state remains at None if image service fails.
+ def fake_fails(*args, **kwargs):
+ raise test.TestingException()
+
+ restore = getattr(fake_image._FakeImageService, method)
+ self.stubs.Set(fake_image._FakeImageService, method, fake_fails)
+
+ instance = self._create_fake_instance()
+ self.assertRaises(test.TestingException,
+ self.compute_api.snapshot,
+ self.context,
+ instance,
+ 'no_image_snapshot',
+ image_id=image_id)
+
+ self.stubs.Set(fake_image._FakeImageService, method, restore)
+ db_instance = db.instance_get_all(self.context)[0]
+ self.assertIsNone(db_instance['task_state'])
+
+ def test_snapshot_image_creation_fails(self):
+ self._do_test_snapshot_image_service_fails('create', None)
+
+ def test_snapshot_image_show_fails(self):
+ self._do_test_snapshot_image_service_fails('show', 'image')
+
+ def _do_test_backup_image_service_fails(self, method, image_id):
+ # Ensure task_state remains at None if image service fails.
+ def fake_fails(*args, **kwargs):
+ raise test.TestingException()
+
+ restore = getattr(fake_image._FakeImageService, method)
+ self.stubs.Set(fake_image._FakeImageService, method, fake_fails)
+
+ instance = self._create_fake_instance()
+ self.assertRaises(test.TestingException,
+ self.compute_api.backup,
+ self.context,
+ instance,
+ 'no_image_backup',
+ 'DAILY',
+ 0,
+ image_id=image_id)
+
+ self.stubs.Set(fake_image._FakeImageService, method, restore)
+ db_instance = db.instance_get_all(self.context)[0]
+ self.assertIsNone(db_instance['task_state'])
+
+ def test_backup_image_creation_fails(self):
+ self._do_test_backup_image_service_fails('create', None)
+
+ def test_backup_image_show_fails(self):
+ self._do_test_backup_image_service_fails('show', 'image')
+
def test_backup(self):
# Can't backup an instance which is already being backed up.
instance = self._create_fake_instance()
@@ -5287,7 +5368,7 @@ class ComputeAPITestCase(BaseTestCase):
_context = context.get_admin_context()
instance = self._create_fake_instance({'metadata': {'key1': 'value1'}})
- instance = dict(instance)
+ instance = dict(instance.iteritems())
metadata = self.compute_api.get_instance_metadata(_context, instance)
self.assertEqual(metadata, {'key1': 'value1'})
@@ -5879,11 +5960,11 @@ class ComputeAPITestCase(BaseTestCase):
instance = self._create_fake_instance()
def rule_get(*args, **kwargs):
- mock_rule = FakeModel({'parent_group_id': 1})
+ mock_rule = db_fakes.FakeModel({'parent_group_id': 1})
return [mock_rule]
def group_get(*args, **kwargs):
- mock_group = FakeModel({'instances': [instance]})
+ mock_group = db_fakes.FakeModel({'instances': [instance]})
return mock_group
self.stubs.Set(
@@ -5908,11 +5989,11 @@ class ComputeAPITestCase(BaseTestCase):
instance = self._create_fake_instance()
def rule_get(*args, **kwargs):
- mock_rule = FakeModel({'parent_group_id': 1})
+ mock_rule = db_fakes.FakeModel({'parent_group_id': 1})
return [mock_rule]
def group_get(*args, **kwargs):
- mock_group = FakeModel({'instances': [instance]})
+ mock_group = db_fakes.FakeModel({'instances': [instance]})
return mock_group
self.stubs.Set(
@@ -5935,11 +6016,11 @@ class ComputeAPITestCase(BaseTestCase):
def test_secgroup_refresh_none(self):
def rule_get(*args, **kwargs):
- mock_rule = FakeModel({'parent_group_id': 1})
+ mock_rule = db_fakes.FakeModel({'parent_group_id': 1})
return [mock_rule]
def group_get(*args, **kwargs):
- mock_group = FakeModel({'instances': []})
+ mock_group = db_fakes.FakeModel({'instances': []})
return mock_group
self.stubs.Set(
@@ -5957,7 +6038,7 @@ class ComputeAPITestCase(BaseTestCase):
instance = self._create_fake_instance()
def group_get(*args, **kwargs):
- mock_group = FakeModel({'instances': [instance]})
+ mock_group = db_fakes.FakeModel({'instances': [instance]})
return mock_group
self.stubs.Set(self.compute_api.db, 'security_group_get', group_get)
@@ -5978,7 +6059,7 @@ class ComputeAPITestCase(BaseTestCase):
instance = self._create_fake_instance()
def group_get(*args, **kwargs):
- mock_group = FakeModel({'instances': [instance]})
+ mock_group = db_fakes.FakeModel({'instances': [instance]})
return mock_group
self.stubs.Set(self.compute_api.db, 'security_group_get', group_get)
@@ -5997,7 +6078,7 @@ class ComputeAPITestCase(BaseTestCase):
def test_secrule_refresh_none(self):
def group_get(*args, **kwargs):
- mock_group = FakeModel({'instances': []})
+ mock_group = db_fakes.FakeModel({'instances': []})
return mock_group
self.stubs.Set(self.compute_api.db, 'security_group_get', group_get)
@@ -6698,6 +6779,7 @@ class ComputeRescheduleOrReraiseTestCase(BaseTestCase):
exc_info = sys.exc_info()
compute_utils.add_instance_fault_from_exc(self.context,
+ self.compute.conductor_api,
self.instance, exc_info[0], exc_info=exc_info)
self.compute._deallocate_network(self.context,
self.instance).AndRaise(InnerTestingException("Error"))
@@ -6748,6 +6830,7 @@ class ComputeRescheduleOrReraiseTestCase(BaseTestCase):
except Exception:
exc_info = sys.exc_info()
compute_utils.add_instance_fault_from_exc(self.context,
+ self.compute.conductor_api,
self.instance, exc_info[0], exc_info=exc_info)
self.compute._deallocate_network(self.context,
self.instance)
@@ -6776,6 +6859,7 @@ class ComputeRescheduleOrReraiseTestCase(BaseTestCase):
exc_info = sys.exc_info()
compute_utils.add_instance_fault_from_exc(self.context,
+ self.compute.conductor_api,
self.instance, exc_info[0], exc_info=exc_info)
self.compute._deallocate_network(self.context,
self.instance)
diff --git a/nova/tests/compute/test_compute_utils.py b/nova/tests/compute/test_compute_utils.py
index 6e7227d4c..4372039e0 100644
--- a/nova/tests/compute/test_compute_utils.py
+++ b/nova/tests/compute/test_compute_utils.py
@@ -359,6 +359,9 @@ class UsageInfoTestCase(test.TestCase):
extra_usage_info = {'image_name': 'fake_name'}
db.instance_system_metadata_update(self.context, instance['uuid'],
sys_metadata, False)
+ # NOTE(russellb) Make sure our instance has the latest system_metadata
+ # in it.
+ instance = db.instance_get(self.context, instance_id)
compute_utils.notify_about_instance_usage(self.context, instance,
'create.start', extra_usage_info=extra_usage_info)
self.assertEquals(len(test_notifier.NOTIFICATIONS), 1)
@@ -382,14 +385,3 @@ class UsageInfoTestCase(test.TestCase):
image_ref_url = "%s/images/1" % glance.generate_glance_url()
self.assertEquals(payload['image_ref_url'], image_ref_url)
self.compute.terminate_instance(self.context, instance)
-
-
-class MetadataToDictTestCase(test.TestCase):
- def test_metadata_to_dict(self):
- self.assertEqual(compute_utils.metadata_to_dict(
- [{'key': 'foo1', 'value': 'bar'},
- {'key': 'foo2', 'value': 'baz'}]),
- {'foo1': 'bar', 'foo2': 'baz'})
-
- def test_metadata_to_dict_empty(self):
- self.assertEqual(compute_utils.metadata_to_dict([]), {})
diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py
index 30d176bbd..f21e67845 100644
--- a/nova/tests/conductor/test_conductor.py
+++ b/nova/tests/conductor/test_conductor.py
@@ -426,6 +426,43 @@ class _BaseTestCase(object):
'fake-values', False)
self.assertEqual(result, 'fake-result')
+ def test_instance_fault_create(self):
+ self.mox.StubOutWithMock(db, 'instance_fault_create')
+ db.instance_fault_create(self.context, 'fake-values').AndReturn(
+ 'fake-result')
+ self.mox.ReplayAll()
+ result = self.conductor.instance_fault_create(self.context,
+ 'fake-values')
+ self.assertEqual(result, 'fake-result')
+
+ def test_task_log_get(self):
+ self.mox.StubOutWithMock(db, 'task_log_get')
+ db.task_log_get(self.context, 'task', 'begin', 'end', 'host',
+ 'state').AndReturn('result')
+ self.mox.ReplayAll()
+ result = self.conductor.task_log_get(self.context, 'task', 'begin',
+ 'end', 'host', 'state')
+ self.assertEqual(result, 'result')
+
+ def test_task_log_begin_task(self):
+ self.mox.StubOutWithMock(db, 'task_log_begin_task')
+ db.task_log_begin_task(self.context.elevated(), 'task', 'begin',
+ 'end', 'host', 'items',
+ 'message').AndReturn('result')
+ self.mox.ReplayAll()
+ result = self.conductor.task_log_begin_task(
+ self.context, 'task', 'begin', 'end', 'host', 'items', 'message')
+ self.assertEqual(result, 'result')
+
+ def test_task_log_end_task(self):
+ self.mox.StubOutWithMock(db, 'task_log_end_task')
+ db.task_log_end_task(self.context.elevated(), 'task', 'begin', 'end',
+ 'host', 'errors', 'message').AndReturn('result')
+ self.mox.ReplayAll()
+ result = self.conductor.task_log_end_task(
+ self.context, 'task', 'begin', 'end', 'host', 'errors', 'message')
+ self.assertEqual(result, 'result')
+
class ConductorTestCase(_BaseTestCase, test.TestCase):
"""Conductor Manager Tests."""
diff --git a/nova/tests/conf_fixture.py b/nova/tests/conf_fixture.py
index 9155a3f68..2f4d0ebb1 100644
--- a/nova/tests/conf_fixture.py
+++ b/nova/tests/conf_fixture.py
@@ -22,7 +22,7 @@ from nova import config
from nova import ipv6
from nova.openstack.common import cfg
from nova import paths
-from nova.tests.utils import cleanup_dns_managers
+from nova.tests import utils
CONF = cfg.CONF
CONF.import_opt('use_ipv6', 'nova.netconf')
@@ -70,5 +70,5 @@ class ConfFixture(fixtures.Fixture):
self.conf.set_default('vlan_interface', 'eth0')
config.parse_args([], default_config_files=[])
self.addCleanup(self.conf.reset)
- self.addCleanup(cleanup_dns_managers)
+ self.addCleanup(utils.cleanup_dns_managers)
self.addCleanup(ipv6.api.reset_backend)
diff --git a/nova/tests/fake_policy.py b/nova/tests/fake_policy.py
index acefa856c..ead43adea 100644
--- a/nova/tests/fake_policy.py
+++ b/nova/tests/fake_policy.py
@@ -105,6 +105,7 @@ policy_data = """
"compute_extension:admin_actions:migrate": "",
"compute_extension:aggregates": "",
"compute_extension:agents": "",
+ "compute_extension:baremetal_nodes": "",
"compute_extension:cells": "",
"compute_extension:certificates": "",
"compute_extension:cloudpipe": "",
@@ -156,8 +157,14 @@ policy_data = """
"compute_extension:virtual_interfaces": "",
"compute_extension:virtual_storage_arrays": "",
"compute_extension:volumes": "",
+ "compute_extension:volume_attachments:index": "",
+ "compute_extension:volume_attachments:show": "",
+ "compute_extension:volume_attachments:create": "",
+ "compute_extension:volume_attachments:delete": "",
"compute_extension:volumetypes": "",
"compute_extension:zones": "",
+ "compute_extension:availability_zone:list": "",
+ "compute_extension:availability_zone:detail": "is_admin:True",
"volume:create": "",
@@ -192,16 +199,20 @@ policy_data = """
"volume_extension:types_extra_specs": "",
- "network:get_all_networks": "",
- "network:get_network": "",
- "network:create_networks": "",
- "network:delete_network": "",
- "network:disassociate_network": "",
+ "network:get_all": "",
+ "network:get": "",
+ "network:create": "",
+ "network:delete": "",
+ "network:associate": "",
+ "network:disassociate": "",
"network:get_vifs_by_instance": "",
"network:allocate_for_instance": "",
"network:deallocate_for_instance": "",
"network:validate_networks": "",
"network:get_instance_uuids_by_ip_filter": "",
+ "network:get_instance_id_by_floating_address": "",
+ "network:setup_networks_on_host": "",
+ "network:get_backdoor_port": "",
"network:get_floating_ip": "",
"network:get_floating_ip_pools": "",
@@ -212,6 +223,7 @@ policy_data = """
"network:deallocate_floating_ip": "",
"network:associate_floating_ip": "",
"network:disassociate_floating_ip": "",
+ "network:release_floating_ip": "",
"network:migrate_instance_start": "",
"network:migrate_instance_finish": "",
diff --git a/nova/tests/fake_volume.py b/nova/tests/fake_volume.py
index f2aa3ea91..c7430ee6d 100644
--- a/nova/tests/fake_volume.py
+++ b/nova/tests/fake_volume.py
@@ -136,7 +136,6 @@ class API(object):
def create_with_kwargs(self, context, **kwargs):
volume_id = kwargs.get('volume_id', None)
- print volume_id
v = fake_volume(kwargs['size'],
kwargs['name'],
kwargs['description'],
@@ -145,7 +144,6 @@ class API(object):
None,
None,
None)
- print v.vol['id']
if kwargs.get('status', None) is not None:
v.vol['status'] = kwargs['status']
if kwargs['host'] is not None:
diff --git a/nova/tests/fakelibvirt.py b/nova/tests/fakelibvirt.py
index a573b7d1c..259d192cb 100644
--- a/nova/tests/fakelibvirt.py
+++ b/nova/tests/fakelibvirt.py
@@ -179,6 +179,7 @@ class Domain(object):
self._def = self._parse_definition(xml)
self._has_saved_state = False
self._snapshots = {}
+ self._id = self._connection._id_counter
def _parse_definition(self, xml):
try:
@@ -299,6 +300,9 @@ class Domain(object):
self._state = VIR_DOMAIN_SHUTOFF
self._connection._mark_not_running(self)
+ def ID(self):
+ return self._id
+
def name(self):
return self._def['name']
@@ -517,6 +521,8 @@ class Connection(object):
if dom._transient:
self._undefine(dom)
+ dom._id = -1
+
for (k, v) in self._running_vms.iteritems():
if v == dom:
del self._running_vms[k]
diff --git a/nova/tests/hyperv/README.rst b/nova/tests/hyperv/README.rst
deleted file mode 100644
index c7ba16046..000000000
--- a/nova/tests/hyperv/README.rst
+++ /dev/null
@@ -1,83 +0,0 @@
-=====================================
-OpenStack Hyper-V Nova Testing Architecture
-=====================================
-
-The Hyper-V Nova Compute plugin uses Windows Management Instrumentation (WMI)
-as the main API for hypervisor related operations.
-WMI has a database / procedural oriented nature that can become difficult to
-test with a traditional static mock / stub based unit testing approach.
-
-The included Hyper-V testing framework has been developed with the
-following goals:
-
-1) Dynamic mock generation.
-2) Decoupling. No dependencies on WMI or any other module.
- The tests are designed to work with mocked objects in all cases, including
- OS-dependent (e.g. wmi, os, subprocess) and non-deterministic
- (e.g. time, uuid) modules
-3) Transparency. Mocks and real objects can be swapped via DI
- or monkey patching.
-4) Platform independence.
-5) Tests need to be executed against the real object or against the mocks
- with a simple configuration switch. Development efforts can highly
- benefit from this feature.
-6) It must be possible to change a mock's behavior without running the tests
- against the hypervisor (e.g. by manually adding a value / return value).
-
-The tests included in this package include dynamically generated mock objects,
-based on the recording of the attribute values and invocations on the
-real WMI objects and other OS dependent features.
-The generated mock objects are serialized in the nova/tests/hyperv/stubs
-directory as gzipped pickled objects.
-
-An environment variable controls the execution mode of the tests.
-
-Recording mode:
-
-NOVA_GENERATE_TEST_MOCKS=True
-Tests are executed on the hypervisor (without mocks), and mock objects are
-generated.
-
-Replay mode:
-
-NOVA_GENERATE_TEST_MOCKS=
-Tests are executed with the existing mock objects (default).
-
-Mock generation is performed by nova.tests.hyperv.mockproxy.MockProxy.
-Instances of this class wrap objects that need to be mocked and act as a
-delegate on the wrapped object by leveraging Python's __getattr__ feature.
-Attribute values and method call return values are recorded at each access.
-Objects returned by attributes and method invocations are wrapped in a
-MockProxy consistently.
-From a caller perspective, the MockProxy is completely transparent,
-with the exception of calls to the type(...) builtin function.
-
-At the end of the test, a mock is generated by each MockProxy by calling
-the get_mock() method. A mock is represented by an instance of the
-nova.tests.hyperv.mockproxy.Mock class.
-
-The Mock class task consists of replicating the behaviour of the mocked
-objects / modules by returning the same values in the same order, for example:
-
-def check_path(path):
- if not os.path.exists(path):
- os.makedirs(path)
-
-check_path(path)
-# The second time os.path.exists returns True
-check_path(path)
-
-The injection of MockProxy / Mock instances is performed by the
-nova.tests.hyperv.basetestcase.BaseTestCase class in the setUp()
-method via selective monkey patching.
-Mocks are serialized in tearDown() during recording.
-
-The actual Hyper-V test case inherits from BaseTestCase:
-nova.tests.hyperv.test_hypervapi.HyperVAPITestCase
-
-
-Future directions:
-
-1) Replace the pickled files with a more generic serialization option (e.g. json)
-2) Add methods to statically extend the mocks (e.g. method call return values)
-3) Extend an existing framework, e.g. mox
diff --git a/nova/tests/hyperv/__init__.py b/nova/tests/hyperv/__init__.py
index e69de29bb..090fc0639 100644
--- a/nova/tests/hyperv/__init__.py
+++ b/nova/tests/hyperv/__init__.py
@@ -0,0 +1,16 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
diff --git a/nova/tests/hyperv/basetestcase.py b/nova/tests/hyperv/basetestcase.py
deleted file mode 100644
index c4f6cf95f..000000000
--- a/nova/tests/hyperv/basetestcase.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# 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
-import sys
-
-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()
-
- # python-subunit will wrap test results with a decorator.
- # Need to access the decorated member of results to get the
- # actual test result when using python-subunit.
- if hasattr(self._currentResult, 'decorated'):
- result = self._currentResult.decorated
- else:
- result = self._currentResult
- has_errors = len([test for (test, msgs) in result.errors
- if test.id() == self.id()]) > 0
- failed = len([test for (test, msgs) in result.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:
- __import__(module_name)
- module = sys.modules[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
index 16d894df8..e384e909a 100644
--- a/nova/tests/hyperv/db_fakes.py
+++ b/nova/tests/hyperv/db_fakes.py
@@ -29,35 +29,35 @@ from nova import utils
def get_fake_instance_data(name, project_id, user_id):
return {'name': name,
- 'id': 1,
- 'uuid': str(uuid.uuid4()),
- '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':
- {'name': 'm1.tiny',
- 'memory_mb': 512,
- 'vcpus': 1,
- 'root_gb': 0,
- 'flavorid': 1,
- 'rxtx_factor': 1}
- }
+ 'id': 1,
+ 'uuid': str(uuid.uuid4()),
+ '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':
+ {'name': 'm1.tiny',
+ 'memory_mb': 512,
+ 'vcpus': 1,
+ 'root_gb': 0,
+ 'flavorid': 1,
+ 'rxtx_factor': 1}
+ }
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',
- }
+ '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):
@@ -72,25 +72,25 @@ def get_fake_volume_info_data(target_portal, volume_id):
'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}],
+ 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):
@@ -99,11 +99,9 @@ def stub_out_db_instance_api(stubs):
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.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)}
+ 'm1.xlarge': dict(memory_mb=16384, vcpus=8, root_gb=160, flavorid=5)}
class FakeModel(object):
"""Stubs out for model."""
@@ -152,7 +150,7 @@ def stub_out_db_instance_api(stubs):
'vcpus': instance_type['vcpus'],
'mac_addresses': [{'address': values['mac_address']}],
'root_gb': instance_type['root_gb'],
- }
+ }
return FakeModel(base_options)
def fake_network_get_by_instance(context, instance_id):
@@ -181,4 +179,4 @@ def stub_out_db_instance_api(stubs):
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)
stubs.Set(db, 'block_device_mapping_get_all_by_instance',
- fake_block_device_mapping_get_all_by_instance)
+ fake_block_device_mapping_get_all_by_instance)
diff --git a/nova/tests/hyperv/fake.py b/nova/tests/hyperv/fake.py
new file mode 100644
index 000000000..9890a5462
--- /dev/null
+++ b/nova/tests/hyperv/fake.py
@@ -0,0 +1,46 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
+
+import io
+import os
+
+
+class PathUtils(object):
+ def open(self, path, mode):
+ return io.BytesIO(b'fake content')
+
+ def get_instances_path(self):
+ return 'C:\\FakePath\\'
+
+ def get_instance_path(self, instance_name):
+ return os.path.join(self.get_instances_path(), instance_name)
+
+ def get_vhd_path(self, instance_name):
+ instance_path = self.get_instance_path(instance_name)
+ return os.path.join(instance_path, instance_name + ".vhd")
+
+ def get_base_vhd_path(self, image_name):
+ base_dir = os.path.join(self.get_instances_path(), '_base')
+ return os.path.join(base_dir, image_name + ".vhd")
+
+ def make_export_path(self, instance_name):
+ export_folder = os.path.join(self.get_instances_path(), "export",
+ instance_name)
+ return export_folder
+
+ def vhd_exists(self, path):
+ return False
diff --git a/nova/tests/hyperv/hypervutils.py b/nova/tests/hyperv/hypervutils.py
deleted file mode 100644
index b71e60229..000000000
--- a/nova/tests/hyperv/hypervutils.py
+++ /dev/null
@@ -1,262 +0,0 @@
-# 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 volumeutilsV2
-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.__conn_storage = None
- self._volumeutils = volumeutilsV2.VolumeUtilsV2(
- self._conn_storage, self._conn_wmi)
-
- @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
-
- @property
- def _conn_storage(self):
- if self.__conn_storage is None:
- storage_namespace = '//./Root/Microsoft/Windows/Storage'
- self.__conn_storage = wmi.WMI(moniker=storage_namespace)
- return self.__conn_storage
-
- 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)
-
- dvds = [r for r in rasds
- if r.ResourceSubType == 'Microsoft Virtual CD/DVD Disk']
- dvd_files = []
- for dvd in dvds:
- dvd_files.extend([c for c in dvd.Connection])
-
- return (disk_files, volume_drives, dvd_files)
-
- 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, dvd_files) = 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 + dvd_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)
- if (self.iscsi_volume_sessions_exist(volume_id)):
- self._volumeutils.logout_storage_target(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
deleted file mode 100644
index 513422c13..000000000
--- a/nova/tests/hyperv/mockproxy.py
+++ /dev/null
@@ -1,272 +0,0 @@
-# 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:
- if isinstance(obj, str) or isinstance(obj, unicode):
- val = obj
- elif hasattr(obj, '__str__') and inspect.ismethod(obj.__str__):
- val = str(obj)
- else:
- val = str(type(obj))
- return val
-
-
-def serialize_args(*args, **kwargs):
- """Workaround for float string conversion issues in Python 2.6."""
- return serialize_obj((args, kwargs))
-
-
-class MockException(Exception):
- def __init__(self, message):
- super(MockException, self).__init__(message)
-
-
-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
-
- try:
- value = self._values[name][c]
- except IndexError as ex:
- raise MockException(_('Couldn\'t find invocation num. %(c)d '
- 'of attribute "%(name)s"') % locals())
- return value
-
- 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
-
- try:
- m = self._values[name]
- except KeyError as ex:
- raise MockException(_('Couldn\'t find attribute "%s"') % (name))
-
- try:
- value = m[params][c]
- except KeyError as ex:
- raise MockException(_('Couldn\'t find attribute "%(name)s" '
- 'with arguments "%(params)s"') % locals())
- except IndexError as ex:
- raise MockException(_('Couldn\'t find invocation num. %(c)d '
- 'of attribute "%(name)s" with arguments "%(params)s"')
- % locals())
-
- return value
-
- 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:
- try:
- isdict = isinstance(self._values[name], dict)
- except KeyError as ex:
- raise MockException(_('Couldn\'t find attribute "%s"')
- % (name))
-
- if isdict:
- 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
deleted file mode 100644
index 150fd3ad1..000000000
--- a/nova/tests/hyperv/stubs/README.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-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
deleted file mode 100644
index c65832c57..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 7076c4868..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index c251f9d6c..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index cac08e3d0..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index d6e624bb0..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index bb18f7453..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index a5f592a74..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 4bebe0e72..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 29a610f36..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index ca92ece00..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 58269455d..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 97cd7e62b..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 708197430..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index d5eb4d746..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index d8c63d8ad..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index d0b27d201..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 657379cec..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 8bf58ef5c..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index c20281811..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index a198af844..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 749eabe40..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index c40e6f995..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index c67dc9271..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 0d671fc18..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 66583beb1..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index efdef819f..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 5edd6f147..000000000
--- a/nova/tests/hyperv/stubs/nova.tests.test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_nova.utils.p.gz
deleted file mode 100644
index f968e2af5..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_nova.virt.configdrive.p.gz
deleted file mode 100644
index bd5ced9f8..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index a48a21ca9..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index c662b602a..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 6a692b3d8..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_subprocess.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index f2ae56be1..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 2d24523aa..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_uuid.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_nova.utils.p.gz
deleted file mode 100644
index aca0d6f0c..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_nova.virt.configdrive.p.gz
deleted file mode 100644
index bbeec53df..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 3bf9bd13a..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 62e3fa329..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 36970348a..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_subprocess.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 8db997abf..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 73f90ac2b..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 3ae9a6f46..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_with_target_connection_failure_wmi.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 5b851f9b7..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_attach_volume_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_nova.utils.p.gz
deleted file mode 100644
index 7a1c47449..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_nova.virt.configdrive.p.gz
deleted file mode 100644
index 48583265e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 90d6a2ca6..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 3b17cc74f..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_subprocess.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 162f52457..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index f88f8bc86..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index f671dc247..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_boot_from_volume_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_nova.utils.p.gz
deleted file mode 100644
index 37892d051..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_nova.virt.configdrive.p.gz
deleted file mode 100644
index 9aec45796..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index ffc21536e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index b47c49202..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 78e4292b6..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 5bc7602a8..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 9ba025e55..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_destroy_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_nova.utils.p.gz
deleted file mode 100644
index 3341bca28..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_nova.virt.configdrive.p.gz
deleted file mode 100644
index 56cb9d103..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 81205e04d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 9d1311341..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index a151a99b4..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_subprocess.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index b1d0b0f3a..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index c2985c424..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 2c4901c9f..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_detach_volume_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_ctypes.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_ctypes.p.gz
deleted file mode 100644
index 2481a7b3e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_ctypes.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_multiprocessing.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_multiprocessing.p.gz
deleted file mode 100644
index 61cbc1854..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_multiprocessing.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_os.p.gz
deleted file mode 100644
index 09b86b24e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_os.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_shutil.p.gz
deleted file mode 100644
index ba89bfd7e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_shutil.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_wmi.p.gz
deleted file mode 100644
index cfce8c10a..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_available_resource_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_os.p.gz
deleted file mode 100644
index 6092f36ab..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_os.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_shutil.p.gz
deleted file mode 100644
index 010c07e56..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_shutil.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_wmi.p.gz
deleted file mode 100644
index 9d3adec48..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_host_stats_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_nova.utils.p.gz
deleted file mode 100644
index 995dde1b5..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_nova.virt.configdrive.p.gz
deleted file mode 100644
index 12d18d12e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 64c756ffa..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index d2cefdc37..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 9fdef3b90..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index c34d2308b..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 36a342e7c..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_get_info_wmi.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 3ab35a29f..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 411c0ed07..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_detail_wmi.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 1af20acde..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index d84122d77..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_list_instances_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_nova.utils.p.gz
deleted file mode 100644
index d650f40a5..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_nova.virt.configdrive.p.gz
deleted file mode 100644
index a03d442a4..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 993d9bb2d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 6693c2ce9..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 07898dd55..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 56e583449..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_uuid.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_nova.utils.p.gz
deleted file mode 100644
index 5d4c0e111..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_nova.virt.configdrive.p.gz
deleted file mode 100644
index cb52cb974..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 8b2ff15f3..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index aee1fb14d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index f926d206f..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 483b23d53..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 14d61039f..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_with_target_failure_wmi.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index daecf0156..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_live_migration_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_nova.utils.p.gz
deleted file mode 100644
index 548b88148..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_nova.virt.configdrive.p.gz
deleted file mode 100644
index 8545a1833..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index c1daf3db9..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 750d68d29..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 6e91b72a2..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 2d0349d96..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 6b9ef360a..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_already_paused_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_nova.utils.p.gz
deleted file mode 100644
index 3e582226f..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_nova.virt.configdrive.p.gz
deleted file mode 100644
index 723966011..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 29b73888b..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 595124af2..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 03d53be74..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 2a0663e6f..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index e651c02fc..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pause_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_nova.utils.p.gz
deleted file mode 100644
index a50935649..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_nova.virt.configdrive.p.gz
deleted file mode 100644
index 4b07271c1..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index f62298ed7..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 12a164f23..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 33f1862e6..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 80853eea4..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 5cebe527d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_already_powered_off_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_nova.utils.p.gz
deleted file mode 100644
index d0c431b9d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_nova.virt.configdrive.p.gz
deleted file mode 100644
index d231f803d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 25fe5f3ff..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 8be80ba56..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 51b6f2df8..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 97812405e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 20b2e021e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_off_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_nova.utils.p.gz
deleted file mode 100644
index c32f9ecd2..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_nova.virt.configdrive.p.gz
deleted file mode 100644
index 672376a0e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index aa6f4ca8a..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 00f5770a7..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 1631f35df..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index ec28756ad..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 699ccde76..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_already_running_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_nova.utils.p.gz
deleted file mode 100644
index 2b99fb9cd..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_nova.virt.configdrive.p.gz
deleted file mode 100644
index a43bfeb7e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 57e74e618..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 273364d95..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 732a0f2e6..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index d6cb32559..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index e44197039..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_power_on_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_nova.utils.p.gz
deleted file mode 100644
index 456af2816..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_nova.utils.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 93568dcef..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 6a4b90850..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index fc816320f..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 83cf9c071..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 93977743f..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_cow_image_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_nova.utils.p.gz
deleted file mode 100644
index f58f80a79..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_nova.utils.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 18a8aed13..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 4225a72b0..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 363c431d4..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_pre_live_migration_no_cow_image_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_nova.utils.p.gz
deleted file mode 100644
index 8761703dc..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_nova.virt.configdrive.p.gz
deleted file mode 100644
index fc907ed31..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 0eca8e6ce..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 0886c942d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index d0fb77bd1..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index df3961276..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 4df451154..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_reboot_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_nova.utils.p.gz
deleted file mode 100644
index 59724b43d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_nova.virt.configdrive.p.gz
deleted file mode 100644
index 4b3711ec0..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 2f9a5de9c..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 8ffa516c0..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 6aade88c6..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 276c06397..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 77a1650d4..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_already_running_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_nova.utils.p.gz
deleted file mode 100644
index ce19ed290..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_nova.virt.configdrive.p.gz
deleted file mode 100644
index b2dadcd4d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index aa378fedd..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 333a27b89..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 16ca553f6..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 8cf3b564e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 0a2c8513b..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_resume_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_nova.utils.p.gz
deleted file mode 100644
index ae42d7734..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_nova.virt.configdrive.p.gz
deleted file mode 100644
index 4fec34d08..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 74e8e95a6..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index da0528797..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 63f02bc75..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index c014d5003..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_uuid.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_nova.utils.p.gz
deleted file mode 100644
index 592658541..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_nova.virt.configdrive.p.gz
deleted file mode 100644
index 892f3c346..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 9996339f5..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 409ee5ef7..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 9e799c196..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 848024366..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 687952c4c..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_with_update_failure_wmi.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 57988a6b6..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_snapshot_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_nova.utils.p.gz
deleted file mode 100644
index 303a47019..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_nova.virt.configdrive.p.gz
deleted file mode 100644
index c211622e1..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_os.p.gz
deleted file mode 100644
index 5e5303cbc..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_os.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_shutil.p.gz
deleted file mode 100644
index 1bcbd48f3..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_shutil.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_time.p.gz
deleted file mode 100644
index ae557d73d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_time.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_uuid.p.gz
deleted file mode 100644
index 90ebff4e7..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_uuid.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_wmi.p.gz
deleted file mode 100644
index beccc2737..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_cdrom_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_nova.utils.p.gz
deleted file mode 100644
index af5082ab6..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_nova.virt.configdrive.p.gz
deleted file mode 100644
index 837d81b70..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_os.p.gz
deleted file mode 100644
index ecea62a01..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_os.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_shutil.p.gz
deleted file mode 100644
index 283cd7fdd..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_shutil.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_time.p.gz
deleted file mode 100644
index 44dcc89ae..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_time.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_uuid.p.gz
deleted file mode 100644
index 5c520c768..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_uuid.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_wmi.p.gz
deleted file mode 100644
index aec53305d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_config_drive_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_nova.utils.p.gz
deleted file mode 100644
index a16c88e54..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_nova.virt.configdrive.p.gz
deleted file mode 100644
index d9c4e9c82..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 94aafb39a..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index e0ad00bf6..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 00f7839ba..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 77422d3f5..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 414194a9d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_cow_image_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_nova.utils.p.gz
deleted file mode 100644
index b1e825822..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_nova.virt.configdrive.p.gz
deleted file mode 100644
index 1e3d89fea..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_os.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_os.p.gz
deleted file mode 100644
index 627c78d7e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_os.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_shutil.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_shutil.p.gz
deleted file mode 100644
index e577cdb5e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_shutil.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_time.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_time.p.gz
deleted file mode 100644
index 72962fc52..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_time.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_uuid.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_uuid.p.gz
deleted file mode 100644
index 5d1351a14..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_uuid.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_wmi.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_wmi.p.gz
deleted file mode 100644
index eb0ed7241..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_config_drive_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_nova.utils.p.gz
deleted file mode 100644
index c65264688..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_nova.virt.configdrive.p.gz
deleted file mode 100644
index ca40d6413..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 1d8081a3e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index e03633b90..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 00c56dacc..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 7381c3cc6..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 115ed1dd5..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_cow_image_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.utils.p.gz
deleted file mode 100644
index df40b08c0..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.virt.configdrive.p.gz
deleted file mode 100644
index b51766f75..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 092a1f933..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 77f333c00..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 8ab166a60..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 97e96be17..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 728464ca9..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_spawn_no_vswitch_exception_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_nova.utils.p.gz
deleted file mode 100644
index 4aa6d171a..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_nova.virt.configdrive.p.gz
deleted file mode 100644
index df063a22e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index b30363fcc..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 1681d9947..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 4469fd90e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index f94f2ebb9..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 03afe2235..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_already_suspended_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_nova.utils.p.gz
deleted file mode 100644
index 2f95f62bf..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_nova.virt.configdrive.p.gz
deleted file mode 100644
index 2e7ab44ad..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index eb514d086..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 810c9e14d..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 2eb2a8372..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 67311757a..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 0779125b3..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_suspend_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_nova.utils.p.gz
deleted file mode 100644
index 7e6cc708e..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_nova.virt.configdrive.p.gz
deleted file mode 100644
index 0ce4bbf63..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 9068792c7..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 9b06cb884..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index e91e6c965..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 271ded270..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 253bdfc82..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_already_running_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_nova.utils.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_nova.utils.p.gz
deleted file mode 100644
index 20486b189..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_nova.utils.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_nova.virt.configdrive.p.gz b/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_nova.virt.configdrive.p.gz
deleted file mode 100644
index be92217ed..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_nova.virt.configdrive.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 36059e753..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_os.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index aea394e9f..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_shutil.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 4850d3cda..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_time.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 99bf1806c..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_uuid.p.gz
+++ /dev/null
Binary files 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
deleted file mode 100644
index 87b571e4a..000000000
--- a/nova/tests/hyperv/stubs/test_hypervapi.HyperVAPITestCase.test_unpause_wmi.p.gz
+++ /dev/null
Binary files differ
diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py
index 9dd9e5121..fb26fa4f1 100644
--- a/nova/tests/image/test_glance.py
+++ b/nova/tests/image/test_glance.py
@@ -339,7 +339,6 @@ class TestGlanceImageService(test.TestCase):
def test_update(self):
fixture = self._make_fixture(name='test image')
image = self.service.create(self.context, fixture)
- print image
image_id = image['id']
fixture['name'] = 'new image name'
self.service.update(self.context, image_id, fixture)
diff --git a/nova/tests/integrated/api_samples/README.rst b/nova/tests/integrated/api_samples/README.rst
index 065df1d32..b2ad71d4c 100644
--- a/nova/tests/integrated/api_samples/README.rst
+++ b/nova/tests/integrated/api_samples/README.rst
@@ -1,11 +1,21 @@
Api Samples
===========
-Samples in this directory are automatically generated from the api samples
-integration tests. To regenerate the samples, simply set GENERATE_SAMPLES
-in the environment before running the tests. For example:
+This part of the tree contains templates for API samples. The
+documentation in doc/api_samples is completely autogenerated from the
+tests in this directory.
+
+To add a new api sample, add tests for the common passing and failing
+cases in this directory for your extension, and modify test_samples.py
+for your tests. There should be both JSON and XML tests included.
+
+Then run the following command:
GENERATE_SAMPLES=True tox -epy27 nova.tests.integrated
+Which will create the files on doc/api_samples.
+
If new tests are added or the .tpl files are changed due to bug fixes, the
-samples should be regenerated so they are in sync with the templates.
+samples must be regenerated so they are in sync with the templates, as
+there is an additional test which reloads the documentation and
+ensures that it's in sync.
diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl
index fe0613646..be2fabec4 100644
--- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl
+++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.json.tpl
@@ -89,6 +89,14 @@
"updated": "%(timestamp)s"
},
{
+ "alias": "os-baremetal-nodes",
+ "description": "%(text)s",
+ "links": [],
+ "name": "BareMetalNodes",
+ "namespace": "http://docs.openstack.org/compute/ext/baremetal_nodes/api/v2",
+ "updated": "%(timestamp)s"
+ },
+ {
"alias": "os-cells",
"description": "%(text)s",
"links": [],
diff --git a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl
index 2051d891a..ae2e9ff9e 100644
--- a/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl
+++ b/nova/tests/integrated/api_samples/all_extensions/extensions-get-resp.xml.tpl
@@ -33,6 +33,9 @@
<extension alias="os-agents" name="Agents" namespace="http://docs.openstack.org/compute/ext/agents/api/v2" updated="%(timestamp)s">
<description>%(text)s</description>
</extension>
+ <extension alias="os-baremetal-nodes" name="BareMetalNodes" namespace="http://docs.openstack.org/compute/ext/baremetal_nodes/api/v2" updated="%(timestamp)s">
+ <description>%(text)s</description>
+ </extension>
<extension alias="os-cells" updated="%(timestamp)s" namespace="http://docs.openstack.org/compute/ext/cells/api/v1.1" name="Cells">
<description>%(text)s</description>
</extension>
diff --git a/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.json.tpl b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.json.tpl
new file mode 100644
index 000000000..6d44692e1
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.json.tpl
@@ -0,0 +1,48 @@
+{
+ "availabilityZoneInfo": [
+ {
+ "zoneName": "zone-1",
+ "zoneState": {
+ "available": true
+ },
+ "hosts": {
+ "fake_host-1": {
+ "nova-compute": {
+ "active": true,
+ "available": true,
+ "updated_at": "2012-12-26T14:45:25.000000"
+ }
+ }
+ }
+ },
+ {
+ "zoneName": "internal",
+ "zoneState": {
+ "available": true
+ },
+ "hosts": {
+ "fake_host-1": {
+ "nova-sched": {
+ "active": true,
+ "available": true,
+ "updated_at": "2012-12-26T14:45:25.000000"
+ }
+ },
+ "fake_host-2": {
+ "nova-network": {
+ "active": true,
+ "available": false,
+ "updated_at": "2012-12-26T14:45:24.000000"
+ }
+ }
+ }
+ },
+ {
+ "zoneName": "zone-2",
+ "zoneState": {
+ "available": false
+ },
+ "hosts": null
+ }
+ ]
+} \ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.xml.tpl b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.xml.tpl
new file mode 100644
index 000000000..856a64957
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-details-resp.xml.tpl
@@ -0,0 +1,44 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<availabilityZones
+ xmlns:os-availability-zone="http://docs.openstack.org/compute/ext/availabilityzone/api/v1.1">
+ <availabilityZone name="zone-1">
+ <zoneState available="True" />
+ <hosts>
+ <host name="fake_host-1">
+ <services>
+ <service name="nova-compute">
+ <serviceState available="True" active="True"
+ updated_at="2012-12-26 14:45:25" />
+ </service>
+ </services>
+ </host>
+ </hosts>
+ <metadata />
+ </availabilityZone>
+ <availabilityZone name="internal">
+ <zoneState available="True" />
+ <hosts>
+ <host name="fake_host-1">
+ <services>
+ <service name="nova-sched">
+ <serviceState available="True" active="True"
+ updated_at="2012-12-26 14:45:25" />
+ </service>
+ </services>
+ </host>
+ <host name="fake_host-2">
+ <services>
+ <service name="nova-network">
+ <serviceState available="False" active="True"
+ updated_at="2012-12-26 14:45:24" />
+ </service>
+ </services>
+ </host>
+ </hosts>
+ <metadata />
+ </availabilityZone>
+ <availabilityZone name="zone-2">
+ <zoneState available="False" />
+ <metadata />
+ </availabilityZone>
+</availabilityZones> \ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.json.tpl b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.json.tpl
new file mode 100644
index 000000000..381708aaf
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.json.tpl
@@ -0,0 +1,18 @@
+{
+ "availabilityZoneInfo": [
+ {
+ "zoneName": "zone-1",
+ "zoneState": {
+ "available": true
+ },
+ "hosts": null
+ },
+ {
+ "zoneName": "zone-2",
+ "zoneState": {
+ "available": false
+ },
+ "hosts": null
+ }
+ ]
+} \ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.xml.tpl b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.xml.tpl
new file mode 100644
index 000000000..1eff177de
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-availability-zone/availability-zone-get-resp.xml.tpl
@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<availabilityZones
+ xmlns:os-availability-zone="http://docs.openstack.org/compute/ext/availabilityzone/api/v1.1">
+ <availabilityZone name="zone-1">
+ <zoneState available="True" />
+ <metadata />
+ </availabilityZone>
+ <availabilityZone name="zone-2">
+ <zoneState available="False" />
+ <metadata />
+ </availabilityZone>
+</availabilityZones> \ No newline at end of file
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.json.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.json.tpl
new file mode 100644
index 000000000..fbc9e5b8d
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.json.tpl
@@ -0,0 +1,5 @@
+{
+ "add_interface": {
+ "address": "%(address)s"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.xml.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.xml.tpl
new file mode 100644
index 000000000..abbbe895b
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-req.xml.tpl
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<add_interface
+ address="%(address)s"
+/>
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.json.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.json.tpl
new file mode 100644
index 000000000..268b41f08
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.json.tpl
@@ -0,0 +1,8 @@
+{
+ "interface": {
+ "id": %(interface_id)s,
+ "address": "aa:aa:aa:aa:aa:aa",
+ "datapath_id": null,
+ "port_no": null
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.xml.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.xml.tpl
new file mode 100644
index 000000000..e5d34f92b
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-add-interface-resp.xml.tpl
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface
+ id="%(interface_id)s"
+ address="aa:aa:aa:aa:aa:aa"
+ datapath_id="None"
+ port_no="None"
+/>
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-req.json.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-req.json.tpl
new file mode 100644
index 000000000..fd2ae101f
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-req.json.tpl
@@ -0,0 +1,14 @@
+{
+ "node": {
+ "service_host": "host",
+ "cpus": 8,
+ "memory_mb": 8192,
+ "local_gb": 128,
+ "pm_address": "10.1.2.3",
+ "pm_user": "pm_user",
+ "pm_password": "pm_pass",
+ "prov_mac_address": "12:34:56:78:90:ab",
+ "prov_vlan_id": 1234,
+ "terminal_port": 8000
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-req.xml.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-req.xml.tpl
new file mode 100644
index 000000000..78a2c1c74
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-req.xml.tpl
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node
+ service_host="host"
+ cpus="8"
+ memory_mb="8192"
+ local_gb="128"
+ pm_address="10.1.2.3"
+ pm_user="pm_user"
+ prov_mac_address="12:34:56:78:90:ab"
+ prov_vlan_id="1234"
+ terminal_port="8000"
+/>
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-resp.json.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-resp.json.tpl
new file mode 100644
index 000000000..d3911b49d
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-resp.json.tpl
@@ -0,0 +1,16 @@
+{
+ "node": {
+ "service_host": "host",
+ "cpus": 8,
+ "memory_mb": 8192,
+ "local_gb": 128,
+ "pm_address": "10.1.2.3",
+ "pm_user": "pm_user",
+ "prov_mac_address": "12:34:56:78:90:ab",
+ "prov_vlan_id": 1234,
+ "terminal_port": 8000,
+ "instance_uuid": null,
+ "id": %(node_id)s,
+ "interfaces": []
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-resp.xml.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-resp.xml.tpl
new file mode 100644
index 000000000..f21d16a11
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-create-resp.xml.tpl
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node
+ service_host="host"
+ cpus="8"
+ memory_mb="8192"
+ local_gb="128"
+ pm_address="10.1.2.3"
+ pm_user="pm_user"
+ prov_mac_address="12:34:56:78:90:ab"
+ prov_vlan_id="1234"
+ terminal_port="8000"
+ instance_uuid="None"
+ id="%(node_id)s">
+ <interfaces/>
+</node>
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-list-resp.json.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-list-resp.json.tpl
new file mode 100644
index 000000000..9b04a9cea
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-list-resp.json.tpl
@@ -0,0 +1,21 @@
+{
+ "nodes": [{
+ "service_host": "host",
+ "cpus": 8,
+ "memory_mb": 8192,
+ "local_gb": 128,
+ "pm_address": "10.1.2.3",
+ "pm_user": "pm_user",
+ "prov_mac_address": "12:34:56:78:90:ab",
+ "prov_vlan_id": 1234,
+ "terminal_port": 8000,
+ "instance_uuid": null,
+ "id": %(node_id)s,
+ "interfaces": [{
+ "id": %(interface_id)s,
+ "address": "%(address)s",
+ "datapath_id": null,
+ "port_no": null
+ }]
+ }]
+}
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-list-resp.xml.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-list-resp.xml.tpl
new file mode 100644
index 000000000..f17b6cc20
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-list-resp.xml.tpl
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<nodes>
+<node
+ service_host="host"
+ cpus="8"
+ memory_mb="8192"
+ local_gb="128"
+ pm_address="10.1.2.3"
+ pm_user="pm_user"
+ prov_mac_address="12:34:56:78:90:ab"
+ prov_vlan_id="1234"
+ terminal_port="8000"
+ instance_uuid="None"
+ id="%(node_id)s">
+ <interfaces>
+ <interface id="%(interface_id)s" address="%(address)s" datapath_id="None" port_no="None"/>
+ </interfaces>
+</node>
+</nodes>
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.json.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.json.tpl
new file mode 100644
index 000000000..eb76a9140
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.json.tpl
@@ -0,0 +1,5 @@
+{
+ "remove_interface": {
+ "address": "%(address)s"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.xml.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.xml.tpl
new file mode 100644
index 000000000..089c94e86
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-remove-interface-req.xml.tpl
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<remove_interface
+ address="%(address)s"
+/>
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-show-resp.json.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-show-resp.json.tpl
new file mode 100644
index 000000000..701b33d24
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-show-resp.json.tpl
@@ -0,0 +1,21 @@
+{
+ "node": {
+ "service_host": "host",
+ "cpus": 8,
+ "memory_mb": 8192,
+ "local_gb": 128,
+ "pm_address": "10.1.2.3",
+ "pm_user": "pm_user",
+ "prov_mac_address": "12:34:56:78:90:ab",
+ "prov_vlan_id": 1234,
+ "terminal_port": 8000,
+ "instance_uuid": null,
+ "id": %(node_id)s,
+ "interfaces": [{
+ "id": %(interface_id)s,
+ "address": "%(address)s",
+ "datapath_id": null,
+ "port_no": null
+ }]
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-show-resp.xml.tpl b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-show-resp.xml.tpl
new file mode 100644
index 000000000..36e5568e5
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-baremetal-nodes/baremetal-node-show-resp.xml.tpl
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node
+ service_host="host"
+ cpus="8"
+ memory_mb="8192"
+ local_gb="128"
+ pm_address="10.1.2.3"
+ pm_user="pm_user"
+ prov_mac_address="12:34:56:78:90:ab"
+ prov_vlan_id="1234"
+ terminal_port="8000"
+ instance_uuid="None"
+ id="%(node_id)s">
+ <interfaces>
+ <interface id="%(interface_id)s" address="%(address)s" datapath_id="None" port_no="None"/>
+ </interfaces>
+</node>
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json.tpl
new file mode 100644
index 000000000..dd858e76c
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.json.tpl
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "%(value1)s",
+ "key2": "%(value2)s"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml.tpl
new file mode 100644
index 000000000..c94595cad
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-req.xml.tpl
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<extra_specs>
+ <key1>%(value1)s</key1>
+ <key2>%(value2)s</key2>
+</extra_specs>
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json.tpl
new file mode 100644
index 000000000..dd858e76c
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.json.tpl
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "%(value1)s",
+ "key2": "%(value2)s"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml.tpl
new file mode 100644
index 000000000..1008b5bb0
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-create-resp.xml.tpl
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_specs>
+ <key2>%(value2)s</key2>
+ <key1>%(value1)s</key1>
+</extra_specs>
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json.tpl
new file mode 100644
index 000000000..adfa77008
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.json.tpl
@@ -0,0 +1,3 @@
+{
+ "key1": "%(value1)s"
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml.tpl
new file mode 100644
index 000000000..e3de59a34
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-get-resp.xml.tpl
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_spec key="key1">%(value1)s</extra_spec>
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json.tpl
new file mode 100644
index 000000000..dd858e76c
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.json.tpl
@@ -0,0 +1,6 @@
+{
+ "extra_specs": {
+ "key1": "%(value1)s",
+ "key2": "%(value2)s"
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml.tpl
new file mode 100644
index 000000000..1008b5bb0
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-list-resp.xml.tpl
@@ -0,0 +1,5 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_specs>
+ <key2>%(value2)s</key2>
+ <key1>%(value1)s</key1>
+</extra_specs>
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json.tpl
new file mode 100644
index 000000000..adfa77008
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.json.tpl
@@ -0,0 +1,3 @@
+{
+ "key1": "%(value1)s"
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml.tpl
new file mode 100644
index 000000000..6421e5959
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-req.xml.tpl
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+ <key1>%(value1)s</key1>
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json.tpl
new file mode 100644
index 000000000..adfa77008
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.json.tpl
@@ -0,0 +1,3 @@
+{
+ "key1": "%(value1)s"
+}
diff --git a/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml.tpl b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml.tpl
new file mode 100644
index 000000000..e3de59a34
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-flavor-extra-specs/flavor-extra-specs-update-resp.xml.tpl
@@ -0,0 +1,2 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<extra_spec key="key1">%(value1)s</extra_spec>
diff --git a/nova/tests/integrated/api_samples/os-floating-ip-pools/floatingippools-list-resp.json.tpl b/nova/tests/integrated/api_samples/os-floating-ip-pools/floatingippools-list-resp.json.tpl
new file mode 100644
index 000000000..607109d70
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ip-pools/floatingippools-list-resp.json.tpl
@@ -0,0 +1,10 @@
+{
+ "floating_ip_pools": [
+ {
+ "name": "%(pool1)s"
+ },
+ {
+ "name": "%(pool2)s"
+ }
+ ]
+}
diff --git a/nova/tests/integrated/api_samples/os-floating-ip-pools/floatingippools-list-resp.xml.tpl b/nova/tests/integrated/api_samples/os-floating-ip-pools/floatingippools-list-resp.xml.tpl
new file mode 100644
index 000000000..ae4b3a4bb
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-floating-ip-pools/floatingippools-list-resp.xml.tpl
@@ -0,0 +1,4 @@
+<floating_ip_pools>
+ <floating_ip_pool name="%(pool1)s"/>
+ <floating_ip_pool name="%(pool2)s"/>
+</floating_ip_pools>
diff --git a/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.json.tpl b/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.json.tpl
new file mode 100644
index 000000000..6974f360f
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.json.tpl
@@ -0,0 +1,17 @@
+{
+ "instance_usage_audit_logs": {
+ "hosts_not_run": [
+ "%(hostid)s"
+ ],
+ "log": {},
+ "num_hosts": 1,
+ "num_hosts_done": 0,
+ "num_hosts_not_run": 1,
+ "num_hosts_running": 0,
+ "overall_status": "0 of 1 hosts done. 0 errors.",
+ "period_beginning": "%(timestamp)s",
+ "period_ending": "%(timestamp)s",
+ "total_errors": 0,
+ "total_instances": 0
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.xml.tpl b/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.xml.tpl
new file mode 100644
index 000000000..4eafa8b4a
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-index-get-resp.xml.tpl
@@ -0,0 +1,16 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<instance_usage_audit_logs>
+ <total_errors>0</total_errors>
+ <total_instances>0</total_instances>
+ <log/>
+ <num_hosts_running>0</num_hosts_running>
+ <num_hosts_done>0</num_hosts_done>
+ <num_hosts_not_run>1</num_hosts_not_run>
+ <hosts_not_run>
+ <item>%(hostid)s</item>
+ </hosts_not_run>
+ <overall_status>0 of 1 hosts done. 0 errors.</overall_status>
+ <period_ending>%(timestamp)s</period_ending>
+ <period_beginning>%(timestamp)s</period_beginning>
+ <num_hosts>1</num_hosts>
+</instance_usage_audit_logs>
diff --git a/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.json.tpl b/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.json.tpl
new file mode 100644
index 000000000..eda952304
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.json.tpl
@@ -0,0 +1,17 @@
+{
+ "instance_usage_audit_log": {
+ "hosts_not_run": [
+ "%(hostid)s"
+ ],
+ "log": {},
+ "num_hosts": 1,
+ "num_hosts_done": 0,
+ "num_hosts_not_run": 1,
+ "num_hosts_running": 0,
+ "overall_status": "0 of 1 hosts done. 0 errors.",
+ "period_beginning": "%(timestamp)s",
+ "period_ending": "%(timestamp)s",
+ "total_errors": 0,
+ "total_instances": 0
+ }
+}
diff --git a/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.xml.tpl b/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.xml.tpl
new file mode 100644
index 000000000..1ef243292
--- /dev/null
+++ b/nova/tests/integrated/api_samples/os-instance_usage_audit_log/inst-usage-audit-log-show-get-resp.xml.tpl
@@ -0,0 +1,16 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<instance_usage_audit_log>
+ <total_errors>0</total_errors>
+ <total_instances>0</total_instances>
+ <log/>
+ <num_hosts_running>0</num_hosts_running>
+ <num_hosts_done>0</num_hosts_done>
+ <num_hosts_not_run>1</num_hosts_not_run>
+ <hosts_not_run>
+ <item>%(hostid)s</item>
+ </hosts_not_run>
+ <overall_status>0 of 1 hosts done. 0 errors.</overall_status>
+ <period_ending>%(timestamp)s</period_ending>
+ <period_beginning>%(timestamp)s</period_beginning>
+ <num_hosts>1</num_hosts>
+</instance_usage_audit_log>
diff --git a/nova/tests/integrated/test_api_samples.py b/nova/tests/integrated/test_api_samples.py
index f101da243..eafc31c61 100644
--- a/nova/tests/integrated/test_api_samples.py
+++ b/nova/tests/integrated/test_api_samples.py
@@ -22,13 +22,13 @@ import re
import urllib
import uuid as uuid_lib
-from coverage import coverage
+import coverage
from lxml import etree
from nova.api.metadata import password
from nova.api.openstack.compute.contrib import coverage_ext
# Import extensions to pull in osapi_compute_extension CONF option used below.
-from nova.cloudpipe.pipelib import CloudPipe
+from nova.cloudpipe import pipelib
from nova import context
from nova import db
from nova.db.sqlalchemy import models
@@ -43,6 +43,7 @@ from nova.openstack.common import timeutils
import nova.quota
from nova.scheduler import driver
from nova import test
+from nova.tests.baremetal.db import base as bm_db_base
from nova.tests import fake_network
from nova.tests.image import fake
from nova.tests.integrated import integrated_helpers
@@ -291,7 +292,7 @@ class ApiSampleTestBase(integrated_helpers._IntegratedTestBase):
# shouldn't be an issue for this case.
'timestamp': '\d{4}-[0,1]\d-[0-3]\d[ ,T]'
'\d{2}:\d{2}:\d{2}'
- '(Z|(\+|-)\d{2}:\d{2}|\.\d{6})',
+ '(Z|(\+|-)\d{2}:\d{2}|\.\d{6}|)',
'password': '[0-9a-zA-Z]{1,12}',
'ip': '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}',
'ip6': '([0-9a-zA-Z]{1,4}:){1,7}:?[0-9a-zA-Z]{1,4}',
@@ -376,12 +377,9 @@ class ApiSamplesTrap(ApiSampleTestBase):
do_not_approve_additions.append('os-config-drive')
do_not_approve_additions.append('os-create-server-ext')
do_not_approve_additions.append('os-flavor-access')
- do_not_approve_additions.append('os-flavor-extra-specs')
do_not_approve_additions.append('os-floating-ip-dns')
- do_not_approve_additions.append('os-floating-ip-pools')
do_not_approve_additions.append('os-fping')
do_not_approve_additions.append('os-hypervisors')
- do_not_approve_additions.append('os-instance_usage_audit_log')
do_not_approve_additions.append('os-networks')
do_not_approve_additions.append('os-services')
do_not_approve_additions.append('os-volumes')
@@ -760,7 +758,7 @@ class CoverageExtJsonTests(ApiSampleTestBase):
self.stubs.Set(coverage_ext.CoverageController, '_check_coverage',
_fake_check_coverage)
- self.stubs.Set(coverage, 'xml_report', _fake_xml_report)
+ self.stubs.Set(coverage.coverage, 'xml_report', _fake_xml_report)
def test_start_coverage(self):
# Start coverage data collection.
@@ -1511,7 +1509,7 @@ class CloudPipeSampleJsonTest(ApiSampleTestBase):
return {'vpn_public_address': '127.0.0.1',
'vpn_public_port': 22}
- self.stubs.Set(CloudPipe, 'get_encoded_zip', get_user_data)
+ self.stubs.Set(pipelib.CloudPipe, 'get_encoded_zip', get_user_data)
self.stubs.Set(network_manager.NetworkManager, "get_network",
network_api_get)
@@ -2589,3 +2587,183 @@ class CellsSampleJsonTest(ApiSampleTestBase):
class CellsSampleXmlTest(CellsSampleJsonTest):
ctype = 'xml'
+
+
+class BareMetalNodesJsonTest(ApiSampleTestBase, bm_db_base.BMDBTestCase):
+ extension_name = ('nova.api.openstack.compute.contrib.baremetal_nodes.'
+ 'Baremetal_nodes')
+
+ def _create_node(self):
+ response = self._do_post("os-baremetal-nodes",
+ "baremetal-node-create-req",
+ {})
+ self.assertEqual(response.status, 200)
+ subs = {'node_id': '(?P<id>\d+)'}
+ return self._verify_response("baremetal-node-create-resp",
+ subs, response)
+
+ def test_create_node(self):
+ self._create_node()
+
+ def test_list_nodes(self):
+ node_id = self._create_node()
+ interface_id = self._add_interface(node_id)
+ response = self._do_get('os-baremetal-nodes')
+ self.assertEqual(response.status, 200)
+ subs = {'node_id': node_id,
+ 'interface_id': interface_id,
+ 'address': 'aa:aa:aa:aa:aa:aa',
+ }
+ return self._verify_response('baremetal-node-list-resp',
+ subs, response)
+
+ def test_show_node(self):
+ node_id = self._create_node()
+ interface_id = self._add_interface(node_id)
+ response = self._do_get('os-baremetal-nodes/%s' % node_id)
+ self.assertEqual(response.status, 200)
+ subs = {'node_id': node_id,
+ 'interface_id': interface_id,
+ 'address': 'aa:aa:aa:aa:aa:aa',
+ }
+ return self._verify_response('baremetal-node-show-resp',
+ subs, response)
+
+ def test_delete_node(self):
+ node_id = self._create_node()
+ response = self._do_delete("os-baremetal-nodes/%s" % node_id)
+ self.assertEqual(response.status, 202)
+
+ def _add_interface(self, node_id):
+ response = self._do_post("os-baremetal-nodes/%s/action" % node_id,
+ "baremetal-node-add-interface-req",
+ {'address': 'aa:aa:aa:aa:aa:aa'})
+ self.assertEqual(response.status, 200)
+ subs = {'interface_id': r'(?P<id>\d+)'}
+ return self._verify_response("baremetal-node-add-interface-resp",
+ subs, response)
+
+ def test_add_interface(self):
+ node_id = self._create_node()
+ self._add_interface(node_id)
+
+ def test_remove_interface(self):
+ node_id = self._create_node()
+ self._add_interface(node_id)
+ response = self._do_post("os-baremetal-nodes/%s/action" % node_id,
+ "baremetal-node-remove-interface-req",
+ {'address': 'aa:aa:aa:aa:aa:aa'})
+ self.assertEqual(response.status, 202)
+ self.assertEqual(response.read(), "")
+
+
+class BareMetalNodesXmlTest(BareMetalNodesJsonTest):
+ ctype = 'xml'
+
+
+class FloatingIPPoolsSampleJsonTests(ApiSampleTestBase):
+ extension_name = ("nova.api.openstack.compute.contrib.floating_ip_pools."
+ "Floating_ip_pools")
+
+ def test_list_floatingippools(self):
+ pool_list = ["pool1", "pool2"]
+
+ def fake_get_floating_ip_pools(self, context):
+ return [{'name': pool_list[0]},
+ {'name': pool_list[1]}]
+
+ self.stubs.Set(network_api.API, "get_floating_ip_pools",
+ fake_get_floating_ip_pools)
+ response = self._do_get('os-floating-ip-pools')
+ self.assertEqual(response.status, 200)
+ subs = {
+ 'pool1': pool_list[0],
+ 'pool2': pool_list[1]
+ }
+ return self._verify_response('floatingippools-list-resp',
+ subs, response)
+
+
+class FloatingIPPoolsSampleXmlTests(FloatingIPPoolsSampleJsonTests):
+ ctype = "xml"
+
+
+class InstanceUsageAuditLogJsonTest(ApiSampleTestBase):
+ extension_name = ("nova.api.openstack.compute.contrib."
+ "instance_usage_audit_log.Instance_usage_audit_log")
+
+ def test_show_instance_usage_audit_log(self):
+ response = self._do_get('os-instance_usage_audit_log/%s' %
+ urllib.quote('2012-07-05 10:00:00'))
+ self.assertEqual(response.status, 200)
+ subs = self._get_regexes()
+ subs['hostid'] = '[a-f0-9]+'
+ return self._verify_response('inst-usage-audit-log-show-get-resp',
+ subs, response)
+
+ def test_index_instance_usage_audit_log(self):
+ response = self._do_get('os-instance_usage_audit_log')
+ self.assertEqual(response.status, 200)
+ subs = self._get_regexes()
+ subs['hostid'] = '[a-f0-9]+'
+ return self._verify_response('inst-usage-audit-log-index-get-resp',
+ subs, response)
+
+
+class InstanceUsageAuditLogXmlTest(InstanceUsageAuditLogJsonTest):
+ ctype = "xml"
+
+
+class FlavorExtraSpecsSampleJsonTests(ApiSampleTestBase):
+ extension_name = ("nova.api.openstack.compute.contrib.flavorextraspecs."
+ "Flavorextraspecs")
+
+ def _flavor_extra_specs_create(self):
+ subs = {'value1': 'value1',
+ 'value2': 'value2'
+ }
+ response = self._do_post('flavors/1/os-extra_specs',
+ 'flavor-extra-specs-create-req', subs)
+ self.assertEqual(response.status, 200)
+ return self._verify_response('flavor-extra-specs-create-resp',
+ subs, response)
+
+ def test_flavor_extra_specs_get(self):
+ subs = {'value1': 'value1'}
+ self._flavor_extra_specs_create()
+ response = self._do_get('flavors/1/os-extra_specs/key1')
+ self.assertEqual(response.status, 200)
+ return self._verify_response('flavor-extra-specs-get-resp',
+ subs, response)
+
+ def test_flavor_extra_specs_list(self):
+ subs = {'value1': 'value1',
+ 'value2': 'value2'
+ }
+ self._flavor_extra_specs_create()
+ response = self._do_get('flavors/1/os-extra_specs')
+ self.assertEqual(response.status, 200)
+ return self._verify_response('flavor-extra-specs-list-resp',
+ subs, response)
+
+ def test_flavor_extra_specs_create(self):
+ return self._flavor_extra_specs_create()
+
+ def test_flavor_extra_specs_update(self):
+ subs = {'value1': 'new_value1'}
+ self._flavor_extra_specs_create()
+ response = self._do_put('flavors/1/os-extra_specs/key1',
+ 'flavor-extra-specs-update-req', subs)
+ self.assertEqual(response.status, 200)
+ return self._verify_response('flavor-extra-specs-update-resp',
+ subs, response)
+
+ def test_flavor_extra_specs_delete(self):
+ self._flavor_extra_specs_create()
+ response = self._do_delete('flavors/1/os-extra_specs/key1')
+ self.assertEqual(response.status, 200)
+ self.assertEqual(response.read(), '')
+
+
+class FlavorExtraSpecsSampleXmlTests(FlavorExtraSpecsSampleJsonTests):
+ ctype = 'xml'
diff --git a/nova/tests/integrated/test_multiprocess_api.py b/nova/tests/integrated/test_multiprocess_api.py
index 5a82e0033..ae4fcc32f 100644
--- a/nova/tests/integrated/test_multiprocess_api.py
+++ b/nova/tests/integrated/test_multiprocess_api.py
@@ -63,7 +63,7 @@ class MultiprocessWSGITest(integrated_helpers._IntegratedTestBase):
try:
traceback.print_exc()
except BaseException:
- print "Couldn't print traceback"
+ LOG.error("Couldn't print traceback")
status = 2
# Really exit
@@ -150,7 +150,7 @@ class MultiprocessWSGITest(integrated_helpers._IntegratedTestBase):
workers = self._get_workers()
LOG.info('workers: %r' % workers)
- self.assertFalse(workers, 'No OS processes left.')
+ self.assertFalse(workers, 'OS processes left %r' % workers)
def test_terminate_sigkill(self):
self._terminate_with_signal(signal.SIGKILL)
diff --git a/nova/tests/network/test_api.py b/nova/tests/network/test_api.py
index 959c5a472..a0179ff94 100644
--- a/nova/tests/network/test_api.py
+++ b/nova/tests/network/test_api.py
@@ -25,14 +25,40 @@ import mox
from nova import context
from nova import exception
from nova import network
+from nova.network import api
from nova.network import rpcapi as network_rpcapi
from nova.openstack.common import rpc
+from nova import policy
from nova import test
FAKE_UUID = 'a47ae74e-ab08-547f-9eee-ffd23fc46c16'
+class NetworkPolicyTestCase(test.TestCase):
+ def setUp(self):
+ super(NetworkPolicyTestCase, self).setUp()
+
+ policy.reset()
+ policy.init()
+
+ self.context = context.get_admin_context()
+
+ def tearDown(self):
+ super(NetworkPolicyTestCase, self).tearDown()
+ policy.reset()
+
+ def test_check_policy(self):
+ self.mox.StubOutWithMock(policy, 'enforce')
+ target = {
+ 'project_id': self.context.project_id,
+ 'user_id': self.context.user_id,
+ }
+ policy.enforce(self.context, 'network:get_all', target)
+ self.mox.ReplayAll()
+ api.check_policy(self.context, 'get_all')
+
+
class ApiTestCase(test.TestCase):
def setUp(self):
super(ApiTestCase, self).setUp()
@@ -57,7 +83,7 @@ class ApiTestCase(test.TestCase):
instance = dict(id='id', uuid='uuid', project_id='project_id',
host='host', instance_type={'rxtx_factor': 0})
self.network_api.allocate_for_instance(
- 'context', instance, 'vpn', 'requested_networks', macs=macs)
+ self.context, instance, 'vpn', 'requested_networks', macs=macs)
def _do_test_associate_floating_ip(self, orig_instance_uuid):
"""Test post-association logic."""
diff --git a/nova/tests/network/test_linux_net.py b/nova/tests/network/test_linux_net.py
index 8a7865b83..3c219f5f4 100644
--- a/nova/tests/network/test_linux_net.py
+++ b/nova/tests/network/test_linux_net.py
@@ -461,14 +461,14 @@ class LinuxNetworkTestCase(test.TestCase):
'bridge_interface': iface}
driver.plug(network, 'fakemac')
expected = [
- ('ebtables', '-D', 'INPUT', '-p', 'ARP', '-i', iface,
- '--arp-ip-dst', dhcp, '-j', 'DROP'),
- ('ebtables', '-I', 'INPUT', '-p', 'ARP', '-i', iface,
- '--arp-ip-dst', dhcp, '-j', 'DROP'),
- ('ebtables', '-D', 'OUTPUT', '-p', 'ARP', '-o', iface,
- '--arp-ip-src', dhcp, '-j', 'DROP'),
- ('ebtables', '-I', 'OUTPUT', '-p', 'ARP', '-o', iface,
- '--arp-ip-src', dhcp, '-j', 'DROP'),
+ ('ebtables', '-t', 'filter', '-D', 'INPUT', '-p', 'ARP', '-i',
+ iface, '--arp-ip-dst', dhcp, '-j', 'DROP'),
+ ('ebtables', '-t', 'filter', '-I', 'INPUT', '-p', 'ARP', '-i',
+ iface, '--arp-ip-dst', dhcp, '-j', 'DROP'),
+ ('ebtables', '-t', 'filter', '-D', 'OUTPUT', '-p', 'ARP', '-o',
+ iface, '--arp-ip-src', dhcp, '-j', 'DROP'),
+ ('ebtables', '-t', 'filter', '-I', 'OUTPUT', '-p', 'ARP', '-o',
+ iface, '--arp-ip-src', dhcp, '-j', 'DROP'),
('iptables-save', '-c'),
('iptables-restore', '-c'),
('ip6tables-save', '-c'),
@@ -500,10 +500,10 @@ class LinuxNetworkTestCase(test.TestCase):
driver.unplug(network)
expected = [
- ('ebtables', '-D', 'INPUT', '-p', 'ARP', '-i', iface,
- '--arp-ip-dst', dhcp, '-j', 'DROP'),
- ('ebtables', '-D', 'OUTPUT', '-p', 'ARP', '-o', iface,
- '--arp-ip-src', dhcp, '-j', 'DROP'),
+ ('ebtables', '-t', 'filter', '-D', 'INPUT', '-p', 'ARP', '-i',
+ iface, '--arp-ip-dst', dhcp, '-j', 'DROP'),
+ ('ebtables', '-t', 'filter', '-D', 'OUTPUT', '-p', 'ARP', '-o',
+ iface, '--arp-ip-src', dhcp, '-j', 'DROP'),
('iptables-save', '-c'),
('iptables-restore', '-c'),
('ip6tables-save', '-c'),
diff --git a/nova/tests/network/test_manager.py b/nova/tests/network/test_manager.py
index b5b3ec107..2cc19bbb8 100644
--- a/nova/tests/network/test_manager.py
+++ b/nova/tests/network/test_manager.py
@@ -15,7 +15,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-import shutil
import fixtures
import mox
@@ -33,7 +32,6 @@ from nova.openstack.common import importutils
from nova.openstack.common import log as logging
from nova.openstack.common import rpc
from nova.openstack.common.rpc import common as rpc_common
-import nova.policy
from nova import test
from nova.tests import fake_ldap
from nova.tests import fake_network
@@ -185,6 +183,7 @@ class FlatNetworkTestCase(test.TestCase):
'vif_devname': None,
'vif_uuid':
'00000000-0000-0000-0000-00000000000000%02d' % nid,
+ 'ovs_interfaceid': None,
'should_create_vlan': False,
'should_create_bridge': False}
self.assertThat(info, matchers.DictMatches(check))
@@ -669,7 +668,7 @@ class VlanNetworkTestCase(test.TestCase):
is_admin=False)
def fake1(*args, **kwargs):
- return '10.0.0.1'
+ return {'address': '10.0.0.1', 'network': 'fakenet'}
# floating ip that's already associated
def fake2(*args, **kwargs):
@@ -789,9 +788,9 @@ class VlanNetworkTestCase(test.TestCase):
self.stubs.Set(self.network.db, 'floating_ip_get_all_by_host',
get_all_by_host)
- def fixed_ip_get(_context, fixed_ip_id):
+ def fixed_ip_get(_context, fixed_ip_id, get_network):
if fixed_ip_id == 1:
- return {'address': 'fakefixed'}
+ return {'address': 'fakefixed', 'network': 'fakenet'}
raise exception.FixedIpNotFound(id=fixed_ip_id)
self.stubs.Set(self.network.db, 'fixed_ip_get', fixed_ip_get)
@@ -799,7 +798,8 @@ class VlanNetworkTestCase(test.TestCase):
self.flags(public_interface=False)
self.network.l3driver.add_floating_ip('fakefloat',
'fakefixed',
- 'fakeiface')
+ 'fakeiface',
+ 'fakenet')
self.mox.ReplayAll()
self.network.init_host_floating_ips()
self.mox.UnsetStubs()
@@ -809,7 +809,8 @@ class VlanNetworkTestCase(test.TestCase):
self.flags(public_interface='fooiface')
self.network.l3driver.add_floating_ip('fakefloat',
'fakefixed',
- 'fooiface')
+ 'fooiface',
+ 'fakenet')
self.mox.ReplayAll()
self.network.init_host_floating_ips()
self.mox.UnsetStubs()
@@ -1804,11 +1805,13 @@ class FloatingIPTestCase(test.TestCase):
def fake_is_stale_floating_ip_address(context, floating_ip):
return floating_ip['address'] == '172.24.4.23'
- def fake_fixed_ip_get(context, fixed_ip_id):
+ def fake_fixed_ip_get(context, fixed_ip_id, get_network):
return {'instance_uuid': 'fake_uuid',
- 'address': '10.0.0.2'}
+ 'address': '10.0.0.2',
+ 'network': 'fakenet'}
- def fake_remove_floating_ip(floating_addr, fixed_addr, interface):
+ def fake_remove_floating_ip(floating_addr, fixed_addr, interface,
+ network):
called['count'] += 1
def fake_floating_ip_update(context, address, args):
@@ -1845,11 +1848,13 @@ class FloatingIPTestCase(test.TestCase):
def fake_is_stale_floating_ip_address(context, floating_ip):
return floating_ip['address'] == '172.24.4.23'
- def fake_fixed_ip_get(context, fixed_ip_id):
+ def fake_fixed_ip_get(context, fixed_ip_id, get_network):
return {'instance_uuid': 'fake_uuid',
- 'address': '10.0.0.2'}
+ 'address': '10.0.0.2',
+ 'network': 'fakenet'}
- def fake_add_floating_ip(floating_addr, fixed_addr, interface):
+ def fake_add_floating_ip(floating_addr, fixed_addr, interface,
+ network):
called['count'] += 1
def fake_floating_ip_update(context, address, args):
@@ -2092,30 +2097,6 @@ class FloatingIPTestCase(test.TestCase):
self.context, 'fake-id')
-class NetworkPolicyTestCase(test.TestCase):
- def setUp(self):
- super(NetworkPolicyTestCase, self).setUp()
-
- nova.policy.reset()
- nova.policy.init()
-
- self.context = context.get_admin_context()
-
- def tearDown(self):
- super(NetworkPolicyTestCase, self).tearDown()
- nova.policy.reset()
-
- def test_check_policy(self):
- self.mox.StubOutWithMock(nova.policy, 'enforce')
- target = {
- 'project_id': self.context.project_id,
- 'user_id': self.context.user_id,
- }
- nova.policy.enforce(self.context, 'network:get_all', target)
- self.mox.ReplayAll()
- network_manager.check_policy(self.context, 'get_all')
-
-
class InstanceDNSTestCase(test.TestCase):
"""Tests nova.network.manager instance DNS."""
def setUp(self):
diff --git a/nova/tests/scheduler/test_host_filters.py b/nova/tests/scheduler/test_host_filters.py
index f8b9f9296..230e2ea03 100644
--- a/nova/tests/scheduler/test_host_filters.py
+++ b/nova/tests/scheduler/test_host_filters.py
@@ -25,7 +25,7 @@ from nova.openstack.common import jsonutils
from nova.openstack.common import timeutils
from nova.scheduler import filters
from nova.scheduler.filters import extra_specs_ops
-from nova.scheduler.filters.trusted_filter import AttestationService
+from nova.scheduler.filters import trusted_filter
from nova import servicegroup
from nova import test
from nova.tests.scheduler import fakes
@@ -242,7 +242,8 @@ class HostFiltersTestCase(test.TestCase):
self.oat_data = ''
self.oat_attested = False
self.stubs = stubout.StubOutForTesting()
- self.stubs.Set(AttestationService, '_request', self.fake_oat_request)
+ self.stubs.Set(trusted_filter.AttestationService, '_request',
+ self.fake_oat_request)
self.context = context.RequestContext('fake', 'fake')
self.json_query = jsonutils.dumps(
['and', ['>=', '$free_ram_mb', 1024],
@@ -336,6 +337,20 @@ class HostFiltersTestCase(test.TestCase):
self.assertTrue(filt_cls.host_passes(host, filter_properties))
+ def test_affinity_different_filter_handles_deleted_instance(self):
+ filt_cls = self.class_map['DifferentHostFilter']()
+ host = fakes.FakeHostState('host1', 'node1', {})
+ instance = fakes.FakeInstance(context=self.context,
+ params={'host': 'host1'})
+ instance_uuid = instance.uuid
+ db.instance_destroy(self.context, instance_uuid)
+
+ filter_properties = {'context': self.context.elevated(),
+ 'scheduler_hints': {
+ 'different_host': [instance_uuid], }}
+
+ self.assertTrue(filt_cls.host_passes(host, filter_properties))
+
def test_affinity_same_filter_no_list_passes(self):
filt_cls = self.class_map['SameHostFilter']()
host = fakes.FakeHostState('host1', 'node1', {})
@@ -387,6 +402,20 @@ class HostFiltersTestCase(test.TestCase):
self.assertTrue(filt_cls.host_passes(host, filter_properties))
+ def test_affinity_same_filter_handles_deleted_instance(self):
+ filt_cls = self.class_map['SameHostFilter']()
+ host = fakes.FakeHostState('host1', 'node1', {})
+ instance = fakes.FakeInstance(context=self.context,
+ params={'host': 'host1'})
+ instance_uuid = instance.uuid
+ db.instance_destroy(self.context, instance_uuid)
+
+ filter_properties = {'context': self.context.elevated(),
+ 'scheduler_hints': {
+ 'same_host': [instance_uuid], }}
+
+ self.assertFalse(filt_cls.host_passes(host, filter_properties))
+
def test_affinity_simple_cidr_filter_passes(self):
filt_cls = self.class_map['SimpleCIDRAffinityFilter']()
host = fakes.FakeHostState('host1', 'node1', {})
diff --git a/nova/tests/ssl_cert/ca.crt b/nova/tests/ssl_cert/ca.crt
new file mode 100644
index 000000000..9d66ca627
--- /dev/null
+++ b/nova/tests/ssl_cert/ca.crt
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGDDCCA/SgAwIBAgIJAPSvwQYk4qI4MA0GCSqGSIb3DQEBBQUAMGExCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMRUwEwYDVQQKEwxPcGVuc3RhY2sg
+Q0ExEjAQBgNVBAsTCUdsYW5jZSBDQTESMBAGA1UEAxMJR2xhbmNlIENBMB4XDTEy
+MDIwOTE3MTAwMloXDTIyMDIwNjE3MTAwMlowYTELMAkGA1UEBhMCQVUxEzARBgNV
+BAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDE9wZW5zdGFjayBDQTESMBAGA1UECxMJ
+R2xhbmNlIENBMRIwEAYDVQQDEwlHbGFuY2UgQ0EwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQDmf+fapWfzy1Uylus0KGalw4X/5xZ+ltPVOr+IdCPbstvi
+RTC5g+O+TvXeOP32V/cnSY4ho/+f2q730za+ZA/cgWO252rcm3Q7KTJn3PoqzJvX
+/l3EXe3/TCrbzgZ7lW3QLTCTEE2eEzwYG3wfDTOyoBq+F6ct6ADh+86gmpbIRfYI
+N+ixB0hVyz9427PTof97fL7qxxkjAayB28OfwHrkEBl7iblNhUC0RoH+/H9r5GEl
+GnWiebxfNrONEHug6PHgiaGq7/Dj+u9bwr7J3/NoS84I08ajMnhlPZxZ8bS/O8If
+ceWGZv7clPozyhABT/otDfgVcNH1UdZ4zLlQwc1MuPYN7CwxrElxc8Quf94ttGjb
+tfGTl4RTXkDofYdG1qBWW962PsGl2tWmbYDXV0q5JhV/IwbrE1X9f+OksJQne1/+
+dZDxMhdf2Q1V0P9hZZICu4+YhmTMs5Mc9myKVnzp4NYdX5fXoB/uNYph+G7xG5IK
+WLSODKhr1wFGTTcuaa8LhOH5UREVenGDJuc6DdgX9a9PzyJGIi2ngQ03TJIkCiU/
+4J/r/vsm81ezDiYZSp2j5JbME+ixW0GBLTUWpOIxUSHgUFwH5f7lQwbXWBOgwXQk
+BwpZTmdQx09MfalhBtWeu4/6BnOCOj7e/4+4J0eVxXST0AmVyv8YjJ2nz1F9oQID
+AQABo4HGMIHDMB0GA1UdDgQWBBTk7Krj4bEsTjHXaWEtI2GZ5ACQyTCBkwYDVR0j
+BIGLMIGIgBTk7Krj4bEsTjHXaWEtI2GZ5ACQyaFlpGMwYTELMAkGA1UEBhMCQVUx
+EzARBgNVBAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDE9wZW5zdGFjayBDQTESMBAG
+A1UECxMJR2xhbmNlIENBMRIwEAYDVQQDEwlHbGFuY2UgQ0GCCQD0r8EGJOKiODAM
+BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQA8Zrss/MiwFHGmDlercE0h
+UvzA54n/EvKP9nP3jHM2qW/VPfKdnFw99nEPFLhb+lN553vdjOpCYFm+sW0Z5Mi4
+qsFkk4AmXIIEFOPt6zKxMioLYDQ9Sw/BUv6EZGeANWr/bhmaE+dMcKJt5le/0jJm
+2ahsVB9fbFu9jBFeYb7Ba/x2aLkEGMxaDLla+6EQhj148fTnS1wjmX9G2cNzJvj/
++C2EfKJIuDJDqw2oS2FGVpP37FA2Bz2vga0QatNneLkGKCFI3ZTenBznoN+fmurX
+TL3eJE4IFNrANCcdfMpdyLAtXz4KpjcehqpZMu70er3d30zbi1l0Ajz4dU+WKz/a
+NQES+vMkT2wqjXHVTjrNwodxw3oLK/EuTgwoxIHJuplx5E5Wrdx9g7Gl1PBIJL8V
+xiOYS5N7CakyALvdhP7cPubA2+TPAjNInxiAcmhdASS/Vrmpvrkat6XhGn8h9liv
+ysDOpMQmYQkmgZBpW8yBKK7JABGGsJADJ3E6J5MMWBX2RR4kFoqVGAzdOU3oyaTy
+I0kz5sfuahaWpdYJVlkO+esc0CRXw8fLDYivabK2tOgUEWeZsZGZ9uK6aV1VxTAY
+9Guu3BJ4Rv/KP/hk7mP8rIeCwotV66/2H8nq72ImQhzSVyWcxbFf2rJiFQJ3BFwA
+WoRMgEwjGJWqzhJZUYpUAQ==
+-----END CERTIFICATE-----
diff --git a/nova/tests/ssl_cert/certificate.crt b/nova/tests/ssl_cert/certificate.crt
new file mode 100644
index 000000000..3c1aa6363
--- /dev/null
+++ b/nova/tests/ssl_cert/certificate.crt
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIFLjCCAxYCAQEwDQYJKoZIhvcNAQEFBQAwYTELMAkGA1UEBhMCQVUxEzARBgNV
+BAgTClNvbWUtU3RhdGUxFTATBgNVBAoTDE9wZW5zdGFjayBDQTESMBAGA1UECxMJ
+R2xhbmNlIENBMRIwEAYDVQQDEwlHbGFuY2UgQ0EwHhcNMTIwMjA5MTcxMDUzWhcN
+MjIwMjA2MTcxMDUzWjBZMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0
+ZTESMBAGA1UEChMJT3BlbnN0YWNrMQ8wDQYDVQQLEwZHbGFuY2UxEDAOBgNVBAMT
+BzAuMC4wLjAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXpUkQN6pu
+avo+gz3o1K4krVdPl1m7NjNJDyD/+ZH0EGNcEN7iag1qPE7JsjqGPNZsQK1dMoXb
+Sz+OSi9qvNeJnBcfwUx5qTAtwyAb9AxGkwuMafIU+lWbsclo+dPGsja01ywbXTCZ
+bF32iqnpOMYhfxWUdoQYiBkhxxhW9eMPKLS/KkP8/bx+Vaa2XJiAebqkd9nrksAA
+BeGc9mlafYBEmiChPdJEPw+1ePA4QVq9aPepDsqAKtGN8JLpmoC3BdxQQTbbwL3Q
+8fTXK4tCNUaVk4AbDy/McFq6y0ocQoBPJjihOY35mWG/OLtcI99yPOpWGnps/5aG
+/64DDJ2D67Fnaj6gKHV+6TXFO8KZxlnxtgtiZDJBZkneTBt9ArSOv+l6NBsumRz0
+iEJ4o4H1S2TSMnprAvX7WnGtc6Xi9gXahYcDHEelwwYzqAiTBv6hxSp4MZ2dNXa+
+KzOitC7ZbV2qsg0au0wjfE/oSQ3NvsvUr8nOmfutJTvHRAwbC1v4G/tuAsO7O0w2
+0u2B3u+pG06m5+rnEqp+rB9hmukRYTfgEFRRsVIvpFl/cwvPXKRcX03UIMx+lLr9
+Ft+ep7YooBhY3wY2kwCxD4lRYNmbwsCIVywZt40f/4ad98TkufR9NhsfycxGeqbr
+mTMFlZ8TTlmP82iohekKCOvoyEuTIWL2+wIDAQABMA0GCSqGSIb3DQEBBQUAA4IC
+AQBMUBgV0R+Qltf4Du7u/8IFmGAoKR/mktB7R1gRRAqsvecUt7kIwBexGdavGg1y
+0pU0+lgUZjJ20N1SlPD8gkNHfXE1fL6fmMjWz4dtYJjzRVhpufHPeBW4tl8DgHPN
+rBGAYQ+drDSXaEjiPQifuzKx8WS+DGA3ki4co5mPjVnVH1xvLIdFsk89z3b3YD1k
+yCJ/a9K36x6Z/c67JK7s6MWtrdRF9+MVnRKJ2PK4xznd1kBz16V+RA466wBDdARY
+vFbtkafbEqOb96QTonIZB7+fAldKDPZYnwPqasreLmaGOaM8sxtlPYAJ5bjDONbc
+AaXG8BMRQyO4FyH237otDKlxPyHOFV66BaffF5S8OlwIMiZoIvq+IcTZOdtDUSW2
+KHNLfe5QEDZdKjWCBrfqAfvNuG13m03WqfmcMHl3o/KiPJlx8l9Z4QEzZ9xcyQGL
+cncgeHM9wJtzi2cD/rTDNFsx/gxvoyutRmno7I3NRbKmpsXF4StZioU3USRspB07
+hYXOVnG3pS+PjVby7ThT3gvFHSocguOsxClx1epdUJAmJUbmM7NmOp5WVBVtMtC2
+Su4NG/xJciXitKzw+btb7C7RjO6OEqv/1X/oBDzKBWQAwxUC+lqmnM7W6oqWJFEM
+YfTLnrjs7Hj6ThMGcEnfvc46dWK3dz0RjsQzUxugPuEkLA==
+-----END CERTIFICATE-----
diff --git a/nova/tests/ssl_cert/privatekey.key b/nova/tests/ssl_cert/privatekey.key
new file mode 100644
index 000000000..b63df3d29
--- /dev/null
+++ b/nova/tests/ssl_cert/privatekey.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEA16VJEDeqbmr6PoM96NSuJK1XT5dZuzYzSQ8g//mR9BBjXBDe
+4moNajxOybI6hjzWbECtXTKF20s/jkovarzXiZwXH8FMeakwLcMgG/QMRpMLjGny
+FPpVm7HJaPnTxrI2tNcsG10wmWxd9oqp6TjGIX8VlHaEGIgZIccYVvXjDyi0vypD
+/P28flWmtlyYgHm6pHfZ65LAAAXhnPZpWn2ARJogoT3SRD8PtXjwOEFavWj3qQ7K
+gCrRjfCS6ZqAtwXcUEE228C90PH01yuLQjVGlZOAGw8vzHBaustKHEKATyY4oTmN
++Zlhvzi7XCPfcjzqVhp6bP+Whv+uAwydg+uxZ2o+oCh1fuk1xTvCmcZZ8bYLYmQy
+QWZJ3kwbfQK0jr/pejQbLpkc9IhCeKOB9Utk0jJ6awL1+1pxrXOl4vYF2oWHAxxH
+pcMGM6gIkwb+ocUqeDGdnTV2viszorQu2W1dqrINGrtMI3xP6EkNzb7L1K/Jzpn7
+rSU7x0QMGwtb+Bv7bgLDuztMNtLtgd7vqRtOpufq5xKqfqwfYZrpEWE34BBUUbFS
+L6RZf3MLz1ykXF9N1CDMfpS6/Rbfnqe2KKAYWN8GNpMAsQ+JUWDZm8LAiFcsGbeN
+H/+GnffE5Ln0fTYbH8nMRnqm65kzBZWfE05Zj/NoqIXpCgjr6MhLkyFi9vsCAwEA
+AQKCAgAA96baQcWr9SLmQOR4NOwLEhQAMWefpWCZhU3amB4FgEVR1mmJjnw868RW
+t0v36jH0Dl44us9K6o2Ab+jCi9JTtbWM2Osk6JNkwSlVtsSPVH2KxbbmTTExH50N
+sYE3tPj12rlB7isXpRrOzlRwzWZmJBHOtrFlAsdKFYCQc03vdXlKGkBv1BuSXYP/
+8W5ltSYXMspxehkOZvhaIejbFREMPbzDvGlDER1a7Q320qQ7kUr7ISvbY1XJUzj1
+f1HwgEA6w/AhED5Jv6wfgvx+8Yo9hYnflTPbsO1XRS4x7kJxGHTMlFuEsSF1ICYH
+Bcos0wUiGcBO2N6uAFuhe98BBn+nOwAPZYWwGkmVuK2psm2mXAHx94GT/XqgK/1r
+VWGSoOV7Fhjauc2Nv8/vJU18DXT3OY5hc4iXVeEBkuZwRb/NVUtnFoHxVO/Mp5Fh
+/W5KZaLWVrLghzvSQ/KUIM0k4lfKDZpY9ZpOdNgWDyZY8tNrXumUZZimzWdXZ9vR
+dBssmd8qEKs1AHGFnMDt56IjLGou6j0qnWsLdR1e/WEFsYzGXLVHCv6vXRNkbjqh
+WFw5nA+2Dw1YAsy+YkTfgx2pOe+exM/wxsVPa7tG9oZ374dywUi1k6VoHw5dkmJw
+1hbXqSLZtx2N51G+SpGmNAV4vLUF0y3dy2wnrzFkFT4uxh1w8QKCAQEA+h6LwHTK
+hgcJx6CQQ6zYRqXo4wdvMooY1FcqJOq7LvJUA2CX5OOLs8qN1TyFrOCuAUTurOrM
+ABlQ0FpsIaP8TOGz72dHe2eLB+dD6Bqjn10sEFMn54zWd/w9ympQrO9jb5X3ViTh
+sCcdYyXVS9Hz8nzbbIF+DaKlxF2Hh71uRDxXpMPxRcGbOIuKZXUj6RkTIulzqT6o
+uawlegWxch05QSgzq/1ASxtjTzo4iuDCAii3N45xqxnB+fV9NXEt4R2oOGquBRPJ
+LxKcOnaQKBD0YNX4muTq+zPlv/kOb8/ys2WGWDUrNkpyJXqhTve4KONjqM7+iL/U
+4WdJuiCjonzk/QKCAQEA3Lc+kNq35FNLxMcnCVcUgkmiCWZ4dyGZZPdqjOPww1+n
+bbudGPzY1nxOvE60dZM4or/tm6qlXYfb2UU3+OOJrK9s297EQybZ8DTZu2GHyitc
+NSFV3Gl4cgvKdbieGKkk9X2dV9xSNesNvX9lJEnQxuwHDTeo8ubLHtV88Ml1xokn
+7W+IFiyEuUIL4e5/fadbrI3EwMrbCF4+9VcfABx4PTNMzdc8LsncCMXE+jFX8AWp
+TsT2JezTe5o2WpvBoKMAYhJQNQiaWATn00pDVY/70H1vK3ljomAa1IUdOr/AhAF7
+3jL0MYMgXSHzXZOKAtc7yf+QfFWF1Ls8+sen1clJVwKCAQEAp59rB0r+Iz56RmgL
+5t7ifs5XujbURemY5E2aN+18DuVmenD0uvfoO1DnJt4NtCNLWhxpXEdq+jH9H/VJ
+fG4a+ydT4IC1vjVRTrWlo9qeh4H4suQX3S1c2kKY4pvHf25blH/Lp9bFzbkZD8Ze
+IRcOxxb4MsrBwL+dGnGYD9dbG63ZCtoqSxaKQSX7VS1hKKmeUopj8ivFBdIht5oz
+JogBQ/J+Vqg9u1gagRFCrYgdXTcOOtRix0lW336vL+6u0ax/fXe5MjvlW3+8Zc3p
+pIBgVrlvh9ccx8crFTIDg9m4DJRgqaLQV+0ifI2np3WK3RQvSQWYPetZ7sm69ltD
+bvUGvQKCAQAz5CEhjUqOs8asjOXwnDiGKSmfbCgGWi/mPQUf+rcwN9z1P5a/uTKB
+utgIDbj/q401Nkp2vrgCNV7KxitSqKxFnTjKuKUL5KZ4gvRtyZBTR751/1BgcauP
+pJYE91K0GZBG5zGG5pWtd4XTd5Af5/rdycAeq2ddNEWtCiRFuBeohbaNbBtimzTZ
+GV4R0DDJKf+zoeEQMqEsZnwG0mTHceoS+WylOGU92teQeG7HI7K5C5uymTwFzpgq
+ByegRd5QFgKRDB0vWsZuyzh1xI/wHdnmOpdYcUGre0zTijhFB7ALWQ32P6SJv3ps
+av78kSNxZ4j3BM7DbJf6W8sKasZazOghAoIBAHekpBcLq9gRv2+NfLYxWN2sTZVB
+1ldwioG7rWvk5YQR2akukecI3NRjtC5gG2vverawG852Y4+oLfgRMHxgp0qNStwX
+juTykzPkCwZn8AyR+avC3mkrtJyM3IigcYOu4/UoaRDFa0xvCC1EfumpnKXIpHag
+miSQZf2sVbgqb3/LWvHIg/ceOP9oGJve87/HVfQtBoLaIe5RXCWkqB7mcI/exvTS
+8ShaW6v2Fe5Bzdvawj7sbsVYRWe93Aq2tmIgSX320D2RVepb6mjD4nr0IUaM3Yed
+TFT7e2ikWXyDLLgVkDTU4Qe8fr3ZKGfanCIDzvgNw6H1gRi+2WQgOmjilMQ=
+-----END RSA PRIVATE KEY-----
diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py
index fb2e76e45..11c16d6dd 100644
--- a/nova/tests/test_api.py
+++ b/nova/tests/test_api.py
@@ -26,9 +26,9 @@ from boto.ec2 import regioninfo
from boto import exception as boto_exc
# newer versions of boto use their own wrapper on top of httplib.HTTPResponse
try:
- from boto.connection import HTTPResponse
+ import boto.connection as httplib
except ImportError:
- from httplib import HTTPResponse
+ import httplib
import fixtures
import webob
@@ -79,7 +79,7 @@ class FakeHttplibConnection(object):
# guess that's a function the web server usually provides.
resp = "HTTP/1.0 %s" % resp
self.sock = FakeHttplibSocket(resp)
- self.http_response = HTTPResponse(self.sock)
+ self.http_response = httplib.HTTPResponse(self.sock)
# NOTE(vish): boto is accessing private variables for some reason
self._HTTPConnection__response = self.http_response
self.http_response.begin()
diff --git a/nova/tests/test_availability_zones.py b/nova/tests/test_availability_zones.py
index 2c5c06921..4192fa08f 100644
--- a/nova/tests/test_availability_zones.py
+++ b/nova/tests/test_availability_zones.py
@@ -23,7 +23,6 @@ from nova import availability_zones as az
from nova import context
from nova import db
from nova.openstack.common import cfg
-from nova import service
from nova import test
CONF = cfg.CONF
diff --git a/nova/tests/test_bdm.py b/nova/tests/test_bdm.py
index 4d62d6bbf..43ca4d7b0 100644
--- a/nova/tests/test_bdm.py
+++ b/nova/tests/test_bdm.py
@@ -246,6 +246,5 @@ class BlockDeviceMappingEc2CloudTestCase(test.TestCase):
result = {}
cloud._format_mappings(properties, result)
- print result
self.assertEqual(result['blockDeviceMapping'].sort(),
expected_result['blockDeviceMapping'].sort())
diff --git a/nova/tests/test_context.py b/nova/tests/test_context.py
index 0915bf157..527534fd5 100644
--- a/nova/tests/test_context.py
+++ b/nova/tests/test_context.py
@@ -74,3 +74,22 @@ class ContextTestCase(test.TestCase):
self.assertTrue(c)
self.assertIn("'extra_arg1': 'meow'", info['log_msg'])
self.assertIn("'extra_arg2': 'wuff'", info['log_msg'])
+
+ def test_service_catalog_default(self):
+ ctxt = context.RequestContext('111', '222')
+ self.assertEquals(ctxt.service_catalog, [])
+
+ def test_service_catalog_cinder_only(self):
+ service_catalog = [
+ {u'type': u'compute', u'name': u'nova'},
+ {u'type': u's3', u'name': u's3'},
+ {u'type': u'image', u'name': u'glance'},
+ {u'type': u'volume', u'name': u'cinder'},
+ {u'type': u'ec2', u'name': u'ec2'},
+ {u'type': u'object-store', u'name': u'swift'},
+ {u'type': u'identity', u'name': u'keystone'}]
+
+ volume_catalog = [{u'type': u'volume', u'name': u'cinder'}]
+ ctxt = context.RequestContext('111', '222',
+ service_catalog=service_catalog)
+ self.assertEquals(ctxt.service_catalog, volume_catalog)
diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py
index e43a32c19..684f9fded 100644
--- a/nova/tests/test_db_api.py
+++ b/nova/tests/test_db_api.py
@@ -253,11 +253,11 @@ class DbApiTestCase(test.TestCase):
values = {'address': 'fixed'}
fixed = db.fixed_ip_create(ctxt, values)
res = db.floating_ip_fixed_ip_associate(ctxt, floating, fixed, 'foo')
- self.assertEqual(res, fixed)
+ self.assertEqual(res['address'], fixed)
res = db.floating_ip_fixed_ip_associate(ctxt, floating, fixed, 'foo')
self.assertEqual(res, None)
res = db.floating_ip_disassociate(ctxt, floating)
- self.assertEqual(res, fixed)
+ self.assertEqual(res['address'], fixed)
res = db.floating_ip_disassociate(ctxt, floating)
self.assertEqual(res, None)
@@ -1510,6 +1510,25 @@ class MigrationTestCase(test.TestCase):
self.assertEqual(migration['instance_uuid'], instance['uuid'])
+class TestFixedIPGetByNetworkHost(test.TestCase):
+ def test_not_found_exception(self):
+ ctxt = context.get_admin_context()
+
+ self.assertRaises(
+ exception.FixedIpNotFoundForNetworkHost,
+ db.fixed_ip_get_by_network_host,
+ ctxt, 1, 'ignore')
+
+ def test_fixed_ip_found(self):
+ ctxt = context.get_admin_context()
+ db.fixed_ip_create(ctxt, dict(network_id=1, host='host'))
+
+ fip = db.fixed_ip_get_by_network_host(ctxt, 1, 'host')
+
+ self.assertEquals(1, fip['network_id'])
+ self.assertEquals('host', fip['host'])
+
+
class TestIpAllocation(test.TestCase):
def setUp(self):
@@ -1671,3 +1690,47 @@ class VolumeUsageDBApiTestCase(test.TestCase):
for key, value in expected_vol_usages.items():
self.assertEqual(vol_usages[0][key], value)
timeutils.clear_time_override()
+
+
+class TaskLogTestCase(test.TestCase):
+
+ def setUp(self):
+ super(TaskLogTestCase, self).setUp()
+ self.context = context.get_admin_context()
+ now = timeutils.utcnow()
+ self.begin = now - datetime.timedelta(seconds=10)
+ self.end = now - datetime.timedelta(seconds=5)
+ self.task_name = 'fake-task-name'
+ self.host = 'fake-host'
+ self.message = 'Fake task message'
+ db.task_log_begin_task(self.context, self.task_name, self.begin,
+ self.end, self.host, message=self.message)
+
+ def test_task_log_get(self):
+ result = db.task_log_get(self.context, self.task_name, self.begin,
+ self.end, self.host)
+ self.assertEqual(result['task_name'], self.task_name)
+ self.assertEqual(result['period_beginning'], self.begin)
+ self.assertEqual(result['period_ending'], self.end)
+ self.assertEqual(result['host'], self.host)
+ self.assertEqual(result['message'], self.message)
+
+ def test_task_log_get_all(self):
+ result = db.task_log_get_all(self.context, self.task_name, self.begin,
+ self.end, host=self.host)
+ self.assertEqual(len(result), 1)
+
+ def test_task_log_begin_task(self):
+ db.task_log_begin_task(self.context, 'fake', self.begin,
+ self.end, self.host, message=self.message)
+ result = db.task_log_get(self.context, 'fake', self.begin,
+ self.end, self.host)
+ self.assertEqual(result['task_name'], 'fake')
+
+ def test_task_log_end_task(self):
+ errors = 1
+ db.task_log_end_task(self.context, self.task_name, self.begin,
+ self.end, self.host, errors, message=self.message)
+ result = db.task_log_get(self.context, self.task_name, self.begin,
+ self.end, self.host)
+ self.assertEqual(result['errors'], 1)
diff --git a/nova/tests/test_hypervapi.py b/nova/tests/test_hypervapi.py
index 9fec9d151..3b624e6d1 100644
--- a/nova/tests/test_hypervapi.py
+++ b/nova/tests/test_hypervapi.py
@@ -18,37 +18,53 @@
Test suite for the Hyper-V driver and related APIs.
"""
-import json
+import io
+import mox
import os
import platform
import shutil
-import sys
+import time
import uuid
+from nova.api.metadata import base as instance_metadata
from nova.compute import power_state
from nova.compute import task_states
from nova import context
from nova import db
from nova.image import glance
from nova.openstack.common import cfg
+from nova import test
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.tests.hyperv import fake
+from nova.tests.image import fake as fake_image
from nova.tests import matchers
+from nova import utils
+from nova.virt import configdrive
+from nova.virt.hyperv import basevolumeutils
from nova.virt.hyperv import constants
from nova.virt.hyperv import driver as driver_hyperv
+from nova.virt.hyperv import hostutils
+from nova.virt.hyperv import livemigrationutils
+from nova.virt.hyperv import networkutils
+from nova.virt.hyperv import pathutils
+from nova.virt.hyperv import vhdutils
from nova.virt.hyperv import vmutils
+from nova.virt.hyperv import volumeutils
+from nova.virt.hyperv import volumeutilsv2
from nova.virt import images
CONF = cfg.CONF
+CONF.import_opt('vswitch_name', 'nova.virt.hyperv.vif')
-class HyperVAPITestCase(basetestcase.BaseTestCase):
+class HyperVAPITestCase(test.TestCase):
"""Unit tests for Hyper-V driver calls."""
+ def __init__(self, test_case_name):
+ self._mox = mox.Mox()
+ super(HyperVAPITestCase, self).__init__(test_case_name)
+
def setUp(self):
super(HyperVAPITestCase, self).setUp()
@@ -56,22 +72,22 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
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 = 'testtargetportal:3260'
- self._volume_id = '8957e088-dbee-4216-8056-978353a3e737'
+ self._volume_id = '0ef5d708-45ab-4129-8c59-d774d2837eb7'
self._context = context.RequestContext(self._user_id, self._project_id)
+ self._instance_ide_disks = []
+ self._instance_ide_dvds = []
+ self._instance_volume_disks = []
self._setup_stubs()
self.flags(instances_path=r'C:\Hyper-V\test\instances',
vswitch_name='external',
- network_api_class='nova.network.quantumv2.api.API')
+ network_api_class='nova.network.quantumv2.api.API',
+ force_volumeutils_v1=True)
- self._hypervutils = hypervutils.HyperVUtils()
self._conn = driver_hyperv.HyperVDriver(None)
def _setup_stubs(self):
@@ -79,14 +95,8 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
fake_image.stub_out_image_service(self.stubs)
fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs)
- def fake_dumps(msg, default=None, **kwargs):
- return '""'
- self.stubs.Set(json, 'dumps', fake_dumps)
-
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):
@@ -98,104 +108,198 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
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',
- 'multiprocessing',
- '_winreg',
- 'nova.virt.configdrive',
- 'nova.utils',
- 'ctypes'
- ]
+ fake_get_remote_image_service)
+
+ def fake_sleep(ms):
+ pass
+ self.stubs.Set(time, 'sleep', fake_sleep)
+
+ self.stubs.Set(pathutils, 'PathUtils', fake.PathUtils)
+ self._mox.StubOutWithMock(fake.PathUtils, 'open')
+
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'vm_exists')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'create_vm')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'destroy_vm')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'attach_ide_drive')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'create_scsi_controller')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'create_nic')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'set_vm_state')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'list_instances')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'get_vm_summary_info')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'take_vm_snapshot')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'remove_vm_snapshot')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'set_nic_connection')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'get_vm_iscsi_controller')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'get_vm_ide_controller')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'get_attached_disks_count')
+ self._mox.StubOutWithMock(vmutils.VMUtils,
+ 'attach_volume_to_controller')
+ self._mox.StubOutWithMock(vmutils.VMUtils,
+ 'get_mounted_disk_by_drive_number')
+ self._mox.StubOutWithMock(vmutils.VMUtils, 'detach_vm_disk')
+
+ self._mox.StubOutWithMock(vhdutils.VHDUtils, 'create_differencing_vhd')
+ self._mox.StubOutWithMock(vhdutils.VHDUtils, 'reconnect_parent_vhd')
+ self._mox.StubOutWithMock(vhdutils.VHDUtils, 'merge_vhd')
+ self._mox.StubOutWithMock(vhdutils.VHDUtils, 'get_vhd_parent_path')
+
+ self._mox.StubOutWithMock(hostutils.HostUtils, 'get_cpus_info')
+ self._mox.StubOutWithMock(hostutils.HostUtils,
+ 'is_cpu_feature_present')
+ self._mox.StubOutWithMock(hostutils.HostUtils, 'get_memory_info')
+ self._mox.StubOutWithMock(hostutils.HostUtils, 'get_volume_info')
+ self._mox.StubOutWithMock(hostutils.HostUtils, 'get_windows_version')
+
+ self._mox.StubOutWithMock(networkutils.NetworkUtils,
+ 'get_external_vswitch')
+ self._mox.StubOutWithMock(networkutils.NetworkUtils,
+ 'create_vswitch_port')
+
+ self._mox.StubOutWithMock(livemigrationutils.LiveMigrationUtils,
+ 'live_migrate_vm')
+ self._mox.StubOutWithMock(livemigrationutils.LiveMigrationUtils,
+ 'check_live_migration_config')
+
+ self._mox.StubOutWithMock(basevolumeutils.BaseVolumeUtils,
+ 'volume_in_mapping')
+ self._mox.StubOutWithMock(basevolumeutils.BaseVolumeUtils,
+ 'get_session_id_from_mounted_disk')
+ self._mox.StubOutWithMock(basevolumeutils.BaseVolumeUtils,
+ 'get_device_number_for_target')
+
+ self._mox.StubOutWithMock(volumeutils.VolumeUtils,
+ 'login_storage_target')
+ self._mox.StubOutWithMock(volumeutils.VolumeUtils,
+ 'logout_storage_target')
+ self._mox.StubOutWithMock(volumeutils.VolumeUtils,
+ 'execute_log_out')
+
+ self._mox.StubOutWithMock(volumeutilsv2.VolumeUtilsV2,
+ 'login_storage_target')
+ self._mox.StubOutWithMock(volumeutilsv2.VolumeUtilsV2,
+ 'logout_storage_target')
+ self._mox.StubOutWithMock(volumeutilsv2.VolumeUtilsV2,
+ 'execute_log_out')
+
+ self._mox.StubOutWithMock(shutil, 'copyfile')
+ self._mox.StubOutWithMock(shutil, 'rmtree')
+
+ self._mox.StubOutWithMock(os, 'remove')
+
+ self._mox.StubOutClassWithMocks(instance_metadata, 'InstanceMetadata')
+ self._mox.StubOutWithMock(instance_metadata.InstanceMetadata,
+ 'metadata_for_config_drive')
+
+ # Can't use StubOutClassWithMocks due to __exit__ and __enter__
+ self._mox.StubOutWithMock(configdrive, 'ConfigDriveBuilder')
+ self._mox.StubOutWithMock(configdrive.ConfigDriveBuilder, 'make_drive')
+
+ self._mox.StubOutWithMock(utils, 'execute')
- # Modules in which the mocks are going to be injected
- from nova.virt.hyperv import baseops
- from nova.virt.hyperv import basevolumeutils
- from nova.virt.hyperv import hostops
- from nova.virt.hyperv import livemigrationops
- from nova.virt.hyperv import snapshotops
- from nova.virt.hyperv import vif
- from nova.virt.hyperv import vmops
- from nova.virt.hyperv import volumeops
- from nova.virt.hyperv import volumeutils
- from nova.virt.hyperv import volumeutilsV2
-
- modules_to_test = [
- driver_hyperv,
- basevolumeutils,
- baseops,
- hostops,
- vif,
- vmops,
- vmutils,
- volumeops,
- volumeutils,
- volumeutilsV2,
- snapshotops,
- livemigrationops,
- hypervutils,
- db_fakes,
- sys.modules[__name__]
- ]
+ def tearDown(self):
+ self._mox.UnsetStubs()
+ super(HyperVAPITestCase, self).tearDown()
- self._inject_mocks_in_modules(modules_to_mock, modules_to_test)
+ def test_get_available_resource(self):
+ cpu_info = {'Architecture': 'fake',
+ 'Name': 'fake',
+ 'Manufacturer': 'ACME, Inc.',
+ 'NumberOfCores': 2,
+ 'NumberOfLogicalProcessors': 4}
- if isinstance(snapshotops.wmi, mockproxy.Mock):
- from nova.virt.hyperv import ioutils
- import StringIO
+ tot_mem_kb = 2000000L
+ free_mem_kb = 1000000L
- def fake_open(name, mode):
- return StringIO.StringIO("fake file content")
- self.stubs.Set(ioutils, 'open', fake_open)
+ tot_hdd_b = 4L * 1024 ** 3
+ free_hdd_b = 3L * 1024 ** 3
- 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"])
+ windows_version = '6.2.9200'
- 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"])
+ hostutils.HostUtils.get_memory_info().AndReturn((tot_mem_kb,
+ free_mem_kb))
- self._hypervutils.logout_iscsi_volume_sessions(self._volume_id)
+ m = hostutils.HostUtils.get_volume_info(mox.IsA(str))
+ m.AndReturn((tot_hdd_b, free_hdd_b))
- shutil.rmtree(CONF.instances_path, True)
+ hostutils.HostUtils.get_cpus_info().AndReturn([cpu_info])
+ m = hostutils.HostUtils.is_cpu_feature_present(mox.IsA(int))
+ m.MultipleTimes()
- fake_image.FakeImageService_reset()
- finally:
- super(HyperVAPITestCase, self).tearDown()
+ m = hostutils.HostUtils.get_windows_version()
+ m.AndReturn(windows_version)
- def test_get_available_resource(self):
+ self._mox.ReplayAll()
dic = self._conn.get_available_resource(None)
+ self._mox.VerifyAll()
+ self.assertEquals(dic['vcpus'], cpu_info['NumberOfLogicalProcessors'])
self.assertEquals(dic['hypervisor_hostname'], platform.node())
+ self.assertEquals(dic['memory_mb'], tot_mem_kb / 1024)
+ self.assertEquals(dic['memory_mb_used'],
+ tot_mem_kb / 1024 - free_mem_kb / 1024)
+ self.assertEquals(dic['local_gb'], tot_hdd_b / 1024 ** 3)
+ self.assertEquals(dic['local_gb_used'],
+ tot_hdd_b / 1024 ** 3 - free_hdd_b / 1024 ** 3)
+ self.assertEquals(dic['hypervisor_version'],
+ windows_version.replace('.', ''))
def test_get_host_stats(self):
+ tot_mem_kb = 2000000L
+ free_mem_kb = 1000000L
+
+ tot_hdd_b = 4L * 1024 ** 3
+ free_hdd_b = 3L * 1024 ** 3
+
+ hostutils.HostUtils.get_memory_info().AndReturn((tot_mem_kb,
+ free_mem_kb))
+
+ m = hostutils.HostUtils.get_volume_info(mox.IsA(str))
+ m.AndReturn((tot_hdd_b, free_hdd_b))
+
+ self._mox.ReplayAll()
dic = self._conn.get_host_stats(True)
+ self._mox.VerifyAll()
+
+ self.assertEquals(dic['disk_total'], tot_hdd_b / 1024 ** 3)
+ self.assertEquals(dic['disk_available'], free_hdd_b / 1024 ** 3)
+
+ self.assertEquals(dic['host_memory_total'], tot_mem_kb / 1024)
+ self.assertEquals(dic['host_memory_free'], free_mem_kb / 1024)
self.assertEquals(dic['disk_total'],
- dic['disk_used'] + dic['disk_available'])
+ dic['disk_used'] + dic['disk_available'])
self.assertEquals(dic['host_memory_total'],
- dic['host_memory_overhead'] + dic['host_memory_free'])
+ dic['host_memory_overhead'] +
+ dic['host_memory_free'])
def test_list_instances(self):
- num_vms = self._hypervutils.get_vm_count()
+ fake_instances = ['fake1', 'fake2']
+ vmutils.VMUtils.list_instances().AndReturn(fake_instances)
+
+ self._mox.ReplayAll()
instances = self._conn.list_instances()
+ self._mox.VerifyAll()
- self.assertEquals(len(instances), num_vms)
+ self.assertEquals(instances, fake_instances)
def test_get_info(self):
- self._spawn_instance(True)
+ self._instance_data = self._get_instance_data()
+
+ summary_info = {'NumberOfProcessors': 2,
+ 'EnabledState': constants.HYPERV_VM_STATE_ENABLED,
+ 'MemoryUsage': 1000,
+ 'UpTime': 1}
+
+ m = vmutils.VMUtils.vm_exists(mox.Func(self._check_instance_name))
+ m.AndReturn(True)
+
+ func = mox.Func(self._check_instance_name)
+ m = vmutils.VMUtils.get_vm_summary_info(func)
+ m.AndReturn(summary_info)
+
+ self._mox.ReplayAll()
info = self._conn.get_info(self._instance_data)
+ self._mox.VerifyAll()
self.assertEquals(info["state"], power_state.RUNNING)
@@ -205,189 +309,237 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
def test_spawn_no_cow_image(self):
self._test_spawn_instance(False)
- def test_spawn_config_drive(self):
- self.skip('broken by move to contextlib for configdrive')
-
+ def _setup_spawn_config_drive_mocks(self, use_cdrom):
+ im = instance_metadata.InstanceMetadata(mox.IgnoreArg(),
+ content=mox.IsA(list),
+ extra_md=mox.IsA(dict))
+
+ cdb = self._mox.CreateMockAnything()
+ m = configdrive.ConfigDriveBuilder(instance_md=mox.IgnoreArg())
+ m.AndReturn(cdb)
+ # __enter__ and __exit__ are required by "with"
+ cdb.__enter__().AndReturn(cdb)
+ cdb.make_drive(mox.IsA(str))
+ cdb.__exit__(None, None, None).AndReturn(None)
+
+ if not use_cdrom:
+ utils.execute(CONF.qemu_img_cmd,
+ 'convert',
+ '-f',
+ 'raw',
+ '-O',
+ 'vpc',
+ mox.IsA(str),
+ mox.IsA(str),
+ attempts=1)
+ os.remove(mox.IsA(str))
+
+ m = vmutils.VMUtils.attach_ide_drive(mox.IsA(str),
+ mox.IsA(str),
+ mox.IsA(int),
+ mox.IsA(int),
+ mox.IsA(str))
+ m.WithSideEffects(self._add_ide_disk)
+
+ def _test_spawn_config_drive(self, use_cdrom):
self.flags(force_config_drive=True)
+ self.flags(config_drive_cdrom=use_cdrom)
self.flags(mkisofs_cmd='mkisofs.exe')
- self._spawn_instance(True)
+ self._setup_spawn_config_drive_mocks(use_cdrom)
- (vhd_paths, _, dvd_paths) = self._hypervutils.get_vm_disks(
- self._instance_data["name"])
- self.assertEquals(len(dvd_paths), 0)
- self.assertEquals(len(vhd_paths), 2)
+ if use_cdrom:
+ expected_ide_disks = 1
+ expected_ide_dvds = 1
+ else:
+ expected_ide_disks = 2
+ expected_ide_dvds = 0
- def test_spawn_config_drive_cdrom(self):
- self.skip('broken by move to contextlib for configdrive')
+ self._test_spawn_instance(expected_ide_disks=expected_ide_disks,
+ expected_ide_dvds=expected_ide_dvds)
- self.flags(force_config_drive=True)
- self.flags(config_drive_cdrom=True)
- self.flags(mkisofs_cmd='mkisofs.exe')
-
- self._spawn_instance(True)
+ def test_spawn_config_drive(self):
+ self._test_spawn_config_drive(False)
- (vhd_paths, _, dvd_paths) = self._hypervutils.get_vm_disks(
- self._instance_data["name"])
- self.assertEquals(len(dvd_paths), 1)
- self.assertEquals(len(vhd_paths), 1)
- self.assertTrue(os.path.exists(dvd_paths[0]))
+ def test_spawn_config_drive_cdrom(self):
+ self._test_spawn_config_drive(True)
def test_spawn_no_config_drive(self):
self.flags(force_config_drive=False)
- self._spawn_instance(True)
+ expected_ide_disks = 1
+ expected_ide_dvds = 0
+
+ self._test_spawn_instance(expected_ide_disks=expected_ide_disks,
+ expected_ide_dvds=expected_ide_dvds)
+
+ def test_spawn_nova_net_vif(self):
+ self.flags(network_api_class='nova.network.api.API')
+ # Reinstantiate driver, as the VIF plugin is loaded during __init__
+ self._conn = driver_hyperv.HyperVDriver(None)
+
+ def setup_vif_mocks():
+ fake_vswitch_path = 'fake vswitch path'
+ fake_vswitch_port = 'fake port'
+
+ m = networkutils.NetworkUtils.get_external_vswitch(
+ CONF.vswitch_name)
+ m.AndReturn(fake_vswitch_path)
+
+ m = networkutils.NetworkUtils.create_vswitch_port(
+ fake_vswitch_path, mox.IsA(str))
+ m.AndReturn(fake_vswitch_port)
- (_, _, dvd_paths) = self._hypervutils.get_vm_disks(
- self._instance_data["name"])
- self.assertEquals(len(dvd_paths), 0)
+ vmutils.VMUtils.set_nic_connection(mox.IsA(str), mox.IsA(str),
+ fake_vswitch_port)
- def test_spawn_no_vswitch_exception(self):
+ self._test_spawn_instance(setup_vif_mocks_func=setup_vif_mocks)
+
+ def test_spawn_nova_net_vif_no_vswitch_exception(self):
self.flags(network_api_class='nova.network.api.API')
# Reinstantiate driver, as the VIF plugin is loaded during __init__
self._conn = driver_hyperv.HyperVDriver(None)
- # 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 setup_vif_mocks():
+ m = networkutils.NetworkUtils.get_external_vswitch(
+ CONF.vswitch_name)
+ m.AndRaise(vmutils.HyperVException(_('fake vswitch not found')))
+
+ self.assertRaises(vmutils.HyperVException, self._test_spawn_instance,
+ setup_vif_mocks_func=setup_vif_mocks,
+ with_exception=True)
+
+ def _check_instance_name(self, vm_name):
+ return vm_name == 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)
+ self._instance_data = self._get_instance_data()
- vmstate = self._hypervutils.get_vm_state(self._instance_data["name"])
- self.assertEquals(vmstate, to_state)
+ vmutils.VMUtils.set_vm_state(mox.Func(self._check_instance_name),
+ to_state)
+
+ self._mox.ReplayAll()
+ action(self._instance_data)
+ self._mox.VerifyAll()
def test_pause(self):
self._test_vm_state_change(self._conn.pause, None,
- constants.HYPERV_VM_STATE_PAUSED)
+ 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)
+ 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)
+ 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)
+ constants.HYPERV_VM_STATE_ENABLED)
def test_suspend(self):
self._test_vm_state_change(self._conn.suspend, None,
- constants.HYPERV_VM_STATE_SUSPENDED)
+ 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)
+ constants.HYPERV_VM_STATE_SUSPENDED,
+ constants.HYPERV_VM_STATE_SUSPENDED)
def test_resume(self):
self._test_vm_state_change(lambda i: self._conn.resume(i, None),
- constants.HYPERV_VM_STATE_SUSPENDED,
- constants.HYPERV_VM_STATE_ENABLED)
+ constants.HYPERV_VM_STATE_SUSPENDED,
+ constants.HYPERV_VM_STATE_ENABLED)
def test_resume_already_running(self):
self._test_vm_state_change(lambda i: self._conn.resume(i, None), None,
- constants.HYPERV_VM_STATE_ENABLED)
+ 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)
+ 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)
+ self._test_vm_state_change(self._conn.power_off,
+ 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)
+ 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)
+ 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)
+ self._instance_data = self._get_instance_data()
- vmstate = self._hypervutils.get_vm_state(self._instance_data["name"])
- self.assertEquals(vmstate, constants.HYPERV_VM_STATE_ENABLED)
+ vmutils.VMUtils.set_vm_state(mox.Func(self._check_instance_name),
+ constants.HYPERV_VM_STATE_REBOOT)
+
+ self._mox.ReplayAll()
+ self._conn.reboot(self._instance_data, network_info, None)
+ self._mox.VerifyAll()
def test_destroy(self):
- self._spawn_instance(True)
- (vhd_paths, _, _) = self._hypervutils.get_vm_disks(
- self._instance_data["name"])
+ self._instance_data = self._get_instance_data()
- self._conn.destroy(self._instance_data)
+ m = vmutils.VMUtils.vm_exists(mox.Func(self._check_instance_name))
+ m.AndReturn(True)
- self.assertFalse(self._hypervutils.vm_exists(
- self._instance_data["name"]))
- self._instance_data = None
+ m = vmutils.VMUtils.destroy_vm(mox.Func(self._check_instance_name),
+ True)
+ m.AndReturn([])
- for vhd_path in vhd_paths:
- self.assertFalse(os.path.exists(vhd_path))
+ self._mox.ReplayAll()
+ self._conn.destroy(self._instance_data)
+ self._mox.VerifyAll()
def test_live_migration(self):
- self.flags(limit_cpu_features=True)
- self._spawn_instance(False)
+ self._test_live_migration(False)
- # Existing server
- self._dest_server = "HV12OSDEMO2"
+ def test_live_migration_with_target_failure(self):
+ self._test_live_migration(True)
- self._live_migration(self._dest_server)
+ def _test_live_migration(self, test_failure):
+ dest_server = 'fake_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))
+ instance_data = self._get_instance_data()
- self.assertTrue(self._post_method_called)
- self.assertFalse(self._recover_method_called)
+ fake_post_method = self._mox.CreateMockAnything()
+ if not test_failure:
+ fake_post_method(self._context, instance_data, dest_server,
+ False)
- def test_live_migration_with_target_failure(self):
- self.flags(limit_cpu_features=True)
- self._spawn_instance(False)
+ fake_recover_method = self._mox.CreateMockAnything()
+ if test_failure:
+ fake_recover_method(self._context, instance_data, dest_server,
+ False)
- dest_server = "nonexistingserver"
+ m = livemigrationutils.LiveMigrationUtils.live_migrate_vm(
+ instance_data['name'], dest_server)
+ if test_failure:
+ m.AndRaise(Exception('Simulated failure'))
- exception_raised = False
+ self._mox.ReplayAll()
try:
- self._live_migration(dest_server)
+ self._conn.live_migration(self._context, instance_data,
+ dest_server, fake_post_method,
+ fake_recover_method)
+ exception_raised = False
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)
+ self.assertTrue(not test_failure ^ exception_raised)
+ self._mox.VerifyAll()
def test_pre_live_migration_cow_image(self):
self._test_pre_live_migration(True)
@@ -398,83 +550,134 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
def _test_pre_live_migration(self, cow):
self.flags(use_cow_images=cow)
- instance_name = 'openstack_unit_test_vm_' + str(uuid.uuid4())
+ instance_data = self._get_instance_data()
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
+ m = livemigrationutils.LiveMigrationUtils.check_live_migration_config()
+ m.AndReturn(True)
+
+ if cow:
+ m = basevolumeutils.BaseVolumeUtils.volume_in_mapping(mox.IsA(str),
+ None)
+ m.AndReturn([])
+
+ self._mox.ReplayAll()
self._conn.pre_live_migration(self._context, instance_data,
- block_device_info, network_info)
+ None, network_info)
+ self._mox.VerifyAll()
if cow:
- self.assertTrue(not self._fetched_image is None)
+ self.assertTrue(self._fetched_image is not None)
else:
self.assertTrue(self._fetched_image is None)
def test_snapshot_with_update_failure(self):
- expected_calls = [
- {'args': (),
- 'kwargs':
- {'task_state': task_states.IMAGE_PENDING_UPLOAD}},
- {'args': (),
- 'kwargs':
- {'task_state': task_states.IMAGE_UPLOADING,
- 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}]
- func_call_matcher = matchers.FunctionCallMatcher(expected_calls)
-
- self._spawn_instance(True)
+ (snapshot_name, func_call_matcher) = self._setup_snapshot_mocks()
self._update_image_raise_exception = True
- snapshot_name = 'test_snapshot_' + str(uuid.uuid4())
+
+ self._mox.ReplayAll()
self.assertRaises(vmutils.HyperVException, self._conn.snapshot,
self._context, self._instance_data, snapshot_name,
func_call_matcher.call)
+ self._mox.VerifyAll()
- # assert states changed in correct order
+ # Assert states changed in correct order
self.assertIsNone(func_call_matcher.match())
- # assert VM snapshots have been removed
- self.assertEquals(self._hypervutils.get_vm_snapshots_count(
- self._instance_data["name"]), 0)
-
- def test_snapshot(self):
+ def _setup_snapshot_mocks(self):
expected_calls = [
{'args': (),
- 'kwargs':
- {'task_state': task_states.IMAGE_PENDING_UPLOAD}},
- {'args': (),
- 'kwargs':
- {'task_state': task_states.IMAGE_UPLOADING,
- 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}]
+ 'kwargs': {'task_state': task_states.IMAGE_PENDING_UPLOAD}},
+ {'args': (),
+ 'kwargs': {'task_state': task_states.IMAGE_UPLOADING,
+ 'expected_state': task_states.IMAGE_PENDING_UPLOAD}}
+ ]
func_call_matcher = matchers.FunctionCallMatcher(expected_calls)
- self._spawn_instance(True)
-
snapshot_name = 'test_snapshot_' + str(uuid.uuid4())
+
+ fake_hv_snapshot_path = 'fake_snapshot_path'
+ fake_parent_vhd_path = 'C:\\fake_vhd_path\\parent.vhd'
+
+ self._instance_data = self._get_instance_data()
+
+ func = mox.Func(self._check_instance_name)
+ m = vmutils.VMUtils.take_vm_snapshot(func)
+ m.AndReturn(fake_hv_snapshot_path)
+
+ m = vhdutils.VHDUtils.get_vhd_parent_path(mox.IsA(str))
+ m.AndReturn(fake_parent_vhd_path)
+
+ self._fake_dest_disk_path = None
+
+ def copy_dest_disk_path(src, dest):
+ self._fake_dest_disk_path = dest
+
+ m = shutil.copyfile(mox.IsA(str), mox.IsA(str))
+ m.WithSideEffects(copy_dest_disk_path)
+
+ self._fake_dest_base_disk_path = None
+
+ def copy_dest_base_disk_path(src, dest):
+ self._fake_dest_base_disk_path = dest
+
+ m = shutil.copyfile(fake_parent_vhd_path, mox.IsA(str))
+ m.WithSideEffects(copy_dest_base_disk_path)
+
+ def check_dest_disk_path(path):
+ return path == self._fake_dest_disk_path
+
+ def check_dest_base_disk_path(path):
+ return path == self._fake_dest_base_disk_path
+
+ func1 = mox.Func(check_dest_disk_path)
+ func2 = mox.Func(check_dest_base_disk_path)
+ # Make sure that the hyper-v base and differential VHDs are merged
+ vhdutils.VHDUtils.reconnect_parent_vhd(func1, func2)
+ vhdutils.VHDUtils.merge_vhd(func1, func2)
+
+ def check_snapshot_path(snapshot_path):
+ return snapshot_path == fake_hv_snapshot_path
+
+ # Make sure that the Hyper-V snapshot is removed
+ func = mox.Func(check_snapshot_path)
+ vmutils.VMUtils.remove_vm_snapshot(func)
+
+ shutil.rmtree(mox.IsA(str))
+
+ m = fake.PathUtils.open(func2, 'rb')
+ m.AndReturn(io.BytesIO(b'fake content'))
+
+ return (snapshot_name, func_call_matcher)
+
+ def test_snapshot(self):
+ (snapshot_name, func_call_matcher) = self._setup_snapshot_mocks()
+
+ self._mox.ReplayAll()
self._conn.snapshot(self._context, self._instance_data, snapshot_name,
func_call_matcher.call)
+ self._mox.VerifyAll()
self.assertTrue(self._image_metadata and
- "disk_format" in self._image_metadata and
- self._image_metadata["disk_format"] == "vhd")
+ "disk_format" in self._image_metadata and
+ self._image_metadata["disk_format"] == "vhd")
- # assert states changed in correct order
+ # Assert states changed in correct order
self.assertIsNone(func_call_matcher.match())
- # assert VM snapshots have been removed
- self.assertEquals(self._hypervutils.get_vm_snapshots_count(
- self._instance_data["name"]), 0)
+ def _get_instance_data(self):
+ instance_name = 'openstack_unit_test_vm_' + str(uuid.uuid4())
+ return db_fakes.get_fake_instance_data(instance_name,
+ self._project_id,
+ self._user_id)
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)
+ self._instance_data = self._get_instance_data()
instance = db.instance_create(self._context, self._instance_data)
image = db_fakes.get_fake_image_data(self._project_id, self._user_id)
@@ -487,73 +690,216 @@ class HyperVAPITestCase(basetestcase.BaseTestCase):
network_info=network_info,
block_device_info=block_device_info)
- def _test_spawn_instance(self, cow):
- self._spawn_instance(cow)
+ def _add_ide_disk(self, vm_name, path, ctrller_addr,
+ drive_addr, drive_type):
+ if drive_type == constants.IDE_DISK:
+ self._instance_ide_disks.append(path)
+ elif drive_type == constants.IDE_DVD:
+ self._instance_ide_dvds.append(path)
+
+ def _add_volume_disk(self, vm_name, controller_path, address,
+ mounted_disk_path):
+ self._instance_volume_disks.append(mounted_disk_path)
- self.assertTrue(self._hypervutils.vm_exists(
- self._instance_data["name"]))
+ def _setup_spawn_instance_mocks(self, cow, setup_vif_mocks_func=None,
+ with_exception=False,
+ block_device_info=None):
+ self._test_vm_name = None
- vmstate = self._hypervutils.get_vm_state(self._instance_data["name"])
- self.assertEquals(vmstate, constants.HYPERV_VM_STATE_ENABLED)
+ def set_vm_name(vm_name):
+ self._test_vm_name = vm_name
- (vhd_paths, _, _) = self._hypervutils.get_vm_disks(
- self._instance_data["name"])
- self.assertEquals(len(vhd_paths), 1)
+ def check_vm_name(vm_name):
+ return vm_name == self._test_vm_name
+
+ m = vmutils.VMUtils.vm_exists(mox.IsA(str))
+ m.WithSideEffects(set_vm_name).AndReturn(False)
+
+ if not block_device_info:
+ m = basevolumeutils.BaseVolumeUtils.volume_in_mapping(mox.IsA(str),
+ None)
+ m.AndReturn([])
+ else:
+ m = basevolumeutils.BaseVolumeUtils.volume_in_mapping(
+ mox.IsA(str), block_device_info)
+ m.AndReturn(True)
- 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)
+ def check_path(parent_path):
+ return parent_path == self._fetched_image
+
+ vhdutils.VHDUtils.create_differencing_vhd(mox.IsA(str),
+ mox.Func(check_path))
+
+ vmutils.VMUtils.create_vm(mox.Func(check_vm_name), mox.IsA(int),
+ mox.IsA(int), mox.IsA(bool))
+
+ if not block_device_info:
+ m = vmutils.VMUtils.attach_ide_drive(mox.Func(check_vm_name),
+ mox.IsA(str),
+ mox.IsA(int),
+ mox.IsA(int),
+ mox.IsA(str))
+ m.WithSideEffects(self._add_ide_disk).InAnyOrder()
+
+ m = vmutils.VMUtils.create_scsi_controller(mox.Func(check_vm_name))
+ m.InAnyOrder()
+
+ vmutils.VMUtils.create_nic(mox.Func(check_vm_name), mox.IsA(str),
+ mox.IsA(str)).InAnyOrder()
+
+ if setup_vif_mocks_func:
+ setup_vif_mocks_func()
+
+ # TODO(alexpilotti) Based on where the exception is thrown
+ # some of the above mock calls need to be skipped
+ if with_exception:
+ m = vmutils.VMUtils.vm_exists(mox.Func(check_vm_name))
+ m.AndReturn(True)
+
+ vmutils.VMUtils.destroy_vm(mox.Func(check_vm_name), True)
else:
- self.assertTrue(parent_path is None)
- self.assertEquals(self._fetched_image, vhd_paths[0])
+ vmutils.VMUtils.set_vm_state(mox.Func(check_vm_name),
+ constants.HYPERV_VM_STATE_ENABLED)
+
+ def _test_spawn_instance(self, cow=True,
+ expected_ide_disks=1,
+ expected_ide_dvds=0,
+ setup_vif_mocks_func=None,
+ with_exception=False):
+ self._setup_spawn_instance_mocks(cow, setup_vif_mocks_func,
+ with_exception)
+
+ self._mox.ReplayAll()
+ self._spawn_instance(cow, )
+ self._mox.VerifyAll()
+
+ self.assertEquals(len(self._instance_ide_disks), expected_ide_disks)
+ self.assertEquals(len(self._instance_ide_dvds), expected_ide_dvds)
+
+ if not cow:
+ self.assertEquals(self._fetched_image, self._instance_ide_disks[0])
+
+ def test_attach_volume(self):
+ instance_data = self._get_instance_data()
+ instance_name = instance_data['name']
- def _attach_volume(self):
- self._spawn_instance(True)
connection_info = db_fakes.get_fake_volume_info_data(
self._volume_target_portal, self._volume_id)
+ data = connection_info['data']
+ target_lun = data['target_lun']
+ target_iqn = data['target_iqn']
+ target_portal = data['target_portal']
- self._conn.attach_volume(connection_info,
- self._instance_data, '/dev/sdc')
+ mount_point = '/dev/sdc'
- def test_attach_volume(self):
- self._attach_volume()
+ volumeutils.VolumeUtils.login_storage_target(target_lun,
+ target_iqn,
+ target_portal)
+
+ fake_mounted_disk = "fake_mounted_disk"
+ fake_device_number = 0
+ fake_controller_path = 'fake_scsi_controller_path'
+ fake_free_slot = 1
+
+ m = volumeutils.VolumeUtils.get_device_number_for_target(target_iqn,
+ target_lun)
+ m.AndReturn(fake_device_number)
+
+ m = vmutils.VMUtils.get_mounted_disk_by_drive_number(
+ fake_device_number)
+ m.AndReturn(fake_mounted_disk)
- (_, volumes_paths, _) = self._hypervutils.get_vm_disks(
- self._instance_data["name"])
- self.assertEquals(len(volumes_paths), 1)
+ m = vmutils.VMUtils.get_vm_iscsi_controller(instance_name)
+ m.AndReturn(fake_controller_path)
- sessions_exist = self._hypervutils.iscsi_volume_sessions_exist(
- self._volume_id)
- self.assertTrue(sessions_exist)
+ m = vmutils.VMUtils.get_attached_disks_count(fake_controller_path)
+ m.AndReturn(fake_free_slot)
+
+ m = vmutils.VMUtils.attach_volume_to_controller(instance_name,
+ fake_controller_path,
+ fake_free_slot,
+ fake_mounted_disk)
+ m.WithSideEffects(self._add_volume_disk)
+
+ self._mox.ReplayAll()
+ self._conn.attach_volume(connection_info, instance_data, mount_point)
+ self._mox.VerifyAll()
+
+ self.assertEquals(len(self._instance_volume_disks), 1)
def test_detach_volume(self):
- self._attach_volume()
+ instance_data = self._get_instance_data()
+ instance_name = instance_data['name']
+
connection_info = db_fakes.get_fake_volume_info_data(
self._volume_target_portal, self._volume_id)
+ data = connection_info['data']
+ target_lun = data['target_lun']
+ target_iqn = data['target_iqn']
+ target_portal = data['target_portal']
+ mount_point = '/dev/sdc'
- self._conn.detach_volume(connection_info,
- self._instance_data, '/dev/sdc')
+ fake_mounted_disk = "fake_mounted_disk"
+ fake_device_number = 0
+ fake_free_slot = 1
+ m = volumeutils.VolumeUtils.get_device_number_for_target(target_iqn,
+ target_lun)
+ m.AndReturn(fake_device_number)
- (_, volumes_paths, _) = self._hypervutils.get_vm_disks(
- self._instance_data["name"])
- self.assertEquals(len(volumes_paths), 0)
+ m = vmutils.VMUtils.get_mounted_disk_by_drive_number(
+ fake_device_number)
+ m.AndReturn(fake_mounted_disk)
- sessions_exist = self._hypervutils.iscsi_volume_sessions_exist(
- self._volume_id)
- self.assertFalse(sessions_exist)
+ vmutils.VMUtils.detach_vm_disk(mox.IsA(str), fake_mounted_disk)
+
+ volumeutils.VolumeUtils.logout_storage_target(mox.IsA(str))
+
+ self._mox.ReplayAll()
+ self._conn.detach_volume(connection_info, instance_data, mount_point)
+ self._mox.VerifyAll()
def test_boot_from_volume(self):
+ connection_info = db_fakes.get_fake_volume_info_data(
+ self._volume_target_portal, self._volume_id)
+ data = connection_info['data']
+ target_lun = data['target_lun']
+ target_iqn = data['target_iqn']
+ target_portal = data['target_portal']
+
block_device_info = db_fakes.get_fake_block_device_info(
self._volume_target_portal, self._volume_id)
- self._spawn_instance(False, block_device_info)
+ fake_mounted_disk = "fake_mounted_disk"
+ fake_device_number = 0
+ fake_controller_path = 'fake_scsi_controller_path'
+
+ volumeutils.VolumeUtils.login_storage_target(target_lun,
+ target_iqn,
+ target_portal)
+
+ m = volumeutils.VolumeUtils.get_device_number_for_target(target_iqn,
+ target_lun)
+ m.AndReturn(fake_device_number)
- (_, volumes_paths, _) = self._hypervutils.get_vm_disks(
- self._instance_data["name"])
+ m = vmutils.VMUtils.get_mounted_disk_by_drive_number(
+ fake_device_number)
+ m.AndReturn(fake_mounted_disk)
- self.assertEquals(len(volumes_paths), 1)
+ m = vmutils.VMUtils.get_vm_ide_controller(mox.IsA(str), mox.IsA(int))
+ m.AndReturn(fake_controller_path)
+
+ m = vmutils.VMUtils.attach_volume_to_controller(mox.IsA(str),
+ fake_controller_path,
+ 0,
+ fake_mounted_disk)
+ m.WithSideEffects(self._add_volume_disk)
+
+ self._setup_spawn_instance_mocks(cow=False,
+ block_device_info=block_device_info)
+
+ self._mox.ReplayAll()
+ self._spawn_instance(False, block_device_info)
+ self._mox.VerifyAll()
- sessions_exist = self._hypervutils.iscsi_volume_sessions_exist(
- self._volume_id)
- self.assertTrue(sessions_exist)
+ self.assertEquals(len(self._instance_volume_disks), 1)
diff --git a/nova/tests/test_imagecache.py b/nova/tests/test_imagecache.py
index 8142312b9..611519514 100644
--- a/nova/tests/test_imagecache.py
+++ b/nova/tests/test_imagecache.py
@@ -331,7 +331,6 @@ class ImageCacheManagerTestCase(test.TestCase):
base_file1 = os.path.join(base_dir, fingerprint)
base_file2 = os.path.join(base_dir, fingerprint + '_sm')
base_file3 = os.path.join(base_dir, fingerprint + '_10737418240')
- print res
self.assertTrue(res == [(base_file1, False, False),
(base_file2, True, False),
(base_file3, False, True)])
diff --git a/nova/tests/test_iptables_network.py b/nova/tests/test_iptables_network.py
index c8f310303..95af25ebd 100644
--- a/nova/tests/test_iptables_network.py
+++ b/nova/tests/test_iptables_network.py
@@ -170,3 +170,22 @@ class IptablesManagerTestCase(test.TestCase):
self.assertTrue('[0:0] -A %s -j %s-%s' %
(chain, self.binary_name, chain) in new_lines,
"Built-in chain %s not wrapped" % (chain,))
+
+ def test_missing_table(self):
+ current_lines = []
+ new_lines = self.manager._modify_rules(current_lines,
+ self.manager.ipv4['filter'],
+ table_name='filter')
+
+ for line in ['*filter',
+ 'COMMIT']:
+ self.assertTrue(line in new_lines, "One of iptables key lines"
+ "went missing.")
+
+ self.assertTrue(len(new_lines) > 4, "No iptables rules added")
+
+ self.assertTrue("#Generated by nova" == new_lines[0] and
+ "*filter" == new_lines[1] and
+ "COMMIT" == new_lines[-2] and
+ "#Completed by nova" == new_lines[-1],
+ "iptables rules not generated in the correct order")
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index 75e758cde..72c886529 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -3833,7 +3833,6 @@ class IptablesFirewallTestCase(test.TestCase):
if '*filter' in lines:
self.out6_rules = lines
return '', ''
- print cmd, kwargs
network_model = _fake_network_info(self.stubs, 1, spectacular=True)
@@ -4250,7 +4249,7 @@ class LibvirtUtilsTestCase(test.TestCase):
def test_pick_disk_driver_name(self):
type_map = {'kvm': ([True, 'qemu'], [False, 'qemu'], [None, 'qemu']),
'qemu': ([True, 'qemu'], [False, 'qemu'], [None, 'qemu']),
- 'xen': ([True, 'phy'], [False, 'file'], [None, 'file']),
+ 'xen': ([True, 'phy'], [False, 'tap'], [None, 'tap']),
'uml': ([True, None], [False, None], [None, None]),
'lxc': ([True, None], [False, None], [None, None])}
@@ -4747,6 +4746,25 @@ class LibvirtDriverTestCase(test.TestCase):
self.libvirtconnection._cleanup_resize(ins_ref,
_fake_network_info(self.stubs, 1))
+ def test_get_instance_disk_info_exception(self):
+ instance_name = "fake-instance-name"
+
+ class FakeExceptionDomain(FakeVirtDomain):
+ def __init__(self):
+ super(FakeExceptionDomain, self).__init__()
+
+ def XMLDesc(self, *args):
+ raise libvirt.libvirtError("Libvirt error")
+
+ def fake_lookup_by_name(instance_name):
+ return FakeExceptionDomain()
+
+ self.stubs.Set(self.libvirtconnection, '_lookup_by_name',
+ fake_lookup_by_name)
+ self.assertRaises(exception.InstanceNotFound,
+ self.libvirtconnection.get_instance_disk_info,
+ instance_name)
+
class LibvirtVolumeUsageTestCase(test.TestCase):
"""Test for nova.virt.libvirt.libvirt_driver.LibvirtDriver
diff --git a/nova/tests/test_libvirt_vif.py b/nova/tests/test_libvirt_vif.py
index 11ffa020f..7ce81cc09 100644
--- a/nova/tests/test_libvirt_vif.py
+++ b/nova/tests/test_libvirt_vif.py
@@ -16,6 +16,8 @@
from lxml import etree
+from nova import exception
+from nova.network import model as network_model
from nova.openstack.common import cfg
from nova import test
from nova import utils
@@ -27,7 +29,7 @@ CONF = cfg.CONF
class LibvirtVifTestCase(test.TestCase):
- net = {
+ net_bridge = {
'cidr': '101.168.1.0/24',
'cidr_v6': '101:1db9::/64',
'gateway_v6': '101:1db9::1',
@@ -42,13 +44,47 @@ class LibvirtVifTestCase(test.TestCase):
'id': 'network-id-xxx-yyy-zzz'
}
- mapping = {
+ mapping_bridge = {
'mac': 'ca:fe:de:ad:be:ef',
- 'gateway_v6': net['gateway_v6'],
+ 'gateway_v6': net_bridge['gateway_v6'],
'ips': [{'ip': '101.168.1.9'}],
'dhcp_server': '191.168.1.1',
'vif_uuid': 'vif-xxx-yyy-zzz',
- 'vif_devname': 'tap-xxx-yyy-zzz'
+ 'vif_devname': 'tap-xxx-yyy-zzz',
+ 'vif_type': network_model.VIF_TYPE_BRIDGE,
+ }
+
+ net_ovs = {
+ 'cidr': '101.168.1.0/24',
+ 'cidr_v6': '101:1db9::/64',
+ 'gateway_v6': '101:1db9::1',
+ 'netmask_v6': '64',
+ 'netmask': '255.255.255.0',
+ 'bridge': 'br0',
+ 'vlan': 99,
+ 'gateway': '101.168.1.1',
+ 'broadcast': '101.168.1.255',
+ 'dns1': '8.8.8.8',
+ 'id': 'network-id-xxx-yyy-zzz'
+ }
+
+ mapping_ovs = {
+ 'mac': 'ca:fe:de:ad:be:ef',
+ 'gateway_v6': net_ovs['gateway_v6'],
+ 'ips': [{'ip': '101.168.1.9'}],
+ 'dhcp_server': '191.168.1.1',
+ 'vif_uuid': 'vif-xxx-yyy-zzz',
+ 'vif_devname': 'tap-xxx-yyy-zzz',
+ 'ovs_interfaceid': 'aaa-bbb-ccc',
+ }
+
+ mapping_none = {
+ 'mac': 'ca:fe:de:ad:be:ef',
+ 'gateway_v6': net_bridge['gateway_v6'],
+ 'ips': [{'ip': '101.168.1.9'}],
+ 'dhcp_server': '191.168.1.1',
+ 'vif_uuid': 'vif-xxx-yyy-zzz',
+ 'vif_devname': 'tap-xxx-yyy-zzz',
}
instance = {
@@ -67,7 +103,7 @@ class LibvirtVifTestCase(test.TestCase):
self.stubs.Set(utils, 'execute', fake_execute)
- def _get_instance_xml(self, driver):
+ def _get_instance_xml(self, driver, net, mapping):
conf = vconfig.LibvirtConfigGuest()
conf.virt_type = "qemu"
conf.name = "fake-name"
@@ -75,7 +111,7 @@ class LibvirtVifTestCase(test.TestCase):
conf.memory = 100 * 1024
conf.vcpus = 4
- nic = driver.get_config(self.instance, self.net, self.mapping)
+ nic = driver.get_config(self.instance, net, mapping)
conf.add_device(nic)
return conf.to_xml()
@@ -125,8 +161,10 @@ class LibvirtVifTestCase(test.TestCase):
self.flags(libvirt_use_virtio_for_bridges=False,
libvirt_type='kvm')
- d = vif.LibvirtBridgeDriver()
- xml = self._get_instance_xml(d)
+ d = vif.LibvirtGenericVIFDriver()
+ xml = self._get_instance_xml(d,
+ self.net_bridge,
+ self.mapping_bridge)
doc = etree.fromstring(xml)
ret = doc.findall('./devices/interface')
@@ -142,8 +180,10 @@ class LibvirtVifTestCase(test.TestCase):
self.flags(libvirt_use_virtio_for_bridges=True,
libvirt_type='kvm')
- d = vif.LibvirtBridgeDriver()
- xml = self._get_instance_xml(d)
+ d = vif.LibvirtGenericVIFDriver()
+ xml = self._get_instance_xml(d,
+ self.net_bridge,
+ self.mapping_bridge)
doc = etree.fromstring(xml)
ret = doc.findall('./devices/interface')
@@ -159,8 +199,10 @@ class LibvirtVifTestCase(test.TestCase):
self.flags(libvirt_use_virtio_for_bridges=True,
libvirt_type='qemu')
- d = vif.LibvirtBridgeDriver()
- xml = self._get_instance_xml(d)
+ d = vif.LibvirtGenericVIFDriver()
+ xml = self._get_instance_xml(d,
+ self.net_bridge,
+ self.mapping_bridge)
doc = etree.fromstring(xml)
ret = doc.findall('./devices/interface')
@@ -176,8 +218,10 @@ class LibvirtVifTestCase(test.TestCase):
self.flags(libvirt_use_virtio_for_bridges=True,
libvirt_type='xen')
- d = vif.LibvirtBridgeDriver()
- xml = self._get_instance_xml(d)
+ d = vif.LibvirtGenericVIFDriver()
+ xml = self._get_instance_xml(d,
+ self.net_bridge,
+ self.mapping_bridge)
doc = etree.fromstring(xml)
ret = doc.findall('./devices/interface')
@@ -189,9 +233,18 @@ class LibvirtVifTestCase(test.TestCase):
ret = node.findall("driver")
self.assertEqual(len(ret), 0)
- def test_bridge_driver(self):
- d = vif.LibvirtBridgeDriver()
- xml = self._get_instance_xml(d)
+ def test_generic_driver_none(self):
+ d = vif.LibvirtGenericVIFDriver()
+ self.assertRaises(exception.NovaException,
+ self._get_instance_xml,
+ d,
+ self.net_bridge,
+ self.mapping_none)
+
+ def _check_bridge_driver(self, d):
+ xml = self._get_instance_xml(d,
+ self.net_bridge,
+ self.mapping_bridge)
doc = etree.fromstring(xml)
ret = doc.findall('./devices/interface')
@@ -199,13 +252,23 @@ class LibvirtVifTestCase(test.TestCase):
node = ret[0]
self.assertEqual(node.get("type"), "bridge")
br_name = node.find("source").get("bridge")
- self.assertEqual(br_name, self.net['bridge'])
+ self.assertEqual(br_name, self.net_bridge['bridge'])
mac = node.find("mac").get("address")
- self.assertEqual(mac, self.mapping['mac'])
+ self.assertEqual(mac, self.mapping_bridge['mac'])
+
+ def test_bridge_driver(self):
+ d = vif.LibvirtBridgeDriver()
+ self._check_bridge_driver(d)
+
+ def test_generic_driver_bridge(self):
+ d = vif.LibvirtGenericVIFDriver()
+ self._check_bridge_driver(d)
def test_ovs_ethernet_driver(self):
d = vif.LibvirtOpenVswitchDriver()
- xml = self._get_instance_xml(d)
+ xml = self._get_instance_xml(d,
+ self.net_ovs,
+ self.mapping_ovs)
doc = etree.fromstring(xml)
ret = doc.findall('./devices/interface')
@@ -215,13 +278,15 @@ class LibvirtVifTestCase(test.TestCase):
dev_name = node.find("target").get("dev")
self.assertTrue(dev_name.startswith("tap"))
mac = node.find("mac").get("address")
- self.assertEqual(mac, self.mapping['mac'])
+ self.assertEqual(mac, self.mapping_ovs['mac'])
script = node.find("script").get("path")
self.assertEquals(script, "")
def test_ovs_virtualport_driver(self):
d = vif.LibvirtOpenVswitchVirtualPortDriver()
- xml = self._get_instance_xml(d)
+ xml = self._get_instance_xml(d,
+ self.net_ovs,
+ self.mapping_ovs)
doc = etree.fromstring(xml)
ret = doc.findall('./devices/interface')
@@ -232,21 +297,24 @@ class LibvirtVifTestCase(test.TestCase):
br_name = node.find("source").get("bridge")
self.assertEqual(br_name, "br0")
mac = node.find("mac").get("address")
- self.assertEqual(mac, self.mapping['mac'])
+ self.assertEqual(mac, self.mapping_ovs['mac'])
vp = node.find("virtualport")
self.assertEqual(vp.get("type"), "openvswitch")
iface_id_found = False
for p_elem in vp.findall("parameters"):
iface_id = p_elem.get("interfaceid", None)
if iface_id:
- self.assertEqual(iface_id, self.mapping['vif_uuid'])
+ self.assertEqual(iface_id,
+ self.mapping_ovs['ovs_interfaceid'])
iface_id_found = True
self.assertTrue(iface_id_found)
def test_quantum_bridge_ethernet_driver(self):
d = vif.QuantumLinuxBridgeVIFDriver()
- xml = self._get_instance_xml(d)
+ xml = self._get_instance_xml(d,
+ self.net_bridge,
+ self.mapping_bridge)
doc = etree.fromstring(xml)
ret = doc.findall('./devices/interface')
@@ -256,13 +324,15 @@ class LibvirtVifTestCase(test.TestCase):
dev_name = node.find("target").get("dev")
self.assertTrue(dev_name.startswith("tap"))
mac = node.find("mac").get("address")
- self.assertEqual(mac, self.mapping['mac'])
+ self.assertEqual(mac, self.mapping_ovs['mac'])
br_name = node.find("source").get("bridge")
self.assertEqual(br_name, "br0")
def test_quantum_hybrid_driver(self):
d = vif.LibvirtHybridOVSBridgeDriver()
- xml = self._get_instance_xml(d)
+ xml = self._get_instance_xml(d,
+ self.net_ovs,
+ self.mapping_ovs)
doc = etree.fromstring(xml)
ret = doc.findall('./devices/interface')
@@ -270,6 +340,6 @@ class LibvirtVifTestCase(test.TestCase):
node = ret[0]
self.assertEqual(node.get("type"), "bridge")
br_name = node.find("source").get("bridge")
- self.assertEqual(br_name, self.net['bridge'])
+ self.assertEqual(br_name, self.net_ovs['bridge'])
mac = node.find("mac").get("address")
- self.assertEqual(mac, self.mapping['mac'])
+ self.assertEqual(mac, self.mapping_ovs['mac'])
diff --git a/nova/tests/test_metadata.py b/nova/tests/test_metadata.py
index f15d71633..c2f0b5a11 100644
--- a/nova/tests/test_metadata.py
+++ b/nova/tests/test_metadata.py
@@ -19,7 +19,7 @@
"""Tests for metadata service."""
import base64
-from copy import copy
+import copy
import json
import re
@@ -120,14 +120,14 @@ class MetadataTestCase(test.TestCase):
spectacular=True)
def test_user_data(self):
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
inst['user_data'] = base64.b64encode("happy")
md = fake_InstanceMetadata(self.stubs, inst)
self.assertEqual(
md.get_ec2_metadata(version='2009-04-04')['user-data'], "happy")
def test_no_user_data(self):
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
del inst['user_data']
md = fake_InstanceMetadata(self.stubs, inst)
obj = object()
@@ -136,7 +136,7 @@ class MetadataTestCase(test.TestCase):
obj)
def test_security_groups(self):
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
sgroups = [{'name': 'default'}, {'name': 'other'}]
expected = ['default', 'other']
@@ -145,7 +145,7 @@ class MetadataTestCase(test.TestCase):
self.assertEqual(data['meta-data']['security-groups'], expected)
def test_local_hostname_fqdn(self):
- md = fake_InstanceMetadata(self.stubs, copy(self.instance))
+ md = fake_InstanceMetadata(self.stubs, copy.copy(self.instance))
data = md.get_ec2_metadata(version='2009-04-04')
self.assertEqual(data['meta-data']['local-hostname'],
"%s.%s" % (self.instance['hostname'], CONF.dhcp_domain))
@@ -195,7 +195,7 @@ class MetadataTestCase(test.TestCase):
expected)
def test_pubkey(self):
- md = fake_InstanceMetadata(self.stubs, copy(self.instance))
+ md = fake_InstanceMetadata(self.stubs, copy.copy(self.instance))
pubkey_ent = md.lookup("/2009-04-04/meta-data/public-keys")
self.assertEqual(base.ec2_md_print(pubkey_ent),
@@ -204,7 +204,7 @@ class MetadataTestCase(test.TestCase):
self.instance['key_data'])
def test_image_type_ramdisk(self):
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
inst['ramdisk_id'] = 'ari-853667c0'
md = fake_InstanceMetadata(self.stubs, inst)
data = md.lookup("/latest/meta-data/ramdisk-id")
@@ -213,7 +213,7 @@ class MetadataTestCase(test.TestCase):
self.assertTrue(re.match('ari-[0-9a-f]{8}', data))
def test_image_type_kernel(self):
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
inst['kernel_id'] = 'aki-c2e26ff2'
md = fake_InstanceMetadata(self.stubs, inst)
data = md.lookup("/2009-04-04/meta-data/kernel-id")
@@ -229,7 +229,7 @@ class MetadataTestCase(test.TestCase):
md.lookup, "/2009-04-04/meta-data/kernel-id")
def test_check_version(self):
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
md = fake_InstanceMetadata(self.stubs, inst)
self.assertTrue(md._check_version('1.0', '2009-04-04'))
@@ -250,7 +250,7 @@ class OpenStackMetadataTestCase(test.TestCase):
def test_top_level_listing(self):
# request for /openstack/<version>/ should show metadata.json
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
mdinst = fake_InstanceMetadata(self.stubs, inst)
listing = mdinst.lookup("/openstack/")
@@ -267,14 +267,14 @@ class OpenStackMetadataTestCase(test.TestCase):
def test_version_content_listing(self):
# request for /openstack/<version>/ should show metadata.json
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
mdinst = fake_InstanceMetadata(self.stubs, inst)
listing = mdinst.lookup("/openstack/2012-08-10")
self.assertTrue("meta_data.json" in listing)
def test_metadata_json(self):
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
content = [
('/etc/my.conf', "content of my.conf"),
('/root/hello', "content of /root/hello"),
@@ -309,7 +309,7 @@ class OpenStackMetadataTestCase(test.TestCase):
def test_extra_md(self):
# make sure extra_md makes it through to metadata
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
extra = {'foo': 'bar', 'mylist': [1, 2, 3],
'mydict': {"one": 1, "two": 2}}
mdinst = fake_InstanceMetadata(self.stubs, inst, extra_md=extra)
@@ -322,14 +322,14 @@ class OpenStackMetadataTestCase(test.TestCase):
def test_password(self):
# make sure extra_md makes it through to metadata
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
mdinst = fake_InstanceMetadata(self.stubs, inst)
result = mdinst.lookup("/openstack/latest/password")
self.assertEqual(result, password.handle_password)
def test_userdata(self):
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
mdinst = fake_InstanceMetadata(self.stubs, inst)
userdata_found = mdinst.lookup("/openstack/2012-08-10/user_data")
@@ -348,7 +348,7 @@ class OpenStackMetadataTestCase(test.TestCase):
mdinst.lookup, "/openstack/2012-08-10/user_data")
def test_random_seed(self):
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
mdinst = fake_InstanceMetadata(self.stubs, inst)
# verify that 2013-04-04 has the 'random' field
@@ -364,7 +364,7 @@ class OpenStackMetadataTestCase(test.TestCase):
def test_no_dashes_in_metadata(self):
# top level entries in meta_data should not contain '-' in their name
- inst = copy(self.instance)
+ inst = copy.copy(self.instance)
mdinst = fake_InstanceMetadata(self.stubs, inst)
mdjson = json.loads(mdinst.lookup("/openstack/latest/meta_data.json"))
@@ -522,7 +522,7 @@ class MetadataPasswordTestCase(test.TestCase):
super(MetadataPasswordTestCase, self).setUp()
fake_network.stub_out_nw_api_get_instance_nw_info(self.stubs,
spectacular=True)
- self.instance = copy(INSTANCES[0])
+ self.instance = copy.copy(INSTANCES[0])
self.mdinst = fake_InstanceMetadata(self.stubs, self.instance,
address=None, sgroups=None)
diff --git a/nova/tests/test_migrations.py b/nova/tests/test_migrations.py
index a6c150971..8c18d5511 100644
--- a/nova/tests/test_migrations.py
+++ b/nova/tests/test_migrations.py
@@ -24,6 +24,7 @@ properly both upgrading and downgrading, and that no data loss occurs
if possible.
"""
+import collections
import commands
import ConfigParser
import os
@@ -87,6 +88,16 @@ def _have_mysql():
return present.lower() in ('', 'true')
+def get_table(engine, name):
+ """Returns an sqlalchemy table dynamically from db.
+
+ Needed because the models don't work for us in migrations
+ as models will be far out of sync with the current data."""
+ metadata = sqlalchemy.schema.MetaData()
+ metadata.bind = engine
+ return sqlalchemy.Table(name, metadata, autoload=True)
+
+
class TestMigrations(test.TestCase):
"""Test sqlalchemy-migrate migrations."""
@@ -136,12 +147,6 @@ class TestMigrations(test.TestCase):
# and recreate it, which ensures that we have no side-effects
# from the tests
self._reset_databases()
-
- # remove these from the list so they aren't used in the migration tests
- if "mysqlcitest" in self.engines:
- del self.engines["mysqlcitest"]
- if "mysqlcitest" in self.test_databases:
- del self.test_databases["mysqlcitest"]
super(TestMigrations, self).tearDown()
def _reset_databases(self):
@@ -233,19 +238,11 @@ class TestMigrations(test.TestCase):
self.engines["mysqlcitest"] = engine
self.test_databases["mysqlcitest"] = connect_string
- # Test that we end in an innodb
- self._check_mysql_innodb(engine)
- # Test IP transition
- self._check_mysql_migration_149(engine)
-
- def _check_mysql_innodb(self, engine):
# build a fully populated mysql database with all the tables
self._reset_databases()
self._walk_versions(engine, False, False)
- uri = _get_connect_string("mysql", database="information_schema")
- connection = sqlalchemy.create_engine(uri).connect()
-
+ connection = engine.connect()
# sanity check
total = connection.execute("SELECT count(*) "
"from information_schema.TABLES "
@@ -259,92 +256,8 @@ class TestMigrations(test.TestCase):
"and TABLE_NAME!='migrate_version'")
count = noninnodb.scalar()
self.assertEqual(count, 0, "%d non InnoDB tables created" % count)
-
- def test_migration_149_postgres(self):
- """Test updating a table with IPAddress columns."""
- if not _is_backend_avail('postgres'):
- self.skipTest("postgres not available")
-
- connect_string = _get_connect_string("postgres")
- engine = sqlalchemy.create_engine(connect_string)
-
- self.engines["postgrescitest"] = engine
- self.test_databases["postgrescitest"] = connect_string
-
- self._reset_databases()
- migration_api.version_control(engine, TestMigrations.REPOSITORY,
- migration.INIT_VERSION)
-
- connection = engine.connect()
-
- self._migrate_up(engine, 148)
- IPS = ("127.0.0.1", "255.255.255.255", "2001:db8::1:2", "::1")
- connection.execute("INSERT INTO provider_fw_rules "
- " (protocol, from_port, to_port, cidr)"
- "VALUES ('tcp', 1234, 1234, '%s'), "
- " ('tcp', 1234, 1234, '%s'), "
- " ('tcp', 1234, 1234, '%s'), "
- " ('tcp', 1234, 1234, '%s')" % IPS)
- self.assertEqual('character varying',
- connection.execute(
- "SELECT data_type FROM INFORMATION_SCHEMA.COLUMNS "
- "WHERE table_name='provider_fw_rules' "
- "AND table_catalog='openstack_citest' "
- "AND column_name='cidr'").scalar())
-
- self._migrate_up(engine, 149)
- self.assertEqual(IPS,
- tuple(tup[0] for tup in connection.execute(
- "SELECT cidr from provider_fw_rules").fetchall()))
- self.assertEqual('inet',
- connection.execute(
- "SELECT data_type FROM INFORMATION_SCHEMA.COLUMNS "
- "WHERE table_name='provider_fw_rules' "
- "AND table_catalog='openstack_citest' "
- "AND column_name='cidr'").scalar())
- connection.close()
-
- def _check_mysql_migration_149(self, engine):
- """Test updating a table with IPAddress columns."""
- self._reset_databases()
- migration_api.version_control(engine, TestMigrations.REPOSITORY,
- migration.INIT_VERSION)
-
- uri = _get_connect_string("mysql", database="openstack_citest")
- connection = sqlalchemy.create_engine(uri).connect()
-
- self._migrate_up(engine, 148)
-
- IPS = ("127.0.0.1", "255.255.255.255", "2001:db8::1:2", "::1")
- connection.execute("INSERT INTO provider_fw_rules "
- " (protocol, from_port, to_port, cidr)"
- "VALUES ('tcp', 1234, 1234, '%s'), "
- " ('tcp', 1234, 1234, '%s'), "
- " ('tcp', 1234, 1234, '%s'), "
- " ('tcp', 1234, 1234, '%s')" % IPS)
- self.assertEqual('varchar(255)',
- connection.execute(
- "SELECT column_type FROM INFORMATION_SCHEMA.COLUMNS "
- "WHERE table_name='provider_fw_rules' "
- "AND table_schema='openstack_citest' "
- "AND column_name='cidr'").scalar())
-
connection.close()
- self._migrate_up(engine, 149)
-
- connection = sqlalchemy.create_engine(uri).connect()
-
- self.assertEqual(IPS,
- tuple(tup[0] for tup in connection.execute(
- "SELECT cidr from provider_fw_rules").fetchall()))
- self.assertEqual('varchar(39)',
- connection.execute(
- "SELECT column_type FROM INFORMATION_SCHEMA.COLUMNS "
- "WHERE table_name='provider_fw_rules' "
- "AND table_schema='openstack_citest' "
- "AND column_name='cidr'").scalar())
-
def _walk_versions(self, engine=None, snake_walk=False, downgrade=True):
# Determine latest version script from the repo, then
# upgrade from 1 through to the latest, with no data
@@ -366,7 +279,7 @@ class TestMigrations(test.TestCase):
for version in xrange(migration.INIT_VERSION + 2,
TestMigrations.REPOSITORY.latest + 1):
# upgrade -> downgrade -> upgrade
- self._migrate_up(engine, version)
+ self._migrate_up(engine, version, with_data=True)
if snake_walk:
self._migrate_down(engine, version)
self._migrate_up(engine, version)
@@ -391,7 +304,19 @@ class TestMigrations(test.TestCase):
migration_api.db_version(engine,
TestMigrations.REPOSITORY))
- def _migrate_up(self, engine, version):
+ def _migrate_up(self, engine, version, with_data=False):
+ """migrate up to a new version of the db.
+
+ We allow for data insertion and post checks at every
+ migration version with special _prerun_### and
+ _check_### functions in the main test.
+ """
+ if with_data:
+ data = None
+ prerun = getattr(self, "_prerun_%d" % version, None)
+ if prerun:
+ data = prerun(engine)
+
migration_api.upgrade(engine,
TestMigrations.REPOSITORY,
version)
@@ -399,94 +324,198 @@ class TestMigrations(test.TestCase):
migration_api.db_version(engine,
TestMigrations.REPOSITORY))
- def test_migration_146(self):
- name = 'name'
- az = 'custom_az'
-
- def _145_check():
- agg = aggregates.select(aggregates.c.id == 1).execute().first()
- self.assertEqual(name, agg.name)
- self.assertEqual(az, agg.availability_zone)
-
- for key, engine in self.engines.items():
- migration_api.version_control(engine, TestMigrations.REPOSITORY,
- migration.INIT_VERSION)
- migration_api.upgrade(engine, TestMigrations.REPOSITORY, 145)
- metadata = sqlalchemy.schema.MetaData()
- metadata.bind = engine
- aggregates = sqlalchemy.Table('aggregates', metadata,
- autoload=True)
-
- aggregates.insert().values(id=1, availability_zone=az,
- aggregate_name=1, name=name).execute()
-
- _145_check()
-
- migration_api.upgrade(engine, TestMigrations.REPOSITORY, 146)
-
- aggregate_metadata = sqlalchemy.Table('aggregate_metadata',
- metadata, autoload=True)
- metadata = aggregate_metadata.select(aggregate_metadata.c.
- aggregate_id == 1).execute().first()
- self.assertEqual(az, metadata['value'])
-
- migration_api.downgrade(engine, TestMigrations.REPOSITORY, 145)
- _145_check()
-
- def test_migration_147(self):
+ if with_data:
+ check = getattr(self, "_check_%d" % version, None)
+ if check:
+ check(engine, data)
+
+ # migration 146, availability zone transition
+ def _prerun_146(self, engine):
+ data = {
+ 'id': 1,
+ 'availability_zone': 'custom_az',
+ 'aggregate_name': 1,
+ 'name': 'name',
+ }
+
+ aggregates = get_table(engine, 'aggregates')
+ aggregates.insert().values(data).execute()
+ return data
+
+ def _check_146(self, engine, data):
+ aggregate_md = get_table(engine, 'aggregate_metadata')
+ md = aggregate_md.select(
+ aggregate_md.c.aggregate_id == 1).execute().first()
+ self.assertEqual(data['availability_zone'], md['value'])
+
+ # migration 147, availability zone transition for services
+ def _prerun_147(self, engine):
az = 'test_zone'
host1 = 'compute-host1'
host2 = 'compute-host2'
-
- def _146_check():
- service = services.select(services.c.id == 1).execute().first()
- self.assertEqual(az, service.availability_zone)
- self.assertEqual(host1, service.host)
- service = services.select(services.c.id == 2).execute().first()
- self.assertNotEqual(az, service.availability_zone)
- service = services.select(services.c.id == 3).execute().first()
- self.assertEqual(az, service.availability_zone)
- self.assertEqual(host2, service.host)
-
- for key, engine in self.engines.items():
- migration_api.version_control(engine, TestMigrations.REPOSITORY,
- migration.INIT_VERSION)
- migration_api.upgrade(engine, TestMigrations.REPOSITORY, 146)
- metadata = sqlalchemy.schema.MetaData()
- metadata.bind = engine
-
- #populate service table
- services = sqlalchemy.Table('services', metadata,
- autoload=True)
- services.insert().values(id=1, host=host1,
- binary='nova-compute', topic='compute', report_count=0,
- availability_zone=az).execute()
- services.insert().values(id=2, host='sched-host',
- binary='nova-scheduler', topic='scheduler', report_count=0,
- availability_zone='ignore_me').execute()
- services.insert().values(id=3, host=host2,
- binary='nova-compute', topic='compute', report_count=0,
- availability_zone=az).execute()
-
- _146_check()
-
- migration_api.upgrade(engine, TestMigrations.REPOSITORY, 147)
-
- # check aggregate metadata
- aggregate_metadata = sqlalchemy.Table('aggregate_metadata',
- metadata, autoload=True)
- aggregate_hosts = sqlalchemy.Table('aggregate_hosts',
- metadata, autoload=True)
- metadata = aggregate_metadata.select(aggregate_metadata.c.
- aggregate_id == 1).execute().first()
- self.assertEqual(az, metadata['value'])
- self.assertEqual(aggregate_hosts.select(
- aggregate_hosts.c.aggregate_id == 1).execute().
- first().host, host1)
- blank = [h for h in aggregate_hosts.select(
- aggregate_hosts.c.aggregate_id == 2).execute()]
- self.assertEqual(blank, [])
-
- migration_api.downgrade(engine, TestMigrations.REPOSITORY, 146)
-
- _146_check()
+ # start at id == 2 because we already inserted one
+ data = [
+ {'id': 1, 'host': host1,
+ 'binary': 'nova-compute', 'topic': 'compute',
+ 'report_count': 0, 'availability_zone': az},
+ {'id': 2, 'host': 'sched-host',
+ 'binary': 'nova-scheduler', 'topic': 'scheduler',
+ 'report_count': 0, 'availability_zone': 'ignore_me'},
+ {'id': 3, 'host': host2,
+ 'binary': 'nova-compute', 'topic': 'compute',
+ 'report_count': 0, 'availability_zone': az},
+ ]
+
+ services = get_table(engine, 'services')
+ engine.execute(services.insert(), data)
+ return data
+
+ def _check_147(self, engine, data):
+ aggregate_md = get_table(engine, 'aggregate_metadata')
+ aggregate_hosts = get_table(engine, 'aggregate_hosts')
+ # NOTE(sdague): hard coded to id == 2, because we added to
+ # aggregate_metadata previously
+ for item in data:
+ md = aggregate_md.select(
+ aggregate_md.c.aggregate_id == 2).execute().first()
+ if item['binary'] == "nova-compute":
+ self.assertEqual(item['availability_zone'], md['value'])
+
+ host = aggregate_hosts.select(
+ aggregate_hosts.c.aggregate_id == 2
+ ).execute().first()
+ self.assertEqual(host['host'], data[0]['host'])
+
+ # NOTE(sdague): id 3 is just non-existent
+ host = aggregate_hosts.select(
+ aggregate_hosts.c.aggregate_id == 3
+ ).execute().first()
+ self.assertEqual(host, None)
+
+ # migration 149, changes IPAddr storage format
+ def _prerun_149(self, engine):
+ provider_fw_rules = get_table(engine, 'provider_fw_rules')
+ data = [
+ {'protocol': 'tcp', 'from_port': 1234,
+ 'to_port': 1234, 'cidr': "127.0.0.1"},
+ {'protocol': 'tcp', 'from_port': 1234,
+ 'to_port': 1234, 'cidr': "255.255.255.255"},
+ {'protocol': 'tcp', 'from_port': 1234,
+ 'to_port': 1234, 'cidr': "2001:db8::1:2"},
+ {'protocol': 'tcp', 'from_port': 1234,
+ 'to_port': 1234, 'cidr': "::1"}
+ ]
+ engine.execute(provider_fw_rules.insert(), data)
+ return data
+
+ def _check_149(self, engine, data):
+ provider_fw_rules = get_table(engine, 'provider_fw_rules')
+ result = provider_fw_rules.select().execute()
+
+ iplist = map(lambda x: x['cidr'], data)
+
+ for row in result:
+ self.assertIn(row['cidr'], iplist)
+
+ # migration 152 - convert deleted from boolean to int
+ def _prerun_152(self, engine):
+ host1 = 'compute-host1'
+ host2 = 'compute-host2'
+ # NOTE(sdague): start at #4 because services data already in table
+ # from 147
+ services_data = [
+ {'id': 4, 'host': host1, 'binary': 'nova-compute',
+ 'report_count': 0, 'topic': 'compute', 'deleted': False},
+ {'id': 5, 'host': host1, 'binary': 'nova-compute',
+ 'report_count': 0, 'topic': 'compute', 'deleted': True}
+ ]
+ volumes_data = [
+ {'id': 'first', 'host': host1, 'deleted': False},
+ {'id': 'second', 'host': host2, 'deleted': True}
+ ]
+
+ services = get_table(engine, 'services')
+ engine.execute(services.insert(), services_data)
+
+ volumes = get_table(engine, 'volumes')
+ engine.execute(volumes.insert(), volumes_data)
+ return dict(services=services_data, volumes=volumes_data)
+
+ def _check_152(self, engine, data):
+ services = get_table(engine, 'services')
+ service = services.select(services.c.id == 4).execute().first()
+ self.assertEqual(0, service.deleted)
+ service = services.select(services.c.id == 5).execute().first()
+ self.assertEqual(service.id, service.deleted)
+
+ volumes = get_table(engine, 'volumes')
+ volume = volumes.select(volumes.c.id == "first").execute().first()
+ self.assertEqual("", volume.deleted)
+ volume = volumes.select(volumes.c.id == "second").execute().first()
+ self.assertEqual(volume.id, volume.deleted)
+
+ # migration 153, copy flavor information into system_metadata
+ def _prerun_153(self, engine):
+ fake_types = [
+ dict(id=10, name='type1', memory_mb=128, vcpus=1,
+ root_gb=10, ephemeral_gb=0, flavorid="1", swap=0,
+ rxtx_factor=1.0, vcpu_weight=1, disabled=False,
+ is_public=True),
+ dict(id=11, name='type2', memory_mb=512, vcpus=1,
+ root_gb=10, ephemeral_gb=5, flavorid="2", swap=0,
+ rxtx_factor=1.5, vcpu_weight=2, disabled=False,
+ is_public=True),
+ dict(id=12, name='type3', memory_mb=128, vcpus=1,
+ root_gb=10, ephemeral_gb=0, flavorid="3", swap=0,
+ rxtx_factor=1.0, vcpu_weight=1, disabled=False,
+ is_public=False),
+ dict(id=13, name='type4', memory_mb=128, vcpus=1,
+ root_gb=10, ephemeral_gb=0, flavorid="4", swap=0,
+ rxtx_factor=1.0, vcpu_weight=1, disabled=True,
+ is_public=True),
+ dict(id=14, name='type5', memory_mb=128, vcpus=1,
+ root_gb=10, ephemeral_gb=0, flavorid="5", swap=0,
+ rxtx_factor=1.0, vcpu_weight=1, disabled=True,
+ is_public=False),
+ ]
+
+ fake_instances = [
+ dict(uuid='m153-uuid1', instance_type_id=10),
+ dict(uuid='m153-uuid2', instance_type_id=11),
+ dict(uuid='m153-uuid3', instance_type_id=12),
+ dict(uuid='m153-uuid4', instance_type_id=13),
+ # NOTE(danms): no use of type5
+ ]
+
+ instances = get_table(engine, 'instances')
+ instance_types = get_table(engine, 'instance_types')
+ engine.execute(instance_types.insert(), fake_types)
+ engine.execute(instances.insert(), fake_instances)
+
+ return fake_types, fake_instances
+
+ def _check_153(self, engine, data):
+ fake_types, fake_instances = data
+ # NOTE(danms): Fetch all the tables and data from scratch after change
+ instances = get_table(engine, 'instances')
+ instance_types = get_table(engine, 'instance_types')
+ sys_meta = get_table(engine, 'instance_system_metadata')
+
+ # Collect all system metadata, indexed by instance_uuid
+ metadata = collections.defaultdict(dict)
+ for values in sys_meta.select().execute():
+ metadata[values['instance_uuid']][values['key']] = values['value']
+
+ # Taken from nova/compute/api.py
+ instance_type_props = ['id', 'name', 'memory_mb', 'vcpus',
+ 'root_gb', 'ephemeral_gb', 'flavorid',
+ 'swap', 'rxtx_factor', 'vcpu_weight']
+
+ for instance in fake_instances:
+ inst_sys_meta = metadata[instance['uuid']]
+ inst_type = fake_types[instance['instance_type_id'] - 10]
+ for prop in instance_type_props:
+ prop_name = 'instance_type_%s' % prop
+ self.assertIn(prop_name, inst_sys_meta)
+ self.assertEqual(str(inst_sys_meta[prop_name]),
+ str(inst_type[prop]))
diff --git a/nova/tests/test_notifications.py b/nova/tests/test_notifications.py
index a300028a0..aec6c8f67 100644
--- a/nova/tests/test_notifications.py
+++ b/nova/tests/test_notifications.py
@@ -187,8 +187,6 @@ class NotificationsTestCase(test.TestCase):
params = {"task_state": task_states.SPAWNING}
(old_ref, new_ref) = db.instance_update_and_get_original(self.context,
self.instance['uuid'], params)
- print old_ref["task_state"]
- print new_ref["task_state"]
notifications.send_update(self.context, old_ref, new_ref)
self.assertEquals(1, len(test_notifier.NOTIFICATIONS))
diff --git a/nova/tests/test_periodic_tasks.py b/nova/tests/test_periodic_tasks.py
index 39669967f..621e86b3a 100644
--- a/nova/tests/test_periodic_tasks.py
+++ b/nova/tests/test_periodic_tasks.py
@@ -15,10 +15,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-
-import fixtures
import time
+from testtools import matchers
+
from nova import manager
from nova import test
@@ -44,10 +44,11 @@ class ManagerMetaTestCase(test.TestCase):
return 'baz'
m = Manager()
- self.assertEqual(2, len(m._periodic_tasks))
+ self.assertThat(m._periodic_tasks, matchers.HasLength(2))
self.assertEqual(None, m._periodic_spacing['foo'])
self.assertEqual(4, m._periodic_spacing['bar'])
- self.assertFalse('baz' in m._periodic_spacing)
+ self.assertThat(
+ m._periodic_spacing, matchers.Not(matchers.Contains('baz')))
class Manager(test.TestCase):
@@ -60,7 +61,7 @@ class Manager(test.TestCase):
return 'bar'
m = Manager()
- self.assertEqual(1, len(m._periodic_tasks))
+ self.assertThat(m._periodic_tasks, matchers.HasLength(1))
self.assertEqual(200, m._periodic_spacing['bar'])
# Now a single pass of the periodic tasks
@@ -87,8 +88,8 @@ class Manager(test.TestCase):
m.periodic_tasks(None)
time.sleep(0.1)
idle = m.periodic_tasks(None)
- self.assertTrue(idle > 9.7)
- self.assertTrue(idle < 9.9)
+ self.assertThat(idle, matchers.GreaterThan(9.7))
+ self.assertThat(idle, matchers.LessThan(9.9))
def test_periodic_tasks_disabled(self):
class Manager(manager.Manager):
@@ -109,7 +110,7 @@ class Manager(test.TestCase):
return 'bar'
m = Manager()
- self.assertEqual(1, len(m._periodic_tasks))
+ self.assertThat(m._periodic_tasks, matchers.HasLength(1))
def test_external_running_elsewhere(self):
self.flags(run_external_periodic_tasks=False)
@@ -120,4 +121,4 @@ class Manager(test.TestCase):
return 'bar'
m = Manager()
- self.assertEqual(0, len(m._periodic_tasks))
+ self.assertEqual([], m._periodic_tasks)
diff --git a/nova/tests/test_sqlalchemy.py b/nova/tests/test_sqlalchemy.py
index f79d607f8..5c7f4450b 100644
--- a/nova/tests/test_sqlalchemy.py
+++ b/nova/tests/test_sqlalchemy.py
@@ -22,8 +22,14 @@ try:
except ImportError:
MySQLdb = None
+from sqlalchemy import Column, MetaData, Table, UniqueConstraint
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy import DateTime, Integer
+
from nova import context
+from nova.db.sqlalchemy import models
from nova.db.sqlalchemy import session
+from nova import exception
from nova import test
@@ -64,3 +70,60 @@ class DbPoolTestCase(test.TestCase):
self.assertEqual(info['kwargs']['max_idle'], 11)
self.assertEqual(info['kwargs']['min_size'], 21)
self.assertEqual(info['kwargs']['max_size'], 42)
+
+
+BASE = declarative_base()
+_TABLE_NAME = '__tmp__test__tmp__'
+
+
+class TmpTable(BASE, models.NovaBase):
+ __tablename__ = _TABLE_NAME
+ id = Column(Integer, primary_key=True)
+ foo = Column(Integer)
+
+
+class SessionErrorWrapperTestCase(test.TestCase):
+ def setUp(self):
+ super(SessionErrorWrapperTestCase, self).setUp()
+ meta = MetaData()
+ meta.bind = session.get_engine()
+ test_table = Table(_TABLE_NAME, meta,
+ Column('id', Integer, primary_key=True,
+ nullable=False),
+ Column('deleted', Integer, default=0),
+ Column('deleted_at', DateTime),
+ Column('updated_at', DateTime),
+ Column('created_at', DateTime),
+ Column('foo', Integer),
+ UniqueConstraint('foo', name='uniq_foo'))
+ test_table.create()
+
+ def tearDown(self):
+ super(SessionErrorWrapperTestCase, self).tearDown()
+ meta = MetaData()
+ meta.bind = session.get_engine()
+ test_table = Table(_TABLE_NAME, meta, autoload=True)
+ test_table.drop()
+
+ def test_flush_wrapper(self):
+ tbl = TmpTable()
+ tbl.update({'foo': 10})
+ tbl.save()
+
+ tbl2 = TmpTable()
+ tbl2.update({'foo': 10})
+ self.assertRaises(exception.DBDuplicateEntry, tbl2.save)
+
+ def test_execute_wrapper(self):
+ _session = session.get_session()
+ with _session.begin():
+ for i in [10, 20]:
+ tbl = TmpTable()
+ tbl.update({'foo': i})
+ tbl.save(session=_session)
+
+ method = _session.query(TmpTable).\
+ filter_by(foo=10).\
+ update
+ self.assertRaises(exception.DBDuplicateEntry,
+ method, {'foo': 20})
diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py
index 9eab72c5b..84d56cadf 100644
--- a/nova/tests/test_utils.py
+++ b/nova/tests/test_utils.py
@@ -778,3 +778,14 @@ class IntLikeTestCase(test.TestCase):
self.assertFalse(
utils.is_int_like("0cc3346e-9fef-4445-abe6-5d2b2690ec64"))
self.assertFalse(utils.is_int_like("a1"))
+
+
+class MetadataToDictTestCase(test.TestCase):
+ def test_metadata_to_dict(self):
+ self.assertEqual(utils.metadata_to_dict(
+ [{'key': 'foo1', 'value': 'bar'},
+ {'key': 'foo2', 'value': 'baz'}]),
+ {'foo1': 'bar', 'foo2': 'baz'})
+
+ def test_metadata_to_dict_empty(self):
+ self.assertEqual(utils.metadata_to_dict([]), {})
diff --git a/nova/tests/test_virt_disk.py b/nova/tests/test_virt_disk.py
index 902d49704..0c51e8267 100644
--- a/nova/tests/test_virt_disk.py
+++ b/nova/tests/test_virt_disk.py
@@ -14,8 +14,10 @@
# License for the specific language governing permissions and limitations
# under the License.
+import os
import sys
+from nova import exception
from nova import test
from nova.tests import fakeguestfs
from nova.virt.disk import api as diskapi
@@ -29,6 +31,25 @@ class VirtDiskTest(test.TestCase):
sys.modules['guestfs'] = fakeguestfs
vfsguestfs.guestfs = fakeguestfs
+ def test_inject_data(self):
+
+ self.assertTrue(diskapi.inject_data("/some/file", use_cow=True))
+
+ self.assertTrue(diskapi.inject_data("/some/file",
+ mandatory=('files',)))
+
+ self.assertTrue(diskapi.inject_data("/some/file", key="mysshkey",
+ mandatory=('key',)))
+
+ os_name = os.name
+ os.name = 'nt' # Cause password injection to fail
+ self.assertRaises(exception.NovaException,
+ diskapi.inject_data,
+ "/some/file", admin_password="p",
+ mandatory=('admin_password',))
+ self.assertFalse(diskapi.inject_data("/some/file", admin_password="p"))
+ os.name = os_name
+
def test_inject_data_key(self):
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
@@ -46,7 +67,7 @@ class VirtDiskTest(test.TestCase):
"key was injected by Nova\nmysshkey\n",
'gid': 100,
'uid': 100,
- 'mode': 0700})
+ 'mode': 0600})
vfs.teardown()
@@ -80,7 +101,7 @@ class VirtDiskTest(test.TestCase):
"key was injected by Nova\nmysshkey\n",
'gid': 100,
'uid': 100,
- 'mode': 0700})
+ 'mode': 0600})
vfs.teardown()
diff --git a/nova/tests/test_virt_disk_vfs_localfs.py b/nova/tests/test_virt_disk_vfs_localfs.py
index 806ed01d8..af4571dd2 100644
--- a/nova/tests/test_virt_disk_vfs_localfs.py
+++ b/nova/tests/test_virt_disk_vfs_localfs.py
@@ -104,7 +104,6 @@ def fake_execute(*args, **kwargs):
else:
path = args[1]
append = False
- print str(files)
if not path in files:
files[path] = {
"content": "Hello World",
diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py
index 9747ecccd..e8e7c329a 100644
--- a/nova/tests/test_virt_drivers.py
+++ b/nova/tests/test_virt_drivers.py
@@ -20,7 +20,7 @@ import netaddr
import sys
import traceback
-from nova.compute.manager import ComputeManager
+from nova.compute import manager
from nova import exception
from nova.openstack.common import importutils
from nova.openstack.common import log as logging
@@ -119,8 +119,6 @@ class _FakeDriverBackendTestCase(object):
def _teardown_fakelibvirt(self):
# Restore libvirt
- import nova.virt.libvirt.driver
- import nova.virt.libvirt.firewall
if self.saved_libvirt:
sys.modules['libvirt'] = self.saved_libvirt
@@ -159,7 +157,7 @@ class VirtDriverLoaderTestCase(_FakeDriverBackendTestCase, test.TestCase):
# NOTE(sdague) the try block is to make it easier to debug a
# failure by knowing which driver broke
try:
- cm = ComputeManager()
+ cm = manager.ComputeManager()
except Exception as e:
self.fail("Couldn't load driver %s - %s" % (cls, e))
@@ -173,7 +171,7 @@ class VirtDriverLoaderTestCase(_FakeDriverBackendTestCase, test.TestCase):
raise test.TestingException()
self.stubs.Set(sys, 'exit', _fake_exit)
- self.assertRaises(test.TestingException, ComputeManager)
+ self.assertRaises(test.TestingException, manager.ComputeManager)
class _VirtDriverTestCase(_FakeDriverBackendTestCase):
diff --git a/nova/tests/test_vmwareapi.py b/nova/tests/test_vmwareapi.py
index 577d227ce..34f03a555 100644
--- a/nova/tests/test_vmwareapi.py
+++ b/nova/tests/test_vmwareapi.py
@@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
@@ -41,7 +42,9 @@ class VMwareAPIVMTestCase(test.TestCase):
self.context = context.RequestContext('fake', 'fake', is_admin=False)
self.flags(vmwareapi_host_ip='test_url',
vmwareapi_host_username='test_username',
- vmwareapi_host_password='test_pass')
+ vmwareapi_host_password='test_pass',
+ vnc_enabled=False,
+ use_linked_clone=False)
self.user_id = 'fake'
self.project_id = 'fake'
self.context = context.RequestContext(self.user_id, self.project_id)
@@ -155,6 +158,12 @@ class VMwareAPIVMTestCase(test.TestCase):
instances = self.conn.list_instances()
self.assertEquals(len(instances), 1)
+ def test_list_interfaces(self):
+ self._create_vm()
+ interfaces = self.conn.list_interfaces(1)
+ self.assertEquals(len(interfaces), 1)
+ self.assertEquals(interfaces[0], 4000)
+
def test_spawn(self):
self._create_vm()
info = self.conn.get_info({'name': 1})
@@ -205,7 +214,7 @@ class VMwareAPIVMTestCase(test.TestCase):
self._check_vm_info(info, power_state.RUNNING)
self.conn.suspend(self.instance)
info = self.conn.get_info({'name': 1})
- self._check_vm_info(info, power_state.PAUSED)
+ self._check_vm_info(info, power_state.SUSPENDED)
self.assertRaises(exception.InstanceRebootFailure, self.conn.reboot,
self.instance, self.network_info, 'SOFT')
@@ -215,7 +224,7 @@ class VMwareAPIVMTestCase(test.TestCase):
self._check_vm_info(info, power_state.RUNNING)
self.conn.suspend(self.instance)
info = self.conn.get_info({'name': 1})
- self._check_vm_info(info, power_state.PAUSED)
+ self._check_vm_info(info, power_state.SUSPENDED)
def test_suspend_non_existent(self):
self._create_instance_in_the_db()
@@ -228,7 +237,7 @@ class VMwareAPIVMTestCase(test.TestCase):
self._check_vm_info(info, power_state.RUNNING)
self.conn.suspend(self.instance)
info = self.conn.get_info({'name': 1})
- self._check_vm_info(info, power_state.PAUSED)
+ self._check_vm_info(info, power_state.SUSPENDED)
self.conn.resume(self.instance, self.network_info)
info = self.conn.get_info({'name': 1})
self._check_vm_info(info, power_state.RUNNING)
@@ -245,6 +254,43 @@ class VMwareAPIVMTestCase(test.TestCase):
self.assertRaises(exception.InstanceResumeFailure, self.conn.resume,
self.instance, self.network_info)
+ def test_power_on(self):
+ self._create_vm()
+ info = self.conn.get_info({'name': 1})
+ self._check_vm_info(info, power_state.RUNNING)
+ self.conn.power_off(self.instance)
+ info = self.conn.get_info({'name': 1})
+ self._check_vm_info(info, power_state.SHUTDOWN)
+ self.conn.power_on(self.instance)
+ info = self.conn.get_info({'name': 1})
+ self._check_vm_info(info, power_state.RUNNING)
+
+ def test_power_on_non_existent(self):
+ self._create_instance_in_the_db()
+ self.assertRaises(exception.InstanceNotFound, self.conn.power_on,
+ self.instance)
+
+ def test_power_off(self):
+ self._create_vm()
+ info = self.conn.get_info({'name': 1})
+ self._check_vm_info(info, power_state.RUNNING)
+ self.conn.power_off(self.instance)
+ info = self.conn.get_info({'name': 1})
+ self._check_vm_info(info, power_state.SHUTDOWN)
+
+ def test_power_off_non_existent(self):
+ self._create_instance_in_the_db()
+ self.assertRaises(exception.InstanceNotFound, self.conn.power_off,
+ self.instance)
+
+ def test_power_off_suspended(self):
+ self._create_vm()
+ self.conn.suspend(self.instance)
+ info = self.conn.get_info({'name': 1})
+ self._check_vm_info(info, power_state.SUSPENDED)
+ self.assertRaises(exception.InstancePowerOffFailure,
+ self.conn.power_off, self.instance)
+
def test_get_info(self):
self._create_vm()
info = self.conn.get_info({'name': 1})
@@ -276,3 +322,48 @@ class VMwareAPIVMTestCase(test.TestCase):
def test_get_console_output(self):
pass
+
+
+class VMwareAPIHostTestCase(test.TestCase):
+ """Unit tests for Vmware API host calls."""
+
+ def setUp(self):
+ super(VMwareAPIHostTestCase, self).setUp()
+ self.flags(vmwareapi_host_ip='test_url',
+ vmwareapi_host_username='test_username',
+ vmwareapi_host_password='test_pass')
+ vmwareapi_fake.reset()
+ stubs.set_stubs(self.stubs)
+ self.conn = driver.VMwareESXDriver(False)
+
+ def tearDown(self):
+ super(VMwareAPIHostTestCase, self).tearDown()
+ vmwareapi_fake.cleanup()
+
+ def test_host_state(self):
+ stats = self.conn.get_host_stats()
+ self.assertEquals(stats['vcpus'], 16)
+ self.assertEquals(stats['disk_total'], 1024)
+ self.assertEquals(stats['disk_available'], 500)
+ self.assertEquals(stats['disk_used'], 1024 - 500)
+ self.assertEquals(stats['host_memory_total'], 1024)
+ self.assertEquals(stats['host_memory_free'], 1024 - 500)
+
+ def _test_host_action(self, method, action, expected=None):
+ result = method('host', action)
+ self.assertEqual(result, expected)
+
+ def test_host_reboot(self):
+ self._test_host_action(self.conn.host_power_action, 'reboot')
+
+ def test_host_shutdown(self):
+ self._test_host_action(self.conn.host_power_action, 'shutdown')
+
+ def test_host_startup(self):
+ self._test_host_action(self.conn.host_power_action, 'startup')
+
+ def test_host_maintenance_on(self):
+ self._test_host_action(self.conn.host_maintenance_mode, True)
+
+ def test_host_maintenance_off(self):
+ self._test_host_action(self.conn.host_maintenance_mode, False)
diff --git a/nova/tests/test_wsgi.py b/nova/tests/test_wsgi.py
index b4b25ed97..cd64688a2 100644
--- a/nova/tests/test_wsgi.py
+++ b/nova/tests/test_wsgi.py
@@ -21,9 +21,19 @@
import os.path
import tempfile
+import eventlet
+import httplib2
+import paste
+
import nova.exception
from nova import test
import nova.wsgi
+import urllib2
+import webob
+
+SSL_CERT_DIR = os.path.normpath(os.path.join(
+ os.path.dirname(os.path.abspath(__file__)),
+ 'ssl_cert'))
class TestLoaderNothingExists(test.TestCase):
@@ -99,3 +109,111 @@ class TestWSGIServer(test.TestCase):
self.assertNotEqual(0, server.port)
server.stop()
server.wait()
+
+ def test_uri_length_limit(self):
+ server = nova.wsgi.Server("test_uri_length_limit", None,
+ host="127.0.0.1", max_url_len=16384)
+ server.start()
+
+ uri = "http://127.0.0.1:%d/%s" % (server.port, 10000 * 'x')
+ resp, _ = httplib2.Http().request(uri)
+ eventlet.sleep(0)
+ self.assertNotEqual(resp.status,
+ paste.httpexceptions.HTTPRequestURITooLong.code)
+
+ uri = "http://127.0.0.1:%d/%s" % (server.port, 20000 * 'x')
+ resp, _ = httplib2.Http().request(uri)
+ eventlet.sleep(0)
+ self.assertEqual(resp.status,
+ paste.httpexceptions.HTTPRequestURITooLong.code)
+ server.stop()
+ server.wait()
+
+
+class TestWSGIServerWithSSL(test.TestCase):
+ """WSGI server with SSL tests."""
+
+ def setUp(self):
+ super(TestWSGIServerWithSSL, self).setUp()
+ self.flags(enabled_ssl_apis=['fake_ssl'],
+ ssl_cert_file=os.path.join(SSL_CERT_DIR, 'certificate.crt'),
+ ssl_key_file=os.path.join(SSL_CERT_DIR, 'privatekey.key'))
+
+ def test_ssl_server(self):
+
+ def test_app(env, start_response):
+ start_response('200 OK', {})
+ return ['PONG']
+
+ fake_ssl_server = nova.wsgi.Server("fake_ssl", test_app,
+ host="127.0.0.1", port=0,
+ use_ssl=True)
+ fake_ssl_server.start()
+ self.assertNotEqual(0, fake_ssl_server.port)
+
+ cli = eventlet.connect(("localhost", fake_ssl_server.port))
+ cli = eventlet.wrap_ssl(cli,
+ ca_certs=os.path.join(SSL_CERT_DIR, 'ca.crt'))
+
+ cli.write('POST / HTTP/1.1\r\nHost: localhost\r\n'
+ 'Connection: close\r\nContent-length:4\r\n\r\nPING')
+ response = cli.read(8192)
+ self.assertEquals(response[-4:], "PONG")
+
+ fake_ssl_server.stop()
+ fake_ssl_server.wait()
+
+ def test_two_servers(self):
+
+ def test_app(env, start_response):
+ start_response('200 OK', {})
+ return ['PONG']
+
+ fake_ssl_server = nova.wsgi.Server("fake_ssl", test_app,
+ host="127.0.0.1", port=0, use_ssl=True)
+ fake_ssl_server.start()
+ self.assertNotEqual(0, fake_ssl_server.port)
+
+ fake_server = nova.wsgi.Server("fake", test_app,
+ host="127.0.0.1", port=0)
+ fake_server.start()
+ self.assertNotEquals(0, fake_server.port)
+
+ cli = eventlet.connect(("localhost", fake_ssl_server.port))
+ cli = eventlet.wrap_ssl(cli,
+ ca_certs=os.path.join(SSL_CERT_DIR, 'ca.crt'))
+
+ cli.write('POST / HTTP/1.1\r\nHost: localhost\r\n'
+ 'Connection: close\r\nContent-length:4\r\n\r\nPING')
+ response = cli.read(8192)
+ self.assertEquals(response[-4:], "PONG")
+
+ cli = eventlet.connect(("localhost", fake_server.port))
+
+ cli.sendall('POST / HTTP/1.1\r\nHost: localhost\r\n'
+ 'Connection: close\r\nContent-length:4\r\n\r\nPING')
+ response = cli.recv(8192)
+ self.assertEquals(response[-4:], "PONG")
+
+ fake_ssl_server.stop()
+ fake_ssl_server.wait()
+
+ def test_app_using_ipv6_and_ssl(self):
+ greetings = 'Hello, World!!!'
+
+ @webob.dec.wsgify
+ def hello_world(req):
+ return greetings
+
+ server = nova.wsgi.Server("fake_ssl",
+ hello_world,
+ host="::1",
+ port=0,
+ use_ssl=True)
+ server.start()
+
+ response = urllib2.urlopen('https://[::1]:%d/' % server.port)
+ self.assertEquals(greetings, response.read())
+
+ server.stop()
+ server.wait()
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index 067e28a13..aa640810b 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -19,7 +19,6 @@
import ast
import base64
import contextlib
-import cPickle as pickle
import functools
import os
import re
@@ -48,6 +47,7 @@ from nova.virt.xenapi import agent
from nova.virt.xenapi import driver as xenapi_conn
from nova.virt.xenapi import fake as xenapi_fake
from nova.virt.xenapi import host
+from nova.virt.xenapi.imageupload import glance
from nova.virt.xenapi import pool
from nova.virt.xenapi import pool_states
from nova.virt.xenapi import vm_utils
@@ -431,15 +431,29 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
{'task_state': task_states.IMAGE_UPLOADING,
'expected_state': task_states.IMAGE_PENDING_UPLOAD}}]
func_call_matcher = matchers.FunctionCallMatcher(expected_calls)
+ image_id = "my_snapshot_id"
stubs.stubout_instance_snapshot(self.stubs)
stubs.stubout_is_snapshot(self.stubs)
# Stubbing out firewall driver as previous stub sets alters
# xml rpc result parsing
stubs.stubout_firewall_driver(self.stubs, self.conn)
+
instance = self._create_instance()
- image_id = "my_snapshot_id"
+ self.fake_upload_called = False
+
+ def fake_image_upload(_self, ctx, session, inst, vdi_uuids,
+ img_id):
+ self.fake_upload_called = True
+ self.assertEqual(ctx, self.context)
+ self.assertEqual(inst, instance)
+ self.assertTrue(isinstance(vdi_uuids, list))
+ self.assertEqual(img_id, image_id)
+
+ self.stubs.Set(glance.GlanceStore, 'upload_image',
+ fake_image_upload)
+
self.conn.snapshot(self.context, instance, image_id,
func_call_matcher.call)
@@ -469,6 +483,8 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
name_label = vdi_rec["name_label"]
self.assert_(not name_label.endswith('snapshot'))
+ self.assertTrue(self.fake_upload_called)
+
def create_vm_record(self, conn, os_type, name):
instances = conn.list_instances()
self.assertEquals(instances, [name])
@@ -2574,54 +2590,6 @@ class SwapXapiHostTestCase(test.TestCase):
"http://someserver", 'otherserver'))
-class VmUtilsTestCase(test.TestCase):
- """Unit tests for xenapi utils."""
-
- def test_upload_image(self):
- def fake_instance_system_metadata_get(context, uuid):
- return dict(image_a=1, image_b=2, image_c='c', d='d')
-
- def fake_get_sr_path(session):
- return "foo"
-
- class FakeInstance(dict):
- def __init__(self):
- super(FakeInstance, self).__init__({
- 'auto_disk_config': 'auto disk config',
- 'os_type': 'os type'})
-
- def __missing__(self, item):
- return "whatever"
-
- class FakeSession(object):
- def call_plugin(session_self, service, command, kwargs):
- self.kwargs = kwargs
-
- def call_plugin_serialized(session_self, service, command, *args,
- **kwargs):
- self.kwargs = kwargs
-
- def fake_dumps(thing):
- return thing
-
- self.stubs.Set(db, "instance_system_metadata_get",
- fake_instance_system_metadata_get)
- self.stubs.Set(vm_utils, "get_sr_path", fake_get_sr_path)
- self.stubs.Set(pickle, "dumps", fake_dumps)
-
- ctx = context.get_admin_context()
-
- instance = FakeInstance()
- session = FakeSession()
- vm_utils.upload_image(ctx, session, instance, "vmi uuids", "image id")
-
- actual = self.kwargs['properties']
- # Inheritance happens in another place, now
- expected = dict(auto_disk_config='auto disk config',
- os_type='os type')
- self.assertEquals(expected, actual)
-
-
class XenAPILiveMigrateTestCase(stubs.XenAPITestBase):
"""Unit tests for live_migration."""
def setUp(self):
diff --git a/nova/tests/virt/xenapi/imageupload/__init__.py b/nova/tests/virt/xenapi/imageupload/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/nova/tests/virt/xenapi/imageupload/__init__.py
diff --git a/nova/tests/virt/xenapi/imageupload/test_glance.py b/nova/tests/virt/xenapi/imageupload/test_glance.py
new file mode 100644
index 000000000..b0518228d
--- /dev/null
+++ b/nova/tests/virt/xenapi/imageupload/test_glance.py
@@ -0,0 +1,74 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack LLC.
+# 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.
+
+
+import mox
+
+from nova import context
+from nova import test
+from nova.virt.xenapi.imageupload import glance
+from nova.virt.xenapi import vm_utils
+
+
+class TestGlanceStore(test.TestCase):
+ def setUp(self):
+ super(TestGlanceStore, self).setUp()
+ self.store = glance.GlanceStore()
+ self.mox = mox.Mox()
+
+ def tearDown(self):
+ super(TestGlanceStore, self).tearDown()
+
+ def test_upload_image(self):
+ glance_host = '0.1.2.3'
+ glance_port = 8143
+ glance_use_ssl = False
+ sr_path = '/fake/sr/path'
+ self.flags(glance_host=glance_host)
+ self.flags(glance_port=glance_port)
+ self.flags(glance_api_insecure=glance_use_ssl)
+
+ def fake_get_sr_path(*_args, **_kwargs):
+ return sr_path
+
+ self.stubs.Set(vm_utils, 'get_sr_path', fake_get_sr_path)
+
+ ctx = context.RequestContext('user', 'project', auth_token='foobar')
+ properties = {
+ 'auto_disk_config': True,
+ 'os_type': 'default',
+ }
+ image_id = 'fake_image_uuid'
+ vdi_uuids = ['fake_vdi_uuid']
+ instance = {'uuid': 'blah'}
+ instance.update(properties)
+
+ params = {'vdi_uuids': vdi_uuids,
+ 'image_id': image_id,
+ 'glance_host': glance_host,
+ 'glance_port': glance_port,
+ 'glance_use_ssl': glance_use_ssl,
+ 'sr_path': sr_path,
+ 'auth_token': 'foobar',
+ 'properties': properties}
+ session = self.mox.CreateMockAnything()
+ session.call_plugin_serialized('glance', 'upload_vhd', **params)
+ self.mox.ReplayAll()
+
+ self.store.upload_image(ctx, session, instance, vdi_uuids, image_id)
+
+ self.mox.VerifyAll()
diff --git a/nova/tests/virt/xenapi/test_vm_utils.py b/nova/tests/virt/xenapi/test_vm_utils.py
index 275088af0..633e6f835 100644
--- a/nova/tests/virt/xenapi/test_vm_utils.py
+++ b/nova/tests/virt/xenapi/test_vm_utils.py
@@ -19,10 +19,8 @@
import contextlib
import fixtures
import mox
-import uuid
from nova import test
-from nova.tests.xenapi import stubs
from nova import utils
from nova.virt.xenapi import vm_utils
diff --git a/nova/tests/virt/xenapi/test_volumeops.py b/nova/tests/virt/xenapi/test_volumeops.py
index 7cc5c70da..844ae8459 100644
--- a/nova/tests/virt/xenapi/test_volumeops.py
+++ b/nova/tests/virt/xenapi/test_volumeops.py
@@ -21,6 +21,13 @@ from nova.virt.xenapi import volumeops
class VolumeAttachTestCase(test.TestCase):
def test_detach_volume_call(self):
+ registered_calls = []
+
+ def regcall(label):
+ def side_effect(*args, **kwargs):
+ registered_calls.append(label)
+ return side_effect
+
ops = volumeops.VolumeOps('session')
self.mox.StubOutWithMock(volumeops.vm_utils, 'vm_ref_or_raise')
self.mox.StubOutWithMock(volumeops.vm_utils, 'find_vbd_by_number')
@@ -45,10 +52,12 @@ class VolumeAttachTestCase(test.TestCase):
volumeops.vm_utils.unplug_vbd('session', 'vbdref')
- volumeops.vm_utils.destroy_vbd('session', 'vbdref')
+ volumeops.vm_utils.destroy_vbd('session', 'vbdref').WithSideEffects(
+ regcall('destroy_vbd'))
volumeops.volume_utils.find_sr_from_vbd(
- 'session', 'vbdref').AndReturn('srref')
+ 'session', 'vbdref').WithSideEffects(
+ regcall('find_sr_from_vbd')).AndReturn('srref')
volumeops.volume_utils.purge_sr('session', 'srref')
@@ -58,6 +67,9 @@ class VolumeAttachTestCase(test.TestCase):
dict(driver_volume_type='iscsi', data='conn_data'),
'instance_1', 'mountpoint')
+ self.assertEquals(
+ ['find_sr_from_vbd', 'destroy_vbd'], registered_calls)
+
def test_attach_volume_call(self):
ops = volumeops.VolumeOps('session')
self.mox.StubOutWithMock(ops, '_connect_volume')
diff --git a/nova/utils.py b/nova/utils.py
index 75cba0a7c..52d4868c9 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -76,6 +76,9 @@ utils_opts = [
default="/etc/nova/rootwrap.conf",
help='Path to the rootwrap configuration file to use for '
'running commands as root'),
+ cfg.StrOpt('tempdir',
+ default=None,
+ help='Explicitly specify the temporary working directory'),
]
CONF = cfg.CONF
CONF.register_opts(monkey_patch_opts)
@@ -507,14 +510,18 @@ def str_dict_replace(s, mapping):
class LazyPluggable(object):
"""A pluggable backend loaded lazily based on some value."""
- def __init__(self, pivot, **backends):
+ def __init__(self, pivot, config_group=None, **backends):
self.__backends = backends
self.__pivot = pivot
self.__backend = None
+ self.__config_group = config_group
def __get_backend(self):
if not self.__backend:
- backend_name = CONF[self.__pivot]
+ if self.__config_group is None:
+ backend_name = CONF[self.__pivot]
+ else:
+ backend_name = CONF[self.__config_group][self.__pivot]
if backend_name not in self.__backends:
msg = _('Invalid backend: %s') % backend_name
raise exception.NovaException(msg)
@@ -1143,6 +1150,7 @@ def temporary_chown(path, owner_uid=None):
@contextlib.contextmanager
def tempdir(**kwargs):
+ tempfile.tempdir = CONF.tempdir
tmpdir = tempfile.mkdtemp(**kwargs)
try:
yield tmpdir
@@ -1257,3 +1265,10 @@ def last_bytes(file_like_object, num):
remaining = file_like_object.tell()
return (file_like_object.read(), remaining)
+
+
+def metadata_to_dict(metadata):
+ result = {}
+ for item in metadata:
+ result[item['key']] = item['value']
+ return result
diff --git a/nova/virt/baremetal/__init__.py b/nova/virt/baremetal/__init__.py
index e3ecef821..9c8318660 100644
--- a/nova/virt/baremetal/__init__.py
+++ b/nova/virt/baremetal/__init__.py
@@ -12,4 +12,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-from nova.virt.baremetal.driver import BareMetalDriver
+from nova.virt.baremetal import driver
+
+BareMetalDriver = driver.BareMetalDriver
diff --git a/nova/virt/baremetal/db/api.py b/nova/virt/baremetal/db/api.py
index 206a59b4f..002425333 100644
--- a/nova/virt/baremetal/db/api.py
+++ b/nova/virt/baremetal/db/api.py
@@ -50,16 +50,21 @@ from nova import utils
# because utils.LazyPluggable doesn't support reading from
# option groups. See bug #1093043.
db_opts = [
- cfg.StrOpt('baremetal_db_backend',
+ cfg.StrOpt('db_backend',
default='sqlalchemy',
- help='The backend to use for db'),
+ help='The backend to use for bare-metal database'),
]
+baremetal_group = cfg.OptGroup(name='baremetal',
+ title='Baremetal Options')
+
CONF = cfg.CONF
-CONF.register_opts(db_opts)
+CONF.register_group(baremetal_group)
+CONF.register_opts(db_opts, baremetal_group)
IMPL = utils.LazyPluggable(
- 'baremetal_db_backend',
+ 'db_backend',
+ config_group='baremetal',
sqlalchemy='nova.virt.baremetal.db.sqlalchemy.api')
diff --git a/nova/virt/baremetal/db/migration.py b/nova/virt/baremetal/db/migration.py
index 40631bf45..d630ccf65 100644
--- a/nova/virt/baremetal/db/migration.py
+++ b/nova/virt/baremetal/db/migration.py
@@ -22,7 +22,8 @@ from nova import utils
IMPL = utils.LazyPluggable(
- 'baremetal_db_backend',
+ 'db_backend',
+ config_group='baremetal',
sqlalchemy='nova.virt.baremetal.db.sqlalchemy.migration')
INIT_VERSION = 0
diff --git a/nova/virt/baremetal/db/sqlalchemy/api.py b/nova/virt/baremetal/db/sqlalchemy/api.py
index e2240053c..34bcd1229 100644
--- a/nova/virt/baremetal/db/sqlalchemy/api.py
+++ b/nova/virt/baremetal/db/sqlalchemy/api.py
@@ -23,14 +23,13 @@
from sqlalchemy.sql.expression import asc
from sqlalchemy.sql.expression import literal_column
-from nova.db.sqlalchemy.api import is_user_context
-from nova.db.sqlalchemy.api import require_admin_context
+from nova.db.sqlalchemy import api as sqlalchemy_api
from nova import exception
from nova.openstack.common import log as logging
from nova.openstack.common import timeutils
from nova.openstack.common import uuidutils
from nova.virt.baremetal.db.sqlalchemy import models
-from nova.virt.baremetal.db.sqlalchemy.session import get_session
+from nova.virt.baremetal.db.sqlalchemy import session as db_session
LOG = logging.getLogger(__name__)
@@ -44,7 +43,7 @@ def model_query(context, *args, **kwargs):
:param project_only: if present and context is user-type, then restrict
query to match the context's project_id.
"""
- session = kwargs.get('session') or get_session()
+ session = kwargs.get('session') or db_session.get_session()
read_deleted = kwargs.get('read_deleted') or context.read_deleted
project_only = kwargs.get('project_only')
@@ -60,7 +59,7 @@ def model_query(context, *args, **kwargs):
raise Exception(
_("Unrecognized read_deleted value '%s'") % read_deleted)
- if project_only and is_user_context(context):
+ if project_only and sqlalchemy_api.is_user_context(context):
query = query.filter_by(project_id=context.project_id)
return query
@@ -68,7 +67,7 @@ def model_query(context, *args, **kwargs):
def _save(ref, session=None):
if not session:
- session = get_session()
+ session = db_session.get_session()
# We must not call ref.save() with session=None, otherwise NovaBase
# uses nova-db's session, which cannot access bm-db.
ref.save(session=session)
@@ -81,7 +80,7 @@ def _build_node_order_by(query):
return query
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_node_get_all(context, service_host=None):
query = model_query(context, models.BareMetalNode, read_deleted="no")
if service_host:
@@ -89,7 +88,7 @@ def bm_node_get_all(context, service_host=None):
return query.all()
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_node_find_free(context, service_host=None,
cpus=None, memory_mb=None, local_gb=None):
query = model_query(context, models.BareMetalNode, read_deleted="no")
@@ -106,7 +105,7 @@ def bm_node_find_free(context, service_host=None,
return query.first()
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_node_get(context, bm_node_id):
# bm_node_id may be passed as a string. Convert to INT to improve DB perf.
bm_node_id = int(bm_node_id)
@@ -120,7 +119,7 @@ def bm_node_get(context, bm_node_id):
return result
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_node_get_by_instance_uuid(context, instance_uuid):
if not uuidutils.is_uuid_like(instance_uuid):
raise exception.InstanceNotFound(instance_id=instance_uuid)
@@ -135,7 +134,7 @@ def bm_node_get_by_instance_uuid(context, instance_uuid):
return result
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_node_create(context, values):
bm_node_ref = models.BareMetalNode()
bm_node_ref.update(values)
@@ -143,14 +142,14 @@ def bm_node_create(context, values):
return bm_node_ref
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_node_update(context, bm_node_id, values):
model_query(context, models.BareMetalNode, read_deleted="no").\
filter_by(id=bm_node_id).\
update(values)
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_node_set_uuid_safe(context, bm_node_id, values):
"""Associate an instance to a node safely
@@ -164,7 +163,7 @@ def bm_node_set_uuid_safe(context, bm_node_id, values):
raise exception.NovaException(_(
"instance_uuid must be supplied to bm_node_set_uuid_safe"))
- session = get_session()
+ session = db_session.get_session()
with session.begin():
query = model_query(context, models.BareMetalNode,
session=session, read_deleted="no").\
@@ -181,7 +180,7 @@ def bm_node_set_uuid_safe(context, bm_node_id, values):
return ref
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_node_destroy(context, bm_node_id):
model_query(context, models.BareMetalNode).\
filter_by(id=bm_node_id).\
@@ -190,13 +189,13 @@ def bm_node_destroy(context, bm_node_id):
'updated_at': literal_column('updated_at')})
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_pxe_ip_get_all(context):
query = model_query(context, models.BareMetalPxeIp, read_deleted="no")
return query.all()
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_pxe_ip_create(context, address, server_address):
ref = models.BareMetalPxeIp()
ref.address = address
@@ -205,7 +204,7 @@ def bm_pxe_ip_create(context, address, server_address):
return ref
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_pxe_ip_create_direct(context, bm_pxe_ip):
ref = bm_pxe_ip_create(context,
address=bm_pxe_ip['address'],
@@ -213,7 +212,7 @@ def bm_pxe_ip_create_direct(context, bm_pxe_ip):
return ref
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_pxe_ip_destroy(context, ip_id):
# Delete physically since it has unique columns
model_query(context, models.BareMetalPxeIp, read_deleted="no").\
@@ -221,7 +220,7 @@ def bm_pxe_ip_destroy(context, ip_id):
delete()
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_pxe_ip_destroy_by_address(context, address):
# Delete physically since it has unique columns
model_query(context, models.BareMetalPxeIp, read_deleted="no").\
@@ -229,7 +228,7 @@ def bm_pxe_ip_destroy_by_address(context, address):
delete()
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_pxe_ip_get(context, ip_id):
result = model_query(context, models.BareMetalPxeIp, read_deleted="no").\
filter_by(id=ip_id).\
@@ -238,7 +237,7 @@ def bm_pxe_ip_get(context, ip_id):
return result
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_pxe_ip_get_by_bm_node_id(context, bm_node_id):
result = model_query(context, models.BareMetalPxeIp, read_deleted="no").\
filter_by(bm_node_id=bm_node_id).\
@@ -250,9 +249,9 @@ def bm_pxe_ip_get_by_bm_node_id(context, bm_node_id):
return result
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_pxe_ip_associate(context, bm_node_id):
- session = get_session()
+ session = db_session.get_session()
with session.begin():
# Check if the node really exists
node_ref = model_query(context, models.BareMetalNode,
@@ -288,14 +287,14 @@ def bm_pxe_ip_associate(context, bm_node_id):
return ip_ref.id
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_pxe_ip_disassociate(context, bm_node_id):
model_query(context, models.BareMetalPxeIp, read_deleted="no").\
filter_by(bm_node_id=bm_node_id).\
update({'bm_node_id': None})
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_interface_get(context, if_id):
result = model_query(context, models.BareMetalInterface,
read_deleted="no").\
@@ -309,14 +308,14 @@ def bm_interface_get(context, if_id):
return result
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_interface_get_all(context):
query = model_query(context, models.BareMetalInterface,
read_deleted="no")
return query.all()
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_interface_destroy(context, if_id):
# Delete physically since it has unique columns
model_query(context, models.BareMetalInterface, read_deleted="no").\
@@ -324,7 +323,7 @@ def bm_interface_destroy(context, if_id):
delete()
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_interface_create(context, bm_node_id, address, datapath_id, port_no):
ref = models.BareMetalInterface()
ref.bm_node_id = bm_node_id
@@ -335,9 +334,9 @@ def bm_interface_create(context, bm_node_id, address, datapath_id, port_no):
return ref.id
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_interface_set_vif_uuid(context, if_id, vif_uuid):
- session = get_session()
+ session = db_session.get_session()
with session.begin():
bm_interface = model_query(context, models.BareMetalInterface,
read_deleted="no", session=session).\
@@ -361,7 +360,7 @@ def bm_interface_set_vif_uuid(context, if_id, vif_uuid):
raise e
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_interface_get_by_vif_uuid(context, vif_uuid):
result = model_query(context, models.BareMetalInterface,
read_deleted="no").\
@@ -375,7 +374,7 @@ def bm_interface_get_by_vif_uuid(context, vif_uuid):
return result
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_interface_get_all_by_bm_node_id(context, bm_node_id):
result = model_query(context, models.BareMetalInterface,
read_deleted="no").\
@@ -388,7 +387,7 @@ def bm_interface_get_all_by_bm_node_id(context, bm_node_id):
return result
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_deployment_create(context, key, image_path, pxe_config_path, root_mb,
swap_mb):
ref = models.BareMetalDeployment()
@@ -401,7 +400,7 @@ def bm_deployment_create(context, key, image_path, pxe_config_path, root_mb,
return ref.id
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_deployment_get(context, dep_id):
result = model_query(context, models.BareMetalDeployment,
read_deleted="no").\
@@ -410,7 +409,7 @@ def bm_deployment_get(context, dep_id):
return result
-@require_admin_context
+@sqlalchemy_api.require_admin_context
def bm_deployment_destroy(context, dep_id):
model_query(context, models.BareMetalDeployment).\
filter_by(id=dep_id).\
diff --git a/nova/virt/baremetal/db/sqlalchemy/migration.py b/nova/virt/baremetal/db/sqlalchemy/migration.py
index 929793e70..cfc26c04c 100644
--- a/nova/virt/baremetal/db/sqlalchemy/migration.py
+++ b/nova/virt/baremetal/db/sqlalchemy/migration.py
@@ -25,7 +25,7 @@ import sqlalchemy
from nova import exception
from nova.openstack.common import log as logging
from nova.virt.baremetal.db import migration
-from nova.virt.baremetal.db.sqlalchemy.session import get_engine
+from nova.virt.baremetal.db.sqlalchemy import session
LOG = logging.getLogger(__name__)
@@ -71,24 +71,25 @@ def db_sync(version=None):
current_version = db_version()
repository = _find_migrate_repo()
if version is None or version > current_version:
- return versioning_api.upgrade(get_engine(), repository, version)
+ return versioning_api.upgrade(session.get_engine(), repository,
+ version)
else:
- return versioning_api.downgrade(get_engine(), repository,
+ return versioning_api.downgrade(session.get_engine(), repository,
version)
def db_version():
repository = _find_migrate_repo()
try:
- return versioning_api.db_version(get_engine(), repository)
+ return versioning_api.db_version(session.get_engine(), repository)
except versioning_exceptions.DatabaseNotControlledError:
meta = sqlalchemy.MetaData()
- engine = get_engine()
+ engine = session.get_engine()
meta.reflect(bind=engine)
tables = meta.tables
if len(tables) == 0:
db_version_control(migration.INIT_VERSION)
- return versioning_api.db_version(get_engine(), repository)
+ return versioning_api.db_version(session.get_engine(), repository)
else:
# Some pre-Essex DB's may not be version controlled.
# Require them to upgrade using Essex first.
@@ -98,7 +99,7 @@ def db_version():
def db_version_control(version=None):
repository = _find_migrate_repo()
- versioning_api.version_control(get_engine(), repository, version)
+ versioning_api.version_control(session.get_engine(), repository, version)
return version
diff --git a/nova/virt/baremetal/fake.py b/nova/virt/baremetal/fake.py
index 7a400af6f..b3f39fdc3 100644
--- a/nova/virt/baremetal/fake.py
+++ b/nova/virt/baremetal/fake.py
@@ -17,7 +17,7 @@
# under the License.
from nova.virt.baremetal import base
-from nova.virt.firewall import NoopFirewallDriver
+from nova.virt import firewall
class FakeDriver(base.NodeDriver):
@@ -52,7 +52,7 @@ class FakePowerManager(base.PowerManager):
super(FakePowerManager, self).__init__(**kwargs)
-class FakeFirewallDriver(NoopFirewallDriver):
+class FakeFirewallDriver(firewall.NoopFirewallDriver):
def __init__(self):
super(FakeFirewallDriver, self).__init__()
diff --git a/nova/virt/baremetal/ipmi.py b/nova/virt/baremetal/ipmi.py
index 97c158727..5d4a87625 100644
--- a/nova/virt/baremetal/ipmi.py
+++ b/nova/virt/baremetal/ipmi.py
@@ -25,7 +25,7 @@ import os
import stat
import tempfile
-from nova.exception import InvalidParameterValue
+from nova import exception
from nova.openstack.common import cfg
from nova.openstack.common import log as logging
from nova import paths
@@ -104,13 +104,17 @@ class IPMI(base.PowerManager):
self.port = node['terminal_port']
if self.node_id == None:
- raise InvalidParameterValue(_("Node id not supplied to IPMI"))
+ raise exception.InvalidParameterValue(_("Node id not supplied "
+ "to IPMI"))
if self.address == None:
- raise InvalidParameterValue(_("Address not supplied to IPMI"))
+ raise exception.InvalidParameterValue(_("Address not supplied "
+ "to IPMI"))
if self.user == None:
- raise InvalidParameterValue(_("User not supplied to IPMI"))
+ raise exception.InvalidParameterValue(_("User not supplied "
+ "to IPMI"))
if self.password == None:
- raise InvalidParameterValue(_("Password not supplied to IPMI"))
+ raise exception.InvalidParameterValue(_("Password not supplied "
+ "to IPMI"))
def _exec_ipmitool(self, command):
args = ['ipmitool',
@@ -126,7 +130,7 @@ class IPMI(base.PowerManager):
args.append(pwfile)
args.extend(command.split(" "))
out, err = utils.execute(*args, attempts=3)
- LOG.debug(_("ipmitool stdout: '%(out)s', stderr: '%(err)%s'"),
+ LOG.debug(_("ipmitool stdout: '%(out)s', stderr: '%(err)s'"),
locals())
return out, err
finally:
diff --git a/nova/virt/baremetal/pxe.py b/nova/virt/baremetal/pxe.py
index 0daac1d46..5a6f58655 100644
--- a/nova/virt/baremetal/pxe.py
+++ b/nova/virt/baremetal/pxe.py
@@ -21,18 +21,15 @@ Class for PXE bare-metal nodes.
"""
import os
-import shutil
from nova.compute import instance_types
from nova import exception
from nova.openstack.common import cfg
from nova.openstack.common import fileutils
from nova.openstack.common import log as logging
-from nova import utils
from nova.virt.baremetal import base
from nova.virt.baremetal import db
from nova.virt.baremetal import utils as bm_utils
-from nova.virt.disk import api as disk
pxe_opts = [
@@ -67,7 +64,8 @@ CHEETAH = None
def _get_cheetah():
global CHEETAH
if CHEETAH is None:
- from Cheetah.Template import Template as CHEETAH
+ from Cheetah import Template
+ CHEETAH = Template.Template
return CHEETAH
@@ -344,7 +342,7 @@ class PXE(base.NodeDriver):
def destroy_images(self, context, node, instance):
"""Delete instance's image file."""
bm_utils.unlink_without_raise(get_image_file_path(instance))
- bm_utils.unlink_without_raise(get_image_dir_path(instance))
+ bm_utils.rmtree_without_raise(get_image_dir_path(instance))
def activate_bootloader(self, context, node, instance):
"""Configure PXE boot loader for an instance
@@ -419,7 +417,7 @@ class PXE(base.NodeDriver):
for mac in macs:
bm_utils.unlink_without_raise(get_pxe_mac_path(mac))
- bm_utils.unlink_without_raise(
+ bm_utils.rmtree_without_raise(
os.path.join(CONF.baremetal.tftp_root, instance['uuid']))
def activate_node(self, context, node, instance):
diff --git a/nova/virt/baremetal/utils.py b/nova/virt/baremetal/utils.py
index 0842ae201..fa2c05983 100644
--- a/nova/virt/baremetal/utils.py
+++ b/nova/virt/baremetal/utils.py
@@ -15,7 +15,9 @@
# License for the specific language governing permissions and limitations
# under the License.
+import errno
import os
+import shutil
from nova.openstack.common import log as logging
from nova.virt.disk import api as disk_api
@@ -43,8 +45,19 @@ def inject_into_image(image, key, net, metadata, admin_password,
def unlink_without_raise(path):
try:
os.unlink(path)
- except OSError:
- LOG.exception(_("Failed to unlink %s") % path)
+ except OSError, e:
+ if e.errno == errno.ENOENT:
+ return
+ else:
+ LOG.warn(_("Failed to unlink %(path)s, error: %(e)s") % locals())
+
+
+def rmtree_without_raise(path):
+ try:
+ if os.path.isdir(path):
+ shutil.rmtree(path)
+ except OSError, e:
+ LOG.warn(_("Failed to remove dir %(path)s, error: %(e)s") % locals())
def write_to_file(path, contents):
@@ -55,9 +68,12 @@ def write_to_file(path, contents):
def create_link_without_raise(source, link):
try:
os.symlink(source, link)
- except OSError:
- LOG.exception(_("Failed to create symlink from %(source)s to %(link)s")
- % locals())
+ except OSError, e:
+ if e.errno == errno.EEXIST:
+ return
+ else:
+ LOG.warn(_("Failed to create symlink from %(source)s to %(link)s"
+ ", error: %(e)s") % locals())
def random_alnum(count):
diff --git a/nova/virt/disk/api.py b/nova/virt/disk/api.py
index d080f6d36..304781a64 100644
--- a/nova/virt/disk/api.py
+++ b/nova/virt/disk/api.py
@@ -271,14 +271,19 @@ class _DiskImage(object):
# Public module functions
def inject_data(image, key=None, net=None, metadata=None, admin_password=None,
- files=None, partition=None, use_cow=False):
- """Injects a ssh key and optionally net data into a disk image.
+ files=None, partition=None, use_cow=False, mandatory=()):
+ """Inject the specified items into a disk image.
+
+ If an item name is not specified in the MANDATORY iterable, then a warning
+ is logged on failure to inject that item, rather than raising an exception.
it will mount the image as a fully partitioned disk and attempt to inject
into the specified partition number.
- If partition is not specified it mounts the image as a single partition.
+ If PARTITION is not specified the image is mounted as a single partition.
+ Returns True if all requested operations completed without issue.
+ Raises an exception if a mandatory item can't be injected.
"""
LOG.debug(_("Inject data image=%(image)s key=%(key)s net=%(net)s "
"metadata=%(metadata)s admin_password=ha-ha-not-telling-you "
@@ -287,11 +292,23 @@ def inject_data(image, key=None, net=None, metadata=None, admin_password=None,
fmt = "raw"
if use_cow:
fmt = "qcow2"
- fs = vfs.VFS.instance_for_image(image, fmt, partition)
- fs.setup()
try:
- inject_data_into_fs(fs,
- key, net, metadata, admin_password, files)
+ fs = vfs.VFS.instance_for_image(image, fmt, partition)
+ fs.setup()
+ except Exception as e:
+ # If a mandatory item is passed to this function,
+ # then reraise the exception to indicate the error.
+ for inject in mandatory:
+ inject_val = locals()[inject]
+ if inject_val:
+ raise
+ LOG.warn(_('Ignoring error injecting data into image '
+ '(%(e)s)') % locals())
+ return False
+
+ try:
+ return inject_data_into_fs(fs, key, net, metadata,
+ admin_password, files, mandatory)
finally:
fs.teardown()
@@ -324,22 +341,37 @@ def teardown_container(container_dir):
LOG.exception(_('Failed to unmount container filesystem: %s'), exn)
-def inject_data_into_fs(fs, key, net, metadata, admin_password, files):
+def inject_data_into_fs(fs, key, net, metadata, admin_password, files,
+ mandatory=()):
"""Injects data into a filesystem already mounted by the caller.
Virt connections can call this directly if they mount their fs
- in a different way to inject_data
+ in a different way to inject_data.
+
+ If an item name is not specified in the MANDATORY iterable, then a warning
+ is logged on failure to inject that item, rather than raising an exception.
+
+ Returns True if all requested operations completed without issue.
+ Raises an exception if a mandatory item can't be injected.
"""
- if key:
- _inject_key_into_fs(key, fs)
- if net:
- _inject_net_into_fs(net, fs)
- if metadata:
- _inject_metadata_into_fs(metadata, fs)
- if admin_password:
- _inject_admin_password_into_fs(admin_password, fs)
- if files:
- for (path, contents) in files:
- _inject_file_into_fs(fs, path, contents)
+ status = True
+ for inject in ('key', 'net', 'metadata', 'admin_password', 'files'):
+ inject_val = locals()[inject]
+ inject_func = globals()['_inject_%s_into_fs' % inject]
+ if inject_val:
+ try:
+ inject_func(inject_val, fs)
+ except Exception as e:
+ if inject in mandatory:
+ raise
+ LOG.warn(_('Ignoring error injecting %(inject)s into image '
+ '(%(e)s)') % locals())
+ status = False
+ return status
+
+
+def _inject_files_into_fs(files, fs):
+ for (path, contents) in files:
+ _inject_file_into_fs(fs, path, contents)
def _inject_file_into_fs(fs, path, contents, append=False):
@@ -411,6 +443,7 @@ def _inject_key_into_fs(key, fs):
])
_inject_file_into_fs(fs, keyfile, key_data, append=True)
+ fs.set_permissions(keyfile, 0600)
_setup_selinux_for_keys(fs, sshdir)
diff --git a/nova/virt/hyperv/__init__.py b/nova/virt/hyperv/__init__.py
index e69de29bb..090fc0639 100644
--- a/nova/virt/hyperv/__init__.py
+++ b/nova/virt/hyperv/__init__.py
@@ -0,0 +1,16 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
diff --git a/nova/virt/hyperv/baseops.py b/nova/virt/hyperv/baseops.py
deleted file mode 100644
index 5b617f898..000000000
--- a/nova/virt/hyperv/baseops.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# 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
- self.__conn_storage = 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
-
- @property
- def _conn_storage(self):
- if self.__conn_storage is None:
- storage_namespace = '//./Root/Microsoft/Windows/Storage'
- self.__conn_storage = wmi.WMI(moniker=storage_namespace)
- return self.__conn_storage
diff --git a/nova/virt/hyperv/basevolumeutils.py b/nova/virt/hyperv/basevolumeutils.py
index 2352c3bef..34b15ea53 100644
--- a/nova/virt/hyperv/basevolumeutils.py
+++ b/nova/virt/hyperv/basevolumeutils.py
@@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Pedro Navarro Perez
+# Copyright 2013 Cloudbase Solutions Srl
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -20,17 +21,18 @@ Helper methods for operations related to the management of volumes,
and storage repositories
"""
+import abc
import sys
+if sys.platform == 'win32':
+ import _winreg
+ import wmi
+
from nova import block_device
from nova.openstack.common import cfg
from nova.openstack.common import log as logging
from nova.virt import driver
-# Check needed for unit testing on Unix
-if sys.platform == 'win32':
- import _winreg
-
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
CONF.import_opt('my_ip', 'nova.netconf')
@@ -38,25 +40,40 @@ CONF.import_opt('my_ip', 'nova.netconf')
class BaseVolumeUtils(object):
+ def __init__(self):
+ if sys.platform == 'win32':
+ self._conn_wmi = wmi.WMI(moniker='//./root/wmi')
+
+ @abc.abstractmethod
+ def login_storage_target(self, target_lun, target_iqn, target_portal):
+ pass
+
+ @abc.abstractmethod
+ def logout_storage_target(self, target_iqn):
+ pass
+
+ @abc.abstractmethod
+ def execute_log_out(self, session_id):
+ pass
+
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"
+ keypath = ("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\"
+ "iSCSI\\Discovery")
try:
key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keypath, 0,
- _winreg.KEY_ALL_ACCESS)
+ _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"))
+ "Choosing the default one"))
computer_system = cim_conn.Win32_ComputerSystem()[0]
- initiator_name = "iqn.1991-05.com.microsoft:" + \
- hostname.lower()
+ initiator_name = "iqn.1991-05.com.microsoft:" + hostname.lower()
return {
'ip': CONF.my_ip,
'initiator': initiator_name,
@@ -78,3 +95,33 @@ class BaseVolumeUtils(object):
LOG.debug(_("block_device_list %s"), block_device_list)
return block_device.strip_dev(mount_device) in block_device_list
+
+ def _get_drive_number_from_disk_path(self, disk_path):
+ # TODO(pnavarro) replace with regex
+ start_device_id = disk_path.find('"', disk_path.find('DeviceID'))
+ end_device_id = disk_path.find('"', start_device_id + 1)
+ device_id = disk_path[start_device_id + 1:end_device_id]
+ return device_id[device_id.find("\\") + 2:]
+
+ def get_session_id_from_mounted_disk(self, physical_drive_path):
+ drive_number = self._get_drive_number_from_disk_path(
+ physical_drive_path)
+ initiator_sessions = self._conn_wmi.query("SELECT * FROM "
+ "MSiSCSIInitiator_Session"
+ "Class")
+ for initiator_session in initiator_sessions:
+ devices = initiator_session.Devices
+ for device in devices:
+ device_number = str(device.DeviceNumber)
+ if device_number == drive_number:
+ return initiator_session.SessionId
+
+ def get_device_number_for_target(self, target_iqn, target_lun):
+ initiator_session = self._conn_wmi.query("SELECT * FROM "
+ "MSiSCSIInitiator_Session"
+ "Class WHERE TargetName='%s'"
+ % target_iqn)[0]
+ devices = initiator_session.Devices
+ for device in devices:
+ if device.ScsiLun == target_lun:
+ return device.DeviceNumber
diff --git a/nova/virt/hyperv/constants.py b/nova/virt/hyperv/constants.py
index 4be569e88..31323f0f4 100644
--- a/nova/virt/hyperv/constants.py
+++ b/nova/virt/hyperv/constants.py
@@ -35,15 +35,6 @@ HYPERV_POWER_STATE = {
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_WIN32_PROCESSOR_ARCHITECTURE = {
0: 'x86',
1: 'MIPS',
diff --git a/nova/virt/hyperv/driver.py b/nova/virt/hyperv/driver.py
index 9316b2598..e8bf8c416 100644
--- a/nova/virt/hyperv/driver.py
+++ b/nova/virt/hyperv/driver.py
@@ -16,49 +16,7 @@
# 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
-
+A Hyper-V Nova Compute driver.
"""
from nova.openstack.common import log as logging
@@ -84,7 +42,7 @@ class HyperVDriver(driver.ComputeDriver):
self._volumeops)
def init_host(self, host):
- self._host = host
+ pass
def list_instances(self):
return self._vmops.list_instances()
@@ -92,7 +50,7 @@ class HyperVDriver(driver.ComputeDriver):
def spawn(self, context, instance, image_meta, injected_files,
admin_password, network_info=None, block_device_info=None):
self._vmops.spawn(context, instance, image_meta, injected_files,
- admin_password, network_info, block_device_info)
+ admin_password, network_info, block_device_info)
def reboot(self, instance, network_info, reboot_type,
block_device_info=None):
@@ -106,16 +64,12 @@ class HyperVDriver(driver.ComputeDriver):
return self._vmops.get_info(instance)
def attach_volume(self, connection_info, instance, mountpoint):
- """Attach volume storage to VM instance."""
return self._volumeops.attach_volume(connection_info,
- instance['name'],
- mountpoint)
+ instance['name'])
def detach_volume(self, connection_info, instance, mountpoint):
- """Detach volume storage to VM instance."""
return self._volumeops.detach_volume(connection_info,
- instance['name'],
- mountpoint)
+ instance['name'])
def get_volume_connector(self, instance):
return self._volumeops.get_volume_connector(instance)
@@ -151,30 +105,38 @@ class HyperVDriver(driver.ComputeDriver):
self._vmops.power_on(instance)
def live_migration(self, context, instance_ref, dest, post_method,
- recover_method, block_migration=False, migrate_data=None):
+ recover_method, block_migration=False,
+ migrate_data=None):
self._livemigrationops.live_migration(context, instance_ref, dest,
- post_method, recover_method, block_migration, migrate_data)
+ post_method, recover_method,
+ block_migration, migrate_data)
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, migrate_data=None):
+ network_info, migrate_data=None):
self._livemigrationops.pre_live_migration(context, instance,
- block_device_info, network_info)
+ block_device_info,
+ network_info)
def post_live_migration_at_destination(self, ctxt, instance_ref,
- network_info, block_migration, block_device_info=None):
+ network_info,
+ block_migr=False,
+ block_device_info=None):
self._livemigrationops.post_live_migration_at_destination(ctxt,
- instance_ref, network_info, block_migration)
-
- def check_can_live_migrate_destination(self, ctxt, instance,
- src_compute_info, dst_compute_info,
- block_migration, disk_over_commit):
+ instance_ref,
+ network_info,
+ block_migr)
+
+ def check_can_live_migrate_destination(self, ctxt, instance_ref,
+ src_compute_info, dst_compute_info,
+ block_migration=False,
+ disk_over_commit=False):
pass
def check_can_live_migrate_destination_cleanup(self, ctxt,
- dest_check_data):
+ dest_check_data):
pass
def check_can_live_migrate_source(self, ctxt, instance, dest_check_data):
@@ -188,25 +150,21 @@ class HyperVDriver(driver.ComputeDriver):
def ensure_filtering_rules_for_instance(self, instance_ref, network_info):
LOG.debug(_("ensure_filtering_rules_for_instance called"),
- instance=instance_ref)
+ 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,
block_device_info=None):
- """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,
- block_device_info=None):
- """Completes a resize, turning on the migrated instance."""
+ network_info, image_meta, resize_instance=False,
+ block_device_info=None):
LOG.debug(_("finish_migration called"), instance=instance)
def get_console_output(self, instance):
diff --git a/nova/virt/hyperv/hostops.py b/nova/virt/hyperv/hostops.py
index 5cbe46c1c..5a22b60de 100644
--- a/nova/virt/hyperv/hostops.py
+++ b/nova/virt/hyperv/hostops.py
@@ -18,25 +18,23 @@
"""
Management class for host operations.
"""
-import ctypes
-import multiprocessing
import os
import platform
-from nova.openstack.common import cfg
from nova.openstack.common import jsonutils
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 hostutils
+from nova.virt.hyperv import pathutils
-CONF = cfg.CONF
LOG = logging.getLogger(__name__)
-class HostOps(baseops.BaseOps):
+class HostOps(object):
def __init__(self):
- super(HostOps, self).__init__()
self._stats = None
+ self._hostutils = hostutils.HostUtils()
+ self._pathutils = pathutils.PathUtils()
def _get_cpu_info(self):
"""Get the CPU information.
@@ -44,94 +42,51 @@ class HostOps(baseops.BaseOps):
of the central processor in the hypervisor.
"""
cpu_info = dict()
- processor = self._conn_cimv2.query(
- "SELECT * FROM Win32_Processor WHERE ProcessorType = 3")
- cpu_info['arch'] = constants.WMI_WIN32_PROCESSOR_ARCHITECTURE\
- .get(processor[0].Architecture, 'Unknown')
- cpu_info['model'] = processor[0].Name
- cpu_info['vendor'] = processor[0].Manufacturer
+ processors = self._hostutils.get_cpus_info()
+
+ w32_arch_dict = constants.WMI_WIN32_PROCESSOR_ARCHITECTURE
+ cpu_info['arch'] = w32_arch_dict.get(processors[0]['Architecture'],
+ 'Unknown')
+ cpu_info['model'] = processors[0]['Name']
+ cpu_info['vendor'] = processors[0]['Manufacturer']
topology = dict()
- topology['sockets'] = len(processor)
- topology['cores'] = processor[0].NumberOfCores
- topology['threads'] = processor[0].NumberOfLogicalProcessors\
- / processor[0].NumberOfCores
+ topology['sockets'] = len(processors)
+ topology['cores'] = processors[0]['NumberOfCores']
+ topology['threads'] = (processors[0]['NumberOfLogicalProcessors'] /
+ processors[0]['NumberOfCores'])
cpu_info['topology'] = topology
features = list()
for fkey, fname in constants.PROCESSOR_FEATURE.items():
- if ctypes.windll.kernel32.IsProcessorFeaturePresent(fkey):
+ if self._hostutils.is_cpu_feature_present(fkey):
features.append(fname)
cpu_info['features'] = features
- return jsonutils.dumps(cpu_info)
+ return cpu_info
- 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_memory_info(self):
+ (total_mem_kb, free_mem_kb) = self._hostutils.get_memory_info()
+ total_mem_mb = total_mem_kb / 1024
+ free_mem_mb = free_mem_kb / 1024
+ return (total_mem_mb, free_mem_mb, total_mem_mb - free_mem_mb)
def _get_local_hdd_info_gb(self):
- """Get the total and used size of the volume containing
- CONF.instances_path expressed in GB.
- :returns:
- A tuple with the total and used space in GB.
- """
- normalized_path = os.path.normpath(CONF.instances_path)
- drive, path = os.path.splitdrive(normalized_path)
- hdd_info = self._conn_cimv2.query(
- ("SELECT FreeSpace,Size FROM win32_logicaldisk WHERE DeviceID='%s'"
- ) % drive)[0]
- total_gb = long(hdd_info.Size) / (1024 ** 3)
- free_gb = long(hdd_info.FreeSpace) / (1024 ** 3)
- used_gb = total_gb - free_gb
- return total_gb, used_gb
+ (drive, _) = os.path.splitdrive(self._pathutils.get_instances_path())
+ (size, free_space) = self._hostutils.get_volume_info(drive)
- 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
+ total_gb = size / (1024 ** 3)
+ free_gb = free_space / (1024 ** 3)
+ used_gb = total_gb - free_gb
+ return (total_gb, free_gb, used_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)
+ version = self._hostutils.get_windows_version().replace('.', '')
+ LOG.debug(_('Windows version: %s ') % version)
return version
def get_available_resource(self):
@@ -143,36 +98,53 @@ class HostOps(baseops.BaseOps):
:returns: dictionary describing resources
"""
- LOG.info(_('get_available_resource called'))
-
- local_gb, used_gb = self._get_local_hdd_info_gb()
- dic = {'vcpus': self._get_vcpu_total(),
- 'memory_mb': self._get_memory_mb_total(),
- 'local_gb': local_gb,
- 'vcpus_used': self._get_vcpu_used(),
- 'memory_mb_used': self._get_memory_mb_used(),
- 'local_gb_used': used_gb,
+ LOG.debug(_('get_available_resource called'))
+
+ (total_mem_mb,
+ free_mem_mb,
+ used_mem_mb) = self._get_memory_info()
+
+ (total_hdd_gb,
+ free_hdd_gb,
+ used_hdd_gb) = self._get_local_hdd_info_gb()
+
+ cpu_info = self._get_cpu_info()
+ cpu_topology = cpu_info['topology']
+ vcpus = (cpu_topology['sockets'] *
+ cpu_topology['cores'] *
+ cpu_topology['threads'])
+
+ dic = {'vcpus': vcpus,
+ 'memory_mb': total_mem_mb,
+ 'memory_mb_used': used_mem_mb,
+ 'local_gb': total_hdd_gb,
+ 'local_gb_used': used_hdd_gb,
'hypervisor_type': "hyperv",
'hypervisor_version': self._get_hypervisor_version(),
'hypervisor_hostname': platform.node(),
- 'cpu_info': self._get_cpu_info()}
+ 'vcpus_used': 0,
+ 'cpu_info': jsonutils.dumps(cpu_info)}
return dic
def _update_stats(self):
LOG.debug(_("Updating host stats"))
+ (total_mem_mb, free_mem_mb, used_mem_mb) = self._get_memory_info()
+ (total_hdd_gb,
+ free_hdd_gb,
+ used_hdd_gb) = self._get_local_hdd_info_gb()
+
data = {}
- data["disk_total"], data["disk_used"] = self._get_local_hdd_info_gb()
- data["disk_available"] = data["disk_total"] - data["disk_used"]
- data["host_memory_total"] = self._get_memory_mb_total()
- data["host_memory_overhead"] = self._get_memory_mb_used()
- data["host_memory_free"] = \
- data["host_memory_total"] - data["host_memory_overhead"]
- data["host_memory_free_computed"] = data["host_memory_free"]
- data["supported_instances"] = \
- [('i686', 'hyperv', 'hvm'),
- ('x86_64', 'hyperv', 'hvm')]
+ data["disk_total"] = total_hdd_gb
+ data["disk_used"] = used_hdd_gb
+ data["disk_available"] = free_hdd_gb
+ data["host_memory_total"] = total_mem_mb
+ data["host_memory_overhead"] = used_mem_mb
+ data["host_memory_free"] = free_mem_mb
+ data["host_memory_free_computed"] = free_mem_mb
+ data["supported_instances"] = [('i686', 'hyperv', 'hvm'),
+ ('x86_64', 'hyperv', 'hvm')]
data["hypervisor_hostname"] = platform.node()
self._stats = data
diff --git a/nova/virt/hyperv/hostutils.py b/nova/virt/hyperv/hostutils.py
new file mode 100644
index 000000000..71f3bc5b2
--- /dev/null
+++ b/nova/virt/hyperv/hostutils.py
@@ -0,0 +1,74 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
+
+import ctypes
+import sys
+
+if sys.platform == 'win32':
+ import wmi
+
+from nova.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+class HostUtils(object):
+ def __init__(self):
+ if sys.platform == 'win32':
+ self._conn_cimv2 = wmi.WMI(moniker='//./root/cimv2')
+
+ def get_cpus_info(self):
+ cpus = self._conn_cimv2.query("SELECT * FROM Win32_Processor "
+ "WHERE ProcessorType = 3")
+ cpus_list = []
+ for cpu in cpus:
+ cpu_info = {'Architecture': cpu.Architecture,
+ 'Name': cpu.Name,
+ 'Manufacturer': cpu.Manufacturer,
+ 'NumberOfCores': cpu.NumberOfCores,
+ 'NumberOfLogicalProcessors':
+ cpu.NumberOfLogicalProcessors}
+ cpus_list.append(cpu_info)
+ return cpus_list
+
+ def is_cpu_feature_present(self, feature_key):
+ return ctypes.windll.kernel32.IsProcessorFeaturePresent(feature_key)
+
+ def get_memory_info(self):
+ """
+ Returns a tuple with total visible memory and free physical memory
+ expressed in kB.
+ """
+ mem_info = self._conn_cimv2.query("SELECT TotalVisibleMemorySize, "
+ "FreePhysicalMemory "
+ "FROM win32_operatingsystem")[0]
+ return (long(mem_info.TotalVisibleMemorySize),
+ long(mem_info.FreePhysicalMemory))
+
+ def get_volume_info(self, drive):
+ """
+ Returns a tuple with total size and free space
+ expressed in bytes.
+ """
+ logical_disk = self._conn_cimv2.query("SELECT Size, FreeSpace "
+ "FROM win32_logicaldisk "
+ "WHERE DeviceID='%s'"
+ % drive)[0]
+ return (long(logical_disk.Size), long(logical_disk.FreeSpace))
+
+ def get_windows_version(self):
+ return self._conn_cimv2.Win32_OperatingSystem()[0].Version
diff --git a/nova/virt/hyperv/ioutils.py b/nova/virt/hyperv/ioutils.py
deleted file mode 100644
index d927e317f..000000000
--- a/nova/virt/hyperv/ioutils.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# 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
index 232cbd660..8ee3005f1 100644
--- a/nova/virt/hyperv/livemigrationops.py
+++ b/nova/virt/hyperv/livemigrationops.py
@@ -19,144 +19,66 @@
Management class for live migration VM operations.
"""
import os
-import sys
-from nova import exception
from nova.openstack.common import cfg
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 livemigrationutils
+from nova.virt.hyperv import pathutils
from nova.virt.hyperv import vmutils
-
-# Check needed for unit testing on Unix
-if sys.platform == 'win32':
- import wmi
+from nova.virt import images
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
CONF.import_opt('use_cow_images', 'nova.virt.driver')
-class LiveMigrationOps(baseops.BaseOps):
+class LiveMigrationOps(object):
def __init__(self, volumeops):
- super(LiveMigrationOps, self).__init__()
+ self._pathutils = pathutils.PathUtils()
self._vmutils = vmutils.VMUtils()
+ self._livemigrutils = livemigrationutils.LiveMigrationUtils()
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, migrate_data=None):
+ recover_method, block_migration=False,
+ migrate_data=None):
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)
+ self._livemigrutils.live_migrate_vm(instance_name, dest)
except Exception:
with excutils.save_and_reraise_exception():
LOG.debug(_("Calling live migration recover_method "
- "for instance: %s"), instance_name)
+ "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)
+ instance_name)
post_method(context, instance_ref, dest, block_migration)
def pre_live_migration(self, context, instance, block_device_info,
- network_info):
+ network_info):
LOG.debug(_("pre_live_migration called"), instance=instance)
- self._check_live_migration_config()
+ self._livemigrutils.check_live_migration_config()
if CONF.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(
+ base_vhd_path = self._pathutils.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"])
+ images.fetch(context, instance["image_ref"], base_vhd_path,
+ instance["user_id"], instance["project_id"])
def post_live_migration_at_destination(self, ctxt, instance_ref,
- network_info, block_migration):
+ network_info, block_migration):
LOG.debug(_("post_live_migration_at_destination called"),
- instance=instance_ref)
+ instance=instance_ref)
def compare_cpu(self, cpu_info):
LOG.debug(_("compare_cpu called %s"), cpu_info)
diff --git a/nova/virt/hyperv/livemigrationutils.py b/nova/virt/hyperv/livemigrationutils.py
new file mode 100644
index 000000000..6af4f0fa5
--- /dev/null
+++ b/nova/virt/hyperv/livemigrationutils.py
@@ -0,0 +1,115 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
+
+import sys
+
+if sys.platform == 'win32':
+ import wmi
+
+from nova.openstack.common import log as logging
+from nova.virt.hyperv import vmutils
+
+LOG = logging.getLogger(__name__)
+
+
+class LiveMigrationUtils(object):
+
+ def __init__(self):
+ self._vmutils = vmutils.VMUtils()
+
+ def _get_conn_v2(self, host='localhost'):
+ try:
+ return wmi.WMI(moniker='//%s/root/virtualization/v2' % host)
+ except wmi.x_wmi as ex:
+ LOG.exception(ex)
+ if ex.com_error.hresult == -2147217394:
+ msg = (_('Live migration is not supported on target host "%s"')
+ % host)
+ elif ex.com_error.hresult == -2147023174:
+ msg = (_('Target live migration host "%s" is unreachable')
+ % host)
+ else:
+ msg = _('Live migration failed: %s') % ex.message
+ raise vmutils.HyperVException(msg)
+
+ def check_live_migration_config(self):
+ conn_v2 = self._get_conn_v2()
+ migration_svc = conn_v2.Msvm_VirtualSystemMigrationService()[0]
+ vsmssds = migration_svc.associators(
+ wmi_association_class='Msvm_ElementSettingData',
+ wmi_result_class='Msvm_VirtualSystemMigrationServiceSettingData')
+ vsmssd = vsmssds[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 _get_vm(self, conn_v2, vm_name):
+ vms = conn_v2.Msvm_ComputerSystem(ElementName=vm_name)
+ n = len(vms)
+ if not n:
+ raise vmutils.HyperVException(_('VM not found: %s') % vm_name)
+ elif n > 1:
+ raise vmutils.HyperVException(_('Duplicate VM name found: %s')
+ % vm_name)
+ return vms[0]
+
+ def live_migrate_vm(self, vm_name, dest_host):
+ self.check_live_migration_config()
+
+ # We need a v2 namespace VM object
+ conn_v2_local = self._get_conn_v2()
+
+ vm = self._get_vm(conn_v2_local, vm_name)
+ 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_host)
+ conn_v2_remote = self._get_conn_v2(dest_host)
+ migr_svc_rmt = conn_v2_remote.Msvm_VirtualSystemMigrationService()[0]
+ rmt_ip_addr_list = migr_svc_rmt.MigrationServiceListenerIPAddressList
+
+ # VirtualSystemAndStorage
+ vsmsd = conn_v2_local.query("select * from "
+ "Msvm_VirtualSystemMigrationSettingData "
+ "where MigrationType = 32771")[0]
+ vsmsd.DestinationIPAddressList = rmt_ip_addr_list
+ migration_setting_data = vsmsd.GetText_(1)
+
+ migr_svc = conn_v2_local.Msvm_VirtualSystemMigrationService()[0]
+
+ LOG.debug(_("Starting live migration for VM: %s"), vm_name)
+ (job_path, ret_val) = migr_svc.MigrateVirtualSystemToHost(
+ ComputerSystem=vm.path_(),
+ DestinationHost=dest_host,
+ MigrationSettingData=migration_setting_data,
+ NewResourceSettingData=new_resource_setting_data)
+ self._vmutils.check_ret_val(ret_val, job_path)
diff --git a/nova/virt/hyperv/networkutils.py b/nova/virt/hyperv/networkutils.py
new file mode 100644
index 000000000..4e1f68685
--- /dev/null
+++ b/nova/virt/hyperv/networkutils.py
@@ -0,0 +1,62 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 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 for network related operations.
+"""
+
+import sys
+import uuid
+
+if sys.platform == 'win32':
+ import wmi
+
+from nova.virt.hyperv import vmutils
+
+
+class NetworkUtils(object):
+ def __init__(self):
+ if sys.platform == 'win32':
+ self._conn = wmi.WMI(moniker='//./root/virtualization')
+
+ def get_external_vswitch(self, vswitch_name):
+ if vswitch_name:
+ vswitches = self._conn.Msvm_VirtualSwitch(ElementName=vswitch_name)
+ else:
+ # Find the vswitch that is connected to the first physical nic.
+ ext_port = self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')[0]
+ port = ext_port.associators(wmi_result_class='Msvm_SwitchPort')[0]
+ vswitches = port.associators(wmi_result_class='Msvm_VirtualSwitch')
+
+ if not len(vswitches):
+ raise vmutils.HyperVException(_('vswitch "%s" not found')
+ % vswitch_name)
+ return vswitches[0].path_()
+
+ def create_vswitch_port(self, vswitch_path, port_name):
+ switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[0]
+ #Create a port on the vswitch.
+ (new_port, ret_val) = switch_svc.CreateSwitchPort(
+ Name=str(uuid.uuid4()),
+ FriendlyName=port_name,
+ ScopeOfResidence="",
+ VirtualSwitch=vswitch_path)
+ if ret_val != 0:
+ raise vmutils.HyperVException(_("Failed to create vswitch port "
+ "%(port_name)s on switch "
+ "%(vswitch_path)s") % locals())
+ return new_port
diff --git a/nova/virt/hyperv/pathutils.py b/nova/virt/hyperv/pathutils.py
new file mode 100644
index 000000000..7bc2e7ac2
--- /dev/null
+++ b/nova/virt/hyperv/pathutils.py
@@ -0,0 +1,67 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013 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.
+
+import os
+import shutil
+
+from nova.openstack.common import cfg
+from nova.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+CONF = cfg.CONF
+CONF.import_opt('instances_path', 'nova.compute.manager')
+
+
+class PathUtils(object):
+ def open(self, path, mode):
+ """Wrapper on __builin__.open used to simplify unit testing."""
+ import __builtin__
+ return __builtin__.open(path, mode)
+
+ def get_instances_path(self):
+ return os.path.normpath(CONF.instances_path)
+
+ def get_instance_path(self, instance_name):
+ instance_path = os.path.join(self.get_instances_path(), instance_name)
+ if not os.path.exists(instance_path):
+ LOG.debug(_('Creating folder %s '), instance_path)
+ os.makedirs(instance_path)
+ return instance_path
+
+ def get_vhd_path(self, instance_name):
+ instance_path = self.get_instance_path(instance_name)
+ return os.path.join(instance_path, instance_name + ".vhd")
+
+ def get_base_vhd_path(self, image_name):
+ base_dir = os.path.join(self.get_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(self.get_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 vhd_exists(self, path):
+ return os.path.exists(path)
diff --git a/nova/virt/hyperv/snapshotops.py b/nova/virt/hyperv/snapshotops.py
index cdc6e45a4..c43f59b70 100644
--- a/nova/virt/hyperv/snapshotops.py
+++ b/nova/virt/hyperv/snapshotops.py
@@ -20,173 +20,97 @@ Management class for VM snapshot operations.
"""
import os
import shutil
-import sys
from nova.compute import task_states
-from nova import exception
from nova.image import glance
from nova.openstack.common import cfg
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 pathutils
+from nova.virt.hyperv import vhdutils
from nova.virt.hyperv import vmutils
-from xml.etree import ElementTree
-
-# Check needed for unit testing on Unix
-if sys.platform == 'win32':
- import wmi
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
-class SnapshotOps(baseops.BaseOps):
+class SnapshotOps(object):
def __init__(self):
- super(SnapshotOps, self).__init__()
+ self._pathutils = pathutils.PathUtils()
self._vmutils = vmutils.VMUtils()
+ self._vhdutils = vhdutils.VHDUtils()
+
+ def _save_glance_image(self, context, name, image_vhd_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": {}}
+ with self._pathutils.open(image_vhd_path, 'rb') as f:
+ glance_image_service.update(context, image_id, image_metadata, f)
def snapshot(self, context, instance, name, update_task_state):
"""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)
- else:
- update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
+ snapshot_path = self._vmutils.take_vm_snapshot(instance_name)
+ update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
export_folder = None
- f = None
try:
- src_vhd_path = os.path.join(CONF.instances_path, instance_name,
- instance_name + ".vhd")
-
- image_man_svc = self._conn.Msvm_ImageManagementService()[0]
+ src_vhd_path = self._pathutils.get_vhd_path(instance_name)
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 = self._vhdutils.get_vhd_parent_path(
+ 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)
+ export_folder = self._pathutils.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())
+ 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))
+ basename = os.path.basename(src_base_disk_path)
+ dest_base_disk_path = os.path.join(export_folder, basename)
LOG.debug(_('Copying base disk %(src_vhd_path)s to '
- '%(dest_base_disk_path)s'), locals())
+ '%(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())
+ "%(dest_base_disk_path)s and diff "
+ "VHD %(dest_vhd_path)s"), locals())
+ self._vhdutils.reconnect_parent_vhd(dest_vhd_path,
+ dest_base_disk_path)
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())
+ "diff disk %(dest_vhd_path)s"), locals())
+ self._vhdutils.merge_vhd(dest_vhd_path, dest_base_disk_path)
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())
+ LOG.debug(_("Updating Glance image %(image_id)s with content from "
+ "merged disk %(image_vhd_path)s"), locals())
update_task_state(task_state=task_states.IMAGE_UPLOADING,
expected_state=task_states.IMAGE_PENDING_UPLOAD)
- glance_image_service.update(context, image_id, image_metadata, f)
+ self._save_glance_image(context, name, image_vhd_path)
LOG.debug(_("Snapshot image %(image_id)s updated for VM "
- "%(instance_name)s"), locals())
+ "%(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()
+ try:
+ LOG.debug(_("Removing snapshot %s"), name)
+ self._vmutils.remove_vm_snapshot(snapshot_path)
+ except Exception as ex:
+ LOG.exception(ex)
+ LOG.warning(_('Failed to remove snapshot for VM %s')
+ % instance_name)
if export_folder:
LOG.debug(_('Removing folder %s '), export_folder)
shutil.rmtree(export_folder)
diff --git a/nova/virt/hyperv/vhdutils.py b/nova/virt/hyperv/vhdutils.py
new file mode 100644
index 000000000..21c4b4a6d
--- /dev/null
+++ b/nova/virt/hyperv/vhdutils.py
@@ -0,0 +1,72 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 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.
+
+import sys
+
+if sys.platform == 'win32':
+ import wmi
+
+from nova.virt.hyperv import vmutils
+from xml.etree import ElementTree
+
+
+class VHDUtils(object):
+
+ def __init__(self):
+ self._vmutils = vmutils.VMUtils()
+ if sys.platform == 'win32':
+ self._conn = wmi.WMI(moniker='//./root/virtualization')
+
+ def create_differencing_vhd(self, path, parent_path):
+ image_man_svc = self._conn.Msvm_ImageManagementService()[0]
+
+ (job_path, ret_val) = image_man_svc.CreateDifferencingVirtualHardDisk(
+ Path=path, ParentPath=parent_path)
+ self._vmutils.check_ret_val(ret_val, job_path)
+
+ def reconnect_parent_vhd(self, child_vhd_path, parent_vhd_path):
+ image_man_svc = self._conn.Msvm_ImageManagementService()[0]
+
+ (job_path, ret_val) = image_man_svc.ReconnectParentVirtualHardDisk(
+ ChildPath=child_vhd_path,
+ ParentPath=parent_vhd_path,
+ Force=True)
+ self._vmutils.check_ret_val(ret_val, job_path)
+
+ def merge_vhd(self, src_vhd_path, dest_vhd_path):
+ image_man_svc = self._conn.Msvm_ImageManagementService()[0]
+
+ (job_path, ret_val) = image_man_svc.MergeVirtualHardDisk(
+ SourcePath=src_vhd_path,
+ DestinationPath=dest_vhd_path)
+ self._vmutils.check_ret_val(ret_val, job_path)
+
+ 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)
+ self._vmutils.check_ret_val(ret_val, job_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/virt/hyperv/vif.py b/nova/virt/hyperv/vif.py
index a898d3ac2..cfe7c6a4c 100644
--- a/nova/virt/hyperv/vif.py
+++ b/nova/virt/hyperv/vif.py
@@ -15,18 +15,15 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-import sys
-import uuid
-# Check needed for unit testing on Unix
-if sys.platform == 'win32':
- import wmi
+import abc
-from abc import abstractmethod
from nova.openstack.common import cfg
from nova.openstack.common import log as logging
+from nova.virt.hyperv import networkutils
from nova.virt.hyperv import vmutils
+
hyperv_opts = [
cfg.StrOpt('vswitch_name',
default=None,
@@ -42,11 +39,11 @@ LOG = logging.getLogger(__name__)
class HyperVBaseVIFDriver(object):
- @abstractmethod
+ @abc.abstractmethod
def plug(self, instance, vif):
pass
- @abstractmethod
+ @abc.abstractmethod
def unplug(self, instance, vif):
pass
@@ -68,65 +65,17 @@ class HyperVNovaNetworkVIFDriver(HyperVBaseVIFDriver):
def __init__(self):
self._vmutils = vmutils.VMUtils()
- self._conn = wmi.WMI(moniker='//./root/virtualization')
-
- 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 ")
- % CONF.vswitch_name)
- if CONF.vswitch_name:
- LOG.debug(_("Attempting to bind NIC to %s ")
- % CONF.vswitch_name)
- bound = self._conn.Msvm_VirtualSwitch(
- ElementName=CONF.vswitch_name)
- else:
- LOG.debug(_("No vSwitch specified, attaching to default"))
- self._conn.Msvm_ExternalEthernetPort(IsBound='TRUE')
- if len(bound) == 0:
- return None
- if CONF.vswitch_name:
- return self._conn.Msvm_VirtualSwitch(
- ElementName=CONF.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]
+ self._netutils = networkutils.NetworkUtils()
def plug(self, instance, vif):
- extswitch = self._find_external_network()
- if extswitch is None:
- raise vmutils.HyperVException(_('Cannot find vSwitch'))
+ vswitch_path = self._netutils.get_external_vswitch(
+ CONF.vswitch_name)
vm_name = instance['name']
-
- nic_data = self._conn.Msvm_SyntheticEthernetPortSettingData(
- ElementName=vif['id'])[0]
-
- switch_svc = self._conn.Msvm_VirtualSwitchManagementService()[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())
-
- vms = self._conn.MSVM_ComputerSystem(ElementName=vm_name)
- vm = vms[0]
-
- nic_data.Connection = [new_port]
- self._vmutils.modify_virt_resource(self._conn, nic_data, vm)
+ LOG.debug(_('Creating vswitch port for instance: %s') % vm_name)
+ vswitch_port = self._netutils.create_vswitch_port(vswitch_path,
+ vm_name)
+ self._vmutils.set_nic_connection(vm_name, vif['id'], vswitch_port)
def unplug(self, instance, vif):
#TODO(alepilotti) Not implemented
diff --git a/nova/virt/hyperv/vmops.py b/nova/virt/hyperv/vmops.py
index 3d8958266..8ce1d508b 100644
--- a/nova/virt/hyperv/vmops.py
+++ b/nova/virt/hyperv/vmops.py
@@ -19,7 +19,6 @@
Management class for basic VM operations.
"""
import os
-import uuid
from nova.api.metadata import base as instance_metadata
from nova import exception
@@ -29,29 +28,31 @@ from nova.openstack.common import lockutils
from nova.openstack.common import log as logging
from nova import utils
from nova.virt import configdrive
-from nova.virt.hyperv import baseops
from nova.virt.hyperv import constants
+from nova.virt.hyperv import pathutils
+from nova.virt.hyperv import vhdutils
from nova.virt.hyperv import vmutils
+from nova.virt import images
LOG = logging.getLogger(__name__)
hyperv_opts = [
cfg.BoolOpt('limit_cpu_features',
- default=False,
- help='Required for live migration among '
- 'hosts with different CPU features'),
+ default=False,
+ help='Required for live migration among '
+ 'hosts with different CPU features'),
cfg.BoolOpt('config_drive_inject_password',
- default=False,
- help='Sets the admin password in the config drive image'),
+ default=False,
+ help='Sets the admin password in the config drive image'),
cfg.StrOpt('qemu_img_cmd',
default="qemu-img.exe",
help='qemu-img is used to convert between '
'different image types'),
cfg.BoolOpt('config_drive_cdrom',
- default=False,
- help='Attaches the Config Drive image as a cdrom drive '
- 'instead of a disk drive')
- ]
+ default=False,
+ help='Attaches the Config Drive image as a cdrom drive '
+ 'instead of a disk drive')
+]
CONF = cfg.CONF
CONF.register_opts(hyperv_opts)
@@ -59,19 +60,20 @@ CONF.import_opt('use_cow_images', 'nova.virt.driver')
CONF.import_opt('network_api_class', 'nova.network')
-class VMOps(baseops.BaseOps):
+class VMOps(object):
_vif_driver_class_map = {
'nova.network.quantumv2.api.API':
- 'nova.virt.hyperv.vif.HyperVQuantumVIFDriver',
+ 'nova.virt.hyperv.vif.HyperVQuantumVIFDriver',
'nova.network.api.API':
- 'nova.virt.hyperv.vif.HyperVNovaNetworkVIFDriver',
+ 'nova.virt.hyperv.vif.HyperVNovaNetworkVIFDriver',
}
def __init__(self, volumeops):
- super(VMOps, self).__init__()
-
self._vmutils = vmutils.VMUtils()
+ self._vhdutils = vhdutils.VHDUtils()
+ self._pathutils = pathutils.PathUtils()
self._volumeops = volumeops
+ self._vif_driver = None
self._load_vif_driver_class()
def _load_vif_driver_class(self):
@@ -84,124 +86,106 @@ class VMOps(baseops.BaseOps):
CONF.network_api_class)
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
+ return self._vmutils.list_instances()
def get_info(self, instance):
"""Get information about the VM."""
LOG.debug(_("get_info called for instance"), instance=instance)
- 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 = 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)d,"
- " mem=%(memusage)s, num_cpu=%(numprocs)s,"
- " uptime=%(uptime)s"), locals())
+ instance_name = instance['name']
+ if not self._vmutils.vm_exists(instance_name):
+ raise exception.InstanceNotFound(instance=instance)
+
+ info = self._vmutils.get_vm_summary_info(instance_name)
+
+ state = constants.HYPERV_POWER_STATE[info['EnabledState']]
return {'state': state,
- 'max_mem': info.MemoryUsage,
- 'mem': info.MemoryUsage,
- 'num_cpu': info.NumberOfProcessors,
- 'cpu_time': info.UpTime}
+ 'max_mem': info['MemoryUsage'],
+ 'mem': info['MemoryUsage'],
+ 'num_cpu': info['NumberOfProcessors'],
+ 'cpu_time': info['UpTime']}
def spawn(self, context, instance, image_meta, injected_files,
- admin_password, network_info, block_device_info=None):
+ admin_password, network_info, block_device_info=None):
"""Create a new VM and start it."""
- vm = self._vmutils.lookup(self._conn, instance['name'])
- if vm is not None:
- raise exception.InstanceExists(name=instance['name'])
+
+ instance_name = instance['name']
+ if self._vmutils.vm_exists(instance_name):
+ raise exception.InstanceExists(name=instance_name)
ebs_root = self._volumeops.volume_in_mapping(
self._volumeops.get_default_root_device(),
- block_device_info)
+ 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'])
+ vhdfile = self._pathutils.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=CONF.use_cow_images)
+ self._cache_image(fn=self._fetch_image,
+ context=context,
+ target=vhdfile,
+ fname=instance['image_ref'],
+ image_id=instance['image_ref'],
+ user=instance['user_id'],
+ project=instance['project_id'],
+ cow=CONF.use_cow_images)
except Exception as exn:
LOG.exception(_('cache image failed: %s'), exn)
- self.destroy(instance)
+ raise
try:
- self._create_vm(instance)
+ self._vmutils.create_vm(instance_name,
+ instance['memory_mb'],
+ instance['vcpus'],
+ CONF.limit_cpu_features)
if not ebs_root:
- self._attach_ide_drive(instance['name'], vhdfile, 0, 0,
- constants.IDE_DISK)
+ self._vmutils.attach_ide_drive(instance_name,
+ vhdfile,
+ 0,
+ 0,
+ constants.IDE_DISK)
else:
self._volumeops.attach_boot_volume(block_device_info,
- instance['name'])
+ instance_name)
- #A SCSI controller for volumes connection is created
- self._create_scsi_controller(instance['name'])
+ self._vmutils.create_scsi_controller(instance_name)
for vif in network_info:
- self._create_nic(instance['name'], vif)
+ LOG.debug(_('Creating nic for instance: %s'), instance_name)
+ self._vmutils.create_nic(instance_name,
+ vif['id'],
+ vif['address'])
self._vif_driver.plug(instance, vif)
if configdrive.required_by(instance):
self._create_config_drive(instance, injected_files,
- admin_password)
+ admin_password)
- 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._set_vm_state(instance_name,
+ constants.HYPERV_VM_STATE_ENABLED)
+ except Exception as ex:
+ LOG.exception(ex)
self.destroy(instance)
- raise exn
+ raise vmutils.HyperVException(_('Spawn instance failed'))
def _create_config_drive(self, instance, injected_files, admin_password):
if CONF.config_drive_format != 'iso9660':
vmutils.HyperVException(_('Invalid config_drive_format "%s"') %
- CONF.config_drive_format)
+ CONF.config_drive_format)
+
+ LOG.info(_('Using config drive for instance: %s'), instance=instance)
- LOG.info(_('Using config drive'), instance=instance)
extra_md = {}
if admin_password and CONF.config_drive_inject_password:
extra_md['admin_pass'] = admin_password
inst_md = instance_metadata.InstanceMetadata(instance,
- content=injected_files, extra_md=extra_md)
+ content=injected_files,
+ extra_md=extra_md)
- instance_path = self._vmutils.get_instance_path(
+ instance_path = self._pathutils.get_instance_path(
instance['name'])
configdrive_path_iso = os.path.join(instance_path, 'configdrive.iso')
LOG.info(_('Creating config drive at %(path)s'),
@@ -218,7 +202,7 @@ class VMOps(baseops.BaseOps):
if not CONF.config_drive_cdrom:
drive_type = constants.IDE_DISK
configdrive_path = os.path.join(instance_path,
- 'configdrive.vhd')
+ 'configdrive.vhd')
utils.execute(CONF.qemu_img_cmd,
'convert',
'-f',
@@ -233,302 +217,88 @@ class VMOps(baseops.BaseOps):
drive_type = constants.IDE_DVD
configdrive_path = configdrive_path_iso
- self._attach_ide_drive(instance['name'], configdrive_path, 1, 0,
- drive_type)
-
- def _create_vm(self, instance):
- """Create a VM but don't start it."""
- vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
+ self._vmutils.attach_ide_drive(instance['name'], configdrive_path,
+ 1, 0, drive_type)
- 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 CONF.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 _get_ide_controller(self, vm, ctrller_addr):
- #Find the IDE controller for the vm.
- 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 == str(ctrller_addr)]
- return ctrller
-
- def _attach_ide_drive(self, vm_name, path, ctrller_addr, drive_addr,
- drive_type=constants.IDE_DISK):
- """Create an IDE drive and attach it to the vm."""
- LOG.debug(_('Creating disk for %(vm_name)s by attaching'
- ' disk file %(path)s') % locals())
-
- vms = self._conn.MSVM_ComputerSystem(ElementName=vm_name)
- vm = vms[0]
-
- ctrller = self._get_ide_controller(vm, ctrller_addr)
-
- if drive_type == constants.IDE_DISK:
- resSubType = 'Microsoft Synthetic Disk Drive'
- elif drive_type == constants.IDE_DVD:
- resSubType = 'Microsoft Synthetic DVD Drive'
-
- #Find the default disk drive object for the vm and clone it.
- drivedflt = self._conn.query(
- "SELECT * FROM Msvm_ResourceAllocationSettingData \
- WHERE ResourceSubType LIKE '%(resSubType)s'\
- AND InstanceID LIKE '%%Default%%'" % locals())[0]
- drive = self._vmutils.clone_wmi_obj(self._conn,
- 'Msvm_ResourceAllocationSettingData', drivedflt)
- #Set the IDE ctrller as parent.
- drive.Parent = ctrller[0].path_()
- drive.Address = drive_addr
- #Add the cloned disk drive object to the vm.
- new_resources = self._vmutils.add_virt_resource(self._conn,
- drive, vm)
- if new_resources is None:
- raise vmutils.HyperVException(
- _('Failed to add drive to VM %s') %
- vm_name)
- drive_path = new_resources[0]
- LOG.debug(_('New %(drive_type)s drive path is %(drive_path)s') %
- locals())
-
- if drive_type == constants.IDE_DISK:
- resSubType = 'Microsoft Virtual Hard Disk'
- elif drive_type == constants.IDE_DVD:
- resSubType = 'Microsoft Virtual CD/DVD Disk'
-
- #Find the default VHD disk object.
- drivedefault = self._conn.query(
- "SELECT * FROM Msvm_ResourceAllocationSettingData \
- WHERE ResourceSubType LIKE '%(resSubType)s' AND \
- InstanceID LIKE '%%Default%%' " % locals())[0]
-
- #Clone the default and point it to the image file.
- res = self._vmutils.clone_wmi_obj(self._conn,
- 'Msvm_ResourceAllocationSettingData', drivedefault)
- #Set the new drive as the parent.
- res.Parent = drive_path
- res.Connection = [path]
-
- #Add the new vhd object as a virtual hard disk to the vm.
- new_resources = self._vmutils.add_virt_resource(self._conn, res, vm)
- if new_resources is None:
- raise vmutils.HyperVException(
- _('Failed to add %(drive_type)s image to VM %(vm_name)s') %
- locals())
- LOG.info(_('Created drive type %(drive_type)s for %(vm_name)s') %
- locals())
-
- def _create_nic(self, vm_name, vif):
- """Create a (synthetic) nic and attach it to the vm."""
- LOG.debug(_('Creating nic for %s '), vm_name)
-
- #Create a new nic
- 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])
-
- #Configure the nic
- new_nic_data.ElementName = vif['id']
- new_nic_data.Address = vif['address'].replace(':', '')
- new_nic_data.StaticMacAddress = 'True'
- new_nic_data.VirtualSystemIdentifiers = ['{' + str(uuid.uuid4()) + '}']
-
- #Add the new nic to the vm
- vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name)
- vm = vms[0]
-
- 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 destroy(self, instance, network_info=None, cleanup=True,
+ destroy_disks=True):
+ instance_name = instance['name']
+ LOG.debug(_("Got request to destroy instance: %s"), instance_name)
+ try:
+ if self._vmutils.vm_exists(instance_name):
+ volumes_drives_list = self._vmutils.destroy_vm(instance_name,
+ destroy_disks)
+ #Disconnect volumes
+ for volume_drive in volumes_drives_list:
+ self._volumeops.disconnect_volume(volume_drive)
+ else:
+ LOG.debug(_("Instance not found: %s"), instance_name)
+ except Exception as ex:
+ LOG.exception(ex)
+ raise vmutils.HyperVException(_('Failed to destroy instance: %s') %
+ instance_name)
def reboot(self, instance, network_info, reboot_type):
"""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_disks=True):
- """Destroy the VM. Also destroy the associated VHD disk files."""
- 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'])
- if destroy_disks:
- #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 %(name)s")
- % {'vhdfile': vhdfile, 'name': instance['name']})
- vhdfile.Delete()
+ LOG.debug(_("reboot instance"), instance=instance)
+ self._set_vm_state(instance['name'],
+ constants.HYPERV_VM_STATE_REBOOT)
def pause(self, instance):
"""Pause VM instance."""
LOG.debug(_("Pause instance"), instance=instance)
- self._set_vm_state(instance["name"], 'Paused')
+ self._set_vm_state(instance["name"],
+ constants.HYPERV_VM_STATE_PAUSED)
def unpause(self, instance):
"""Unpause paused VM instance."""
LOG.debug(_("Unpause instance"), instance=instance)
- self._set_vm_state(instance["name"], 'Enabled')
+ self._set_vm_state(instance["name"],
+ constants.HYPERV_VM_STATE_ENABLED)
def suspend(self, instance):
"""Suspend the specified instance."""
print instance
LOG.debug(_("Suspend instance"), instance=instance)
- self._set_vm_state(instance["name"], 'Suspended')
+ self._set_vm_state(instance["name"],
+ constants.HYPERV_VM_STATE_SUSPENDED)
def resume(self, instance):
"""Resume the suspended VM instance."""
LOG.debug(_("Resume instance"), instance=instance)
- self._set_vm_state(instance["name"], 'Enabled')
+ self._set_vm_state(instance["name"],
+ constants.HYPERV_VM_STATE_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')
+ self._set_vm_state(instance["name"],
+ constants.HYPERV_VM_STATE_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')
+ self._set_vm_state(instance["name"],
+ constants.HYPERV_VM_STATE_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:
+ try:
+ self._vmutils.set_vm_state(vm_name, req_state)
+ LOG.debug(_("Successfully changed state of VM %(vm_name)s"
+ " to: %(req_state)s") % locals())
+ except Exception as ex:
+ LOG.exception(ex)
msg = _("Failed to change vm state of %(vm_name)s"
" to %(req_state)s") % locals()
- LOG.error(msg)
raise vmutils.HyperVException(msg)
- 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.
+ def _fetch_image(self, target, context, image_id, user, project,
+ *args, **kwargs):
+ images.fetch(context, image_id, target, user, project)
+
+ def _cache_image(self, fn, target, fname, cow=False, size=None,
+ *args, **kwargs):
+ """Wrapper for a method that creates and caches an image.
This wrapper will save the image into a common store and create a
copy for use by the hypervisor.
@@ -543,32 +313,23 @@ class VMOps(baseops.BaseOps):
"""
@lockutils.synchronized(fname, 'nova-')
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(path):
+ fn(target=path, *args, **kwargs)
- if not os.path.exists(target):
- LOG.debug(_("use_cow_image:%s"), cow)
+ if not self._pathutils.vhd_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:
+ parent_path = self._pathutils.get_base_vhd_path(fname)
+ call_if_not_exists(parent_path, fn, *args, **kwargs)
+
+ LOG.debug(_("Creating differencing VHD. Parent: "
+ "%(parent_path)s, Target: %(target)s") % locals())
+ try:
+ self._vhdutils.create_differencing_vhd(target, parent_path)
+ except Exception as ex:
+ LOG.exception(ex)
raise vmutils.HyperVException(
- _('Failed to create Difference Disk from '
- '%(base)s to %(target)s') % locals())
-
+ _('Failed to create a differencing disk from '
+ '%(parent_path)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
index d899f977d..0305d8306 100644
--- a/nova/virt/hyperv/vmutils.py
+++ b/nova/virt/hyperv/vmutils.py
@@ -16,24 +16,20 @@
# under the License.
"""
-Utility class for VM related operations.
+Utility class for VM related operations on Hyper-V.
"""
-import os
-import shutil
import sys
import time
import uuid
+if sys.platform == 'win32':
+ import wmi
+
from nova import exception
from nova.openstack.common import cfg
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
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
@@ -45,19 +41,342 @@ class HyperVException(exception.NovaException):
class VMUtils(object):
- def lookup(self, conn, i):
- vms = conn.Msvm_ComputerSystem(ElementName=i)
+
+ def __init__(self):
+ if sys.platform == 'win32':
+ self._conn = wmi.WMI(moniker='//./root/virtualization')
+ self._conn_cimv2 = wmi.WMI(moniker='//./root/cimv2')
+
+ def list_instances(self):
+ """Return the names of all the instances known to Hyper-V."""
+ vm_names = [v.ElementName
+ for v in self._conn.Msvm_ComputerSystem(['ElementName'],
+ Caption="Virtual Machine")]
+ return vm_names
+
+ def get_vm_summary_info(self, vm_name):
+ vm = self._lookup_vm_check(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]
+ #See http://msdn.microsoft.com/en-us/library/cc160706%28VS.85%29.aspx
+ (ret_val, 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)
+ if ret_val:
+ raise HyperVException(_('Cannot get VM summary data for: %s')
+ % vm_name)
+
+ si = summary_info[0]
+ memory_usage = None
+ if si.MemoryUsage is not None:
+ memory_usage = long(si.MemoryUsage)
+ up_time = None
+ if si.UpTime is not None:
+ up_time = long(si.UpTime)
+
+ summary_info_dict = {'NumberOfProcessors': si.NumberOfProcessors,
+ 'EnabledState': si.EnabledState,
+ 'MemoryUsage': memory_usage,
+ 'UpTime': up_time}
+ return summary_info_dict
+
+ def _lookup_vm_check(self, vm_name):
+ vm = self._lookup_vm(vm_name)
+ if not vm:
+ raise HyperVException(_('VM not found: %s') % vm_name)
+ return vm
+
+ def _lookup_vm(self, vm_name):
+ vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name)
n = len(vms)
if n == 0:
return None
elif n > 1:
- raise HyperVException(_('duplicate name found: %s') % i)
+ raise HyperVException(_('Duplicate VM name found: %s') % vm_name)
else:
- return vms[0].ElementName
+ return vms[0]
+
+ def vm_exists(self, vm_name):
+ return self._lookup_vm(vm_name) is not None
+
+ def _get_vm_setting_data(self, vm):
+ vmsettings = vm.associators(
+ wmi_result_class='Msvm_VirtualSystemSettingData')
+ # Avoid snapshots
+ return [s for s in vmsettings if s.SettingType == 3][0]
+
+ def _set_vm_memory(self, vm, vmsetting, memory_mb):
+ memsetting = vmsetting.associators(
+ wmi_result_class='Msvm_MemorySettingData')[0]
+ #No Dynamic Memory, so reservation, limit and quantity are identical.
+ mem = long(memory_mb)
+ memsetting.VirtualQuantity = mem
+ memsetting.Reservation = mem
+ memsetting.Limit = mem
+
+ self._modify_virt_resource(memsetting, vm.path_())
+
+ def _set_vm_vcpus(self, vm, vmsetting, vcpus_num, limit_cpu_features):
+ procsetting = vmsetting.associators(
+ wmi_result_class='Msvm_ProcessorSettingData')[0]
+ vcpus = long(vcpus_num)
+ procsetting.VirtualQuantity = vcpus
+ procsetting.Reservation = vcpus
+ procsetting.Limit = 100000 # static assignment to 100%
+ procsetting.LimitProcessorFeatures = limit_cpu_features
+
+ self._modify_virt_resource(procsetting, vm.path_())
+
+ def create_vm(self, vm_name, memory_mb, vcpus_num, limit_cpu_features):
+ """Creates a VM."""
+ vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
+
+ vs_gs_data = self._conn.Msvm_VirtualSystemGlobalSettingData.new()
+ vs_gs_data.ElementName = vm_name
+
+ LOG.debug(_('Creating VM %s'), vm_name)
+ (job_path,
+ ret_val) = vs_man_svc.DefineVirtualSystem([], None,
+ vs_gs_data.GetText_(1))[1:]
+ self.check_ret_val(ret_val, job_path)
+
+ vm = self._lookup_vm_check(vm_name)
+ vmsetting = self._get_vm_setting_data(vm)
+
+ LOG.debug(_('Setting memory for vm %s'), vm_name)
+ self._set_vm_memory(vm, vmsetting, memory_mb)
+
+ LOG.debug(_('Set vCPUs for vm %s'), vm_name)
+ self._set_vm_vcpus(vm, vmsetting, vcpus_num, limit_cpu_features)
+
+ def get_vm_iscsi_controller(self, vm_name):
+ vm = self._lookup_vm_check(vm_name)
+
+ vmsettings = vm.associators(
+ wmi_result_class='Msvm_VirtualSystemSettingData')
+ rasds = vmsettings[0].associators(
+ wmi_result_class='MSVM_ResourceAllocationSettingData')
+ res = [r for r in rasds
+ if r.ResourceSubType ==
+ 'Microsoft Synthetic SCSI Controller'][0]
+ return res.path_()
+
+ def _get_vm_ide_controller(self, vm, ctrller_addr):
+ vmsettings = vm.associators(
+ wmi_result_class='Msvm_VirtualSystemSettingData')
+ rasds = vmsettings[0].associators(
+ wmi_result_class='MSVM_ResourceAllocationSettingData')
+ return [r for r in rasds
+ if r.ResourceSubType == 'Microsoft Emulated IDE Controller'
+ and r.Address == str(ctrller_addr)][0].path_()
+
+ def get_vm_ide_controller(self, vm_name, ctrller_addr):
+ vm = self._lookup_vm_check(vm_name)
+ return self._get_vm_ide_controller(vm, ctrller_addr)
+
+ def get_attached_disks_count(self, scsi_controller_path):
+ volumes = self._conn.query("SELECT * FROM "
+ "Msvm_ResourceAllocationSettingData "
+ "WHERE ResourceSubType LIKE "
+ "'Microsoft Physical Disk Drive' "
+ "AND Parent = '%s'" %
+ scsi_controller_path.replace("'", "''"))
+ return len(volumes)
+
+ def attach_ide_drive(self, vm_name, path, ctrller_addr, drive_addr,
+ drive_type=constants.IDE_DISK):
+ """Create an IDE drive and attach it to the vm."""
+
+ vm = self._lookup_vm_check(vm_name)
+
+ ctrller_path = self._get_vm_ide_controller(vm, ctrller_addr)
+
+ if drive_type == constants.IDE_DISK:
+ res_sub_type = 'Microsoft Synthetic Disk Drive'
+ elif drive_type == constants.IDE_DVD:
+ res_sub_type = 'Microsoft Synthetic DVD Drive'
+
+ #Find the default disk drive object for the vm and clone it.
+ drivedflt = self._conn.query("SELECT * FROM "
+ "Msvm_ResourceAllocationSettingData "
+ "WHERE ResourceSubType LIKE "
+ "'%(res_sub_type)s' AND InstanceID LIKE "
+ "'%%Default%%'" % locals())[0]
+ drive = self._clone_wmi_obj('Msvm_ResourceAllocationSettingData',
+ drivedflt)
+ #Set the IDE ctrller as parent.
+ drive.Parent = ctrller_path
+ drive.Address = drive_addr
+ #Add the cloned disk drive object to the vm.
+ new_resources = self._add_virt_resource(drive, vm.path_())
+ drive_path = new_resources[0]
+
+ if drive_type == constants.IDE_DISK:
+ res_sub_type = 'Microsoft Virtual Hard Disk'
+ elif drive_type == constants.IDE_DVD:
+ res_sub_type = 'Microsoft Virtual CD/DVD Disk'
+
+ #Find the default VHD disk object.
+ drivedefault = self._conn.query("SELECT * FROM "
+ "Msvm_ResourceAllocationSettingData "
+ "WHERE ResourceSubType LIKE "
+ "'%(res_sub_type)s' AND "
+ "InstanceID LIKE '%%Default%%'"
+ % locals())[0]
+
+ #Clone the default and point it to the image file.
+ res = self._clone_wmi_obj('Msvm_ResourceAllocationSettingData',
+ drivedefault)
+ #Set the new drive as the parent.
+ res.Parent = drive_path
+ res.Connection = [path]
+
+ #Add the new vhd object as a virtual hard disk to the vm.
+ self._add_virt_resource(res, vm.path_())
+
+ def create_scsi_controller(self, vm_name):
+ """Create an iscsi controller ready to mount volumes."""
+
+ vm = self._lookup_vm_check(vm_name)
+ scsicontrldflt = self._conn.query("SELECT * FROM "
+ "Msvm_ResourceAllocationSettingData "
+ "WHERE ResourceSubType = 'Microsoft "
+ "Synthetic SCSI Controller' AND "
+ "InstanceID LIKE '%Default%'")[0]
+ if scsicontrldflt is None:
+ raise HyperVException(_('Controller not found'))
+ scsicontrl = self._clone_wmi_obj('Msvm_ResourceAllocationSettingData',
+ scsicontrldflt)
+ scsicontrl.VirtualSystemIdentifiers = ['{' + str(uuid.uuid4()) + '}']
+ scsiresource = self._add_virt_resource(scsicontrl, vm.path_())
+
+ def attach_volume_to_controller(self, vm_name, controller_path, address,
+ mounted_disk_path):
+ """Attach a volume to a controller."""
- def check_job_status(self, jobpath):
- """Poll WMI job state for completion."""
- job_wmi_path = jobpath.replace('\\', '/')
+ vm = self._lookup_vm_check(vm_name)
+
+ diskdflt = self._conn.query("SELECT * FROM "
+ "Msvm_ResourceAllocationSettingData "
+ "WHERE ResourceSubType LIKE "
+ "'Microsoft Physical Disk Drive' "
+ "AND InstanceID LIKE '%Default%'")[0]
+ diskdrive = self._clone_wmi_obj('Msvm_ResourceAllocationSettingData',
+ diskdflt)
+ diskdrive.Address = address
+ diskdrive.Parent = controller_path
+ diskdrive.HostResource = [mounted_disk_path]
+ self._add_virt_resource(diskdrive, vm.path_())
+
+ def set_nic_connection(self, vm_name, nic_name, vswitch_port):
+ nic_data = self._get_nic_data_by_name(nic_name)
+ nic_data.Connection = [vswitch_port]
+
+ vm = self._lookup_vm_check(vm_name)
+ self._modify_virt_resource(nic_data, vm.path_())
+
+ def _get_nic_data_by_name(self, name):
+ return self._conn.Msvm_SyntheticEthernetPortSettingData(
+ ElementName=name)[0]
+
+ def create_nic(self, vm_name, nic_name, mac_address):
+ """Create a (synthetic) nic and attach it to the vm."""
+ #Create a new nic
+ 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._clone_wmi_obj(
+ 'Msvm_SyntheticEthernetPortSettingData', default_nic_data[0])
+
+ #Configure the nic
+ new_nic_data.ElementName = nic_name
+ new_nic_data.Address = mac_address.replace(':', '')
+ new_nic_data.StaticMacAddress = 'True'
+ new_nic_data.VirtualSystemIdentifiers = ['{' + str(uuid.uuid4()) + '}']
+
+ #Add the new nic to the vm
+ vm = self._lookup_vm_check(vm_name)
+
+ self._add_virt_resource(new_nic_data, vm.path_())
+
+ def set_vm_state(self, vm_name, req_state):
+ """Set the desired state of the VM."""
+
+ vm = self._lookup_vm_check(vm_name)
+ (job_path, ret_val) = vm.RequestStateChange(req_state)
+ #Invalid state for current operation (32775) typically means that
+ #the VM is already in the state requested
+ self.check_ret_val(ret_val, job_path, [0, 32775])
+ LOG.debug(_("Successfully changed vm state of %(vm_name)s"
+ " to %(req_state)s") % locals())
+
+ def destroy_vm(self, vm_name, destroy_disks=True):
+ """Destroy the VM. Also destroy the associated VHD disk files."""
+
+ vm = self._lookup_vm_check(vm_name)
+
+ #Stop the VM first.
+ self.set_vm_state(vm_name, constants.HYPERV_VM_STATE_DISABLED)
+
+ vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
+ vmsettings = vm.associators(
+ wmi_result_class='Msvm_VirtualSystemSettingData')
+ rasds = vmsettings[0].associators(
+ wmi_result_class='MSVM_ResourceAllocationSettingData')
+ disk_resources = [r for r in rasds
+ if r.ResourceSubType ==
+ 'Microsoft Virtual Hard Disk']
+ volume_resources = [r for r in rasds
+ if r.ResourceSubType ==
+ 'Microsoft Physical Disk Drive']
+
+ #Collect volumes information before destroying the VM.
+ volumes_drives_list = []
+ for volume_resource in volume_resources:
+ drive_path = volume_resource.HostResource[0]
+ #Appending the Msvm_Disk path
+ volumes_drives_list.append(drive_path)
+
+ #Collect disk file information before destroying the VM.
+ disk_files = []
+ for disk_resource in disk_resources:
+ disk_files.extend([c for c in disk_resource.Connection])
+
+ #Remove the VM. Does not destroy disks.
+ (job_path, ret_val) = vs_man_svc.DestroyVirtualSystem(vm.path_())
+ self.check_ret_val(ret_val, job_path)
+
+ if destroy_disks:
+ #Delete associated vhd disk files.
+ for disk in disk_files:
+ LOG.debug(_("Deleting disk file: %(disk)s") % locals())
+ self._delete_file(disk)
+
+ return volumes_drives_list
+
+ def _delete_file(self, path):
+ f = self._conn_cimv2.query("Select * from CIM_DataFile where "
+ "Name = '%s'" % path.replace("'", "''"))[0]
+ f.Delete()
+
+ def check_ret_val(self, ret_val, job_path, success_values=[0]):
+ if ret_val == constants.WMI_JOB_STATUS_STARTED:
+ self._wait_for_job(job_path)
+ elif ret_val not in success_values:
+ raise HyperVException(_('Operation failed with return value: %s')
+ % ret_val)
+
+ def _wait_for_job(self, job_path):
+ """Poll WMI job state and wait for completion."""
+
+ job_wmi_path = job_path.replace('\\', '/')
job = wmi.WMI(moniker=job_wmi_path)
while job.JobState == constants.WMI_JOB_STATE_RUNNING:
@@ -69,54 +388,30 @@ class VMUtils(object):
err_sum_desc = job.ErrorSummaryDescription
err_desc = job.ErrorDescription
err_code = job.ErrorCode
- LOG.debug(_("WMI job failed with status %(job_state)d. "
- "Error details: %(err_sum_desc)s - %(err_desc)s - "
- "Error code: %(err_code)d") % locals())
+ raise HyperVException(_("WMI job failed with status "
+ "%(job_state)d. Error details: "
+ "%(err_sum_desc)s - %(err_desc)s - "
+ "Error code: %(err_code)d")
+ % locals())
else:
(error, ret_val) = job.GetError()
if not ret_val and error:
- LOG.debug(_("WMI job failed with status %(job_state)d. "
- "Error details: %(error)s") % locals())
+ raise HyperVException(_("WMI job failed with status "
+ "%(job_state)d. Error details: "
+ "%(error)s") % locals())
else:
- LOG.debug(_("WMI job failed with status %(job_state)d. "
- "No error description available") % locals())
- return False
+ raise HyperVException(_("WMI job failed with status "
+ "%(job_state)d. No error "
+ "description available")
+ % locals())
desc = job.Description
elap = job.ElapsedTime
LOG.debug(_("WMI job succeeded: %(desc)s, Elapsed=%(elap)s")
- % locals())
- return True
-
- def get_instance_path(self, instance_name):
- instance_path = os.path.join(CONF.instances_path, instance_name)
- if not os.path.exists(instance_path):
- LOG.debug(_('Creating folder %s '), instance_path)
- os.makedirs(instance_path)
- return instance_path
-
- def get_vhd_path(self, instance_name):
- instance_path = self.get_instance_path(instance_name)
- return os.path.join(instance_path, instance_name + ".vhd")
-
- def get_base_vhd_path(self, image_name):
- base_dir = os.path.join(CONF.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(CONF.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):
+ % locals())
+
+ def _clone_wmi_obj(self, wmi_class, wmi_obj):
"""Clone a WMI object."""
- cl = conn.__getattr__(wmi_class) # get the class
+ cl = getattr(self._conn, wmi_class) # get the class
newinst = cl.new()
#Copy the properties from the original.
for prop in wmi_obj._properties:
@@ -125,51 +420,78 @@ class VMUtils(object):
strguid.append(str(uuid.uuid4()))
newinst.Properties_.Item(prop).Value = strguid
else:
- newinst.Properties_.Item(prop).Value = \
- wmi_obj.Properties_.Item(prop).Value
+ prop_value = wmi_obj.Properties_.Item(prop).Value
+ newinst.Properties_.Item(prop).Value = prop_value
+
return newinst
- def add_virt_resource(self, conn, res_setting_data, target_vm):
+ def _add_virt_resource(self, res_setting_data, vm_path):
"""Adds a new resource 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
+ vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
+ res_xml = [res_setting_data.GetText_(1)]
+ (job_path,
+ new_resources,
+ ret_val) = vs_man_svc.AddVirtualSystemResources(res_xml, vm_path)
+ self.check_ret_val(ret_val, job_path)
+ return new_resources
- def modify_virt_resource(self, conn, res_setting_data, target_vm):
+ def _modify_virt_resource(self, res_setting_data, vm_path):
"""Updates a VM resource."""
- vs_man_svc = conn.Msvm_VirtualSystemManagementService()[0]
- (job, ret_val) = vs_man_svc.ModifyVirtualSystemResources(
+ vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
+ (job_path, ret_val) = vs_man_svc.ModifyVirtualSystemResources(
ResourceSettingData=[res_setting_data.GetText_(1)],
- ComputerSystem=target_vm.path_())
- if ret_val == constants.WMI_JOB_STATUS_STARTED:
- success = self.check_job_status(job)
- else:
- success = (ret_val == 0)
- return success
+ ComputerSystem=vm_path)
+ self.check_ret_val(ret_val, job_path)
- def remove_virt_resource(self, conn, res_setting_data, target_vm):
+ def _remove_virt_resource(self, res_setting_data, vm_path):
"""Removes a VM resource."""
- 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
+ vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
+ res_path = [res_setting_data.path_()]
+ (job_path, ret_val) = vs_man_svc.RemoveVirtualSystemResources(res_path,
+ vm_path)
+ self.check_ret_val(ret_val, job_path)
+
+ def take_vm_snapshot(self, vm_name):
+ vm = self._lookup_vm_check(vm_name)
+
+ vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
+
+ (job_path, ret_val,
+ snp_setting_data) = vs_man_svc.CreateVirtualSystemSnapshot(vm.path_())
+ self.check_ret_val(ret_val, job_path)
+
+ job_wmi_path = job_path.replace('\\', '/')
+ job = wmi.WMI(moniker=job_wmi_path)
+ snp_setting_data = job.associators(
+ wmi_result_class='Msvm_VirtualSystemSettingData')[0]
+ return snp_setting_data.path_()
+
+ def remove_vm_snapshot(self, snapshot_path):
+ vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
+
+ (job_path, ret_val) = vs_man_svc.RemoveVirtualSystemSnapshot(
+ snapshot_path)
+ self.check_ret_val(ret_val, job_path)
+
+ def detach_vm_disk(self, vm_name, disk_path):
+ vm = self._lookup_vm_check(vm_name)
+ physical_disk = self._get_mounted_disk_resource_from_path(
+ disk_path)
+ self._remove_virt_resource(physical_disk, vm.path_())
+
+ def _get_mounted_disk_resource_from_path(self, disk_path):
+ physical_disks = self._conn.query("SELECT * FROM "
+ "Msvm_ResourceAllocationSettingData"
+ " WHERE ResourceSubType = "
+ "'Microsoft Physical Disk Drive'")
+ for physical_disk in physical_disks:
+ if physical_disk.HostResource:
+ if physical_disk.HostResource[0].lower() == disk_path.lower():
+ return physical_disk
- def fetch_image(self, target, context, image_id, user, project,
- *args, **kwargs):
- images.fetch(context, image_id, target, user, project)
+ def get_mounted_disk_by_drive_number(self, device_number):
+ mounted_disks = self._conn.query("SELECT * FROM Msvm_DiskDrive "
+ "WHERE DriveNumber=" +
+ str(device_number))
+ if len(mounted_disks):
+ return mounted_disks[0].path_()
diff --git a/nova/virt/hyperv/volumeops.py b/nova/virt/hyperv/volumeops.py
index b69cf7bf1..a7e56b739 100644
--- a/nova/virt/hyperv/volumeops.py
+++ b/nova/virt/hyperv/volumeops.py
@@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Pedro Navarro Perez
+# Copyright 2013 Cloudbase Solutions Srl
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -20,210 +21,140 @@ Management class for Storage-related functions (attach, detach, etc).
"""
import time
-from nova import block_device
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 hostutils
from nova.virt.hyperv import vmutils
from nova.virt.hyperv import volumeutils
-from nova.virt.hyperv import volumeutilsV2
+from nova.virt.hyperv import volumeutilsv2
LOG = logging.getLogger(__name__)
hyper_volumeops_opts = [
cfg.IntOpt('hyperv_attaching_volume_retry_count',
- default=10,
- help='The number of times we retry on attaching volume '),
+ default=10,
+ help='The number of times we retry on attaching volume '),
cfg.IntOpt('hyperv_wait_between_attach_retry',
- default=5,
- help='The seconds to wait between a volume attachment attempt'),
+ default=5,
+ help='The seconds to wait between an volume '
+ 'attachment attempt'),
cfg.BoolOpt('force_volumeutils_v1',
- default=False,
- help='Force volumeutils v1'),
- ]
+ default=False,
+ help='Force volumeutils v1'),
+]
CONF = cfg.CONF
CONF.register_opts(hyper_volumeops_opts)
CONF.import_opt('my_ip', 'nova.netconf')
-class VolumeOps(baseops.BaseOps):
+class VolumeOps(object):
"""
Management class for Volume-related tasks
"""
def __init__(self):
- super(VolumeOps, self).__init__()
-
+ self._hostutils = hostutils.HostUtils()
self._vmutils = vmutils.VMUtils()
- self._driver = driver
- self._block_device = block_device
- self._time = time
+ self._volutils = self._get_volume_utils()
self._initiator = None
self._default_root_device = 'vda'
- self._attaching_volume_retry_count = \
- CONF.hyperv_attaching_volume_retry_count
- self._wait_between_attach_retry = \
- CONF.hyperv_wait_between_attach_retry
- self._volutils = self._get_volume_utils()
def _get_volume_utils(self):
- if(not CONF.force_volumeutils_v1) and \
- (self._get_hypervisor_version() >= 6.2):
- return volumeutilsV2.VolumeUtilsV2(
- self._conn_storage, self._conn_wmi)
+ if(not CONF.force_volumeutils_v1 and
+ self._hostutils.get_windows_version() >= 6.2):
+ return volumeutilsv2.VolumeUtilsV2()
else:
- return volumeutils.VolumeUtils(self._conn_wmi)
-
- def _get_hypervisor_version(self):
- """Get hypervisor version.
- :returns: hypervisor version (ex. 12003)
- """
- version = self._conn_cimv2.Win32_OperatingSystem()[0]\
- .Version
- LOG.info(_('Windows version: %s ') % version)
- return version
+ return 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(
+ ebs_root = 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)
+ target_portal)
try:
#Getting the mounted disk
- mounted_disk = self._get_mounted_disk_from_lun(target_iqn,
- target_lun)
- #Attach to IDE controller
+ mounted_disk_path = self._get_mounted_disk_from_lun(target_iqn,
+ target_lun)
#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"]
+ ctrller_path = self._vmutils.get_vm_ide_controller(vm_name, 0)
#Attaching to the same slot as the VHD disk file
- self._attach_volume_to_controller(ctrller, 0, mounted_disk, vm)
+ self._vmutils.attach_volume_to_controller(vm_name,
+ ctrller_path, 0,
+ mounted_disk_path)
except Exception as exn:
LOG.exception(_('Attach boot from volume failed: %s'), exn)
self._volutils.logout_storage_target(target_iqn)
raise vmutils.HyperVException(
- _('Unable to attach boot volume to instance %s')
- % vm_name)
+ _('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)
+ block_device_info)
- def attach_volume(self, connection_info, instance_name, mountpoint):
+ def attach_volume(self, connection_info, instance_name):
"""Attach a volume to the SCSI controller."""
- LOG.debug(_("Attach_volume: %(connection_info)s, %(instance_name)s,"
- " %(mountpoint)s") % locals())
+ LOG.debug(_("Attach_volume: %(connection_info)s to %(instance_name)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)
+ target_portal)
try:
#Getting the mounted disk
- mounted_disk = self._get_mounted_disk_from_lun(target_iqn,
- target_lun)
+ mounted_disk_path = 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)
+ ctrller_path = self._vmutils.get_vm_iscsi_controller(instance_name)
+
+ slot = self._get_free_controller_slot(ctrller_path)
+ self._vmutils.attach_volume_to_controller(instance_name,
+ ctrller_path,
+ slot,
+ mounted_disk_path)
except Exception as exn:
LOG.exception(_('Attach volume failed: %s'), exn)
self._volutils.logout_storage_target(target_iqn)
- raise vmutils.HyperVException(
- _('Unable to attach volume to instance %s')
- % instance_name)
+ 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_path):
+ #Slots starts from 0, so the lenght of the disks gives us the free slot
+ return self._vmutils.get_attached_disks_count(scsi_controller_path)
- 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 length of the disks gives us the free slot
- return len(volumes)
-
- def detach_volume(self, connection_info, instance_name, mountpoint):
+ def detach_volume(self, connection_info, instance_name):
"""Dettach a volume to the SCSI controller."""
- LOG.debug(_("Detach_volume: %(connection_info)s, %(instance_name)s,"
- " %(mountpoint)s") % locals())
+ LOG.debug(_("Detach_volume: %(connection_info)s "
+ "from %(instance_name)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)
+ mounted_disk_path = self._get_mounted_disk_from_lun(target_iqn,
+ target_lun)
+
+ LOG.debug(_("Detaching physical disk from instance: %s"),
+ mounted_disk_path)
+ self._vmutils.detach_vm_disk(instance_name, mounted_disk_path)
+
#Sending logout
self._volutils.logout_storage_target(target_iqn)
def get_volume_connector(self, instance):
if not self._initiator:
- self._initiator = self._get_iscsi_initiator()
+ self._initiator = self._volutils.get_iscsi_initiator()
if not self._initiator:
LOG.warn(_('Could not determine iscsi initiator name'),
instance=instance)
@@ -232,87 +163,35 @@ class VolumeOps(baseops.BaseOps):
'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
+ device_number = self._volutils.get_device_number_for_target(target_iqn,
+ target_lun)
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)
+ raise vmutils.HyperVException(_('Unable to find a mounted '
+ 'disk for target_iqn: %s')
+ % target_iqn)
+ LOG.debug(_('Device number: %(device_number)s, '
+ 'target lun: %(target_lun)s') % locals())
#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:
+ for i in range(1, CONF.hyperv_attaching_volume_retry_count):
+ mounted_disk_path = self._vmutils.get_mounted_disk_by_drive_number(
+ device_number)
+ if mounted_disk_path:
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
+ time.sleep(CONF.hyperv_wait_between_attach_retry)
+
+ if not mounted_disk_path:
+ raise vmutils.HyperVException(_('Unable to find a mounted disk '
+ 'for target_iqn: %s')
+ % target_iqn)
+ return mounted_disk_path
def disconnect_volume(self, physical_drive_path):
#Get the session_id of the ISCSI connection
- session_id = self._get_session_id_from_mounted_disk(
+ session_id = self._volutils.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
index 051c37fd6..713ace258 100644
--- a/nova/virt/hyperv/volumeutils.py
+++ b/nova/virt/hyperv/volumeutils.py
@@ -1,6 +1,7 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012 Pedro Navarro Perez
+# Copyright 2013 Cloudbase Solutions Srl
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -35,47 +36,48 @@ CONF = cfg.CONF
class VolumeUtils(basevolumeutils.BaseVolumeUtils):
- def __init__(self, conn_wmi):
- self._conn_wmi = conn_wmi
+ def __init__(self):
+ super(VolumeUtils, self).__init__()
- def execute(self, *args, **kwargs):
- _PIPE = subprocess.PIPE # pylint: disable=E1101
- proc = subprocess.Popen(
- [args],
- stdin=_PIPE,
- stdout=_PIPE,
- stderr=_PIPE,
- )
- 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 execute(self, *args, **kwargs):
+ _PIPE = subprocess.PIPE # pylint: disable=E1101
+ proc = subprocess.Popen(
+ [args],
+ stdin=_PIPE,
+ stdout=_PIPE,
+ stderr=_PIPE,
+ )
+ 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 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 to avoid sleep
- time.sleep(CONF.hyperv_wait_between_attach_retry)
+ 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 to avoid sleep
+ time.sleep(CONF.hyperv_wait_between_attach_retry)
- def logout_storage_target(self, target_iqn):
- """Logs out storage target through its session id."""
+ def logout_storage_target(self, target_iqn):
+ """Logs out storage target through its session id."""
- sessions = self._conn_wmi.query(
- "SELECT * FROM MSiSCSIInitiator_SessionClass \
- WHERE TargetName='" + target_iqn + "'")
- for session in sessions:
- self.execute_log_out(session.SessionId)
+ sessions = self._conn_wmi.query("SELECT * FROM "
+ "MSiSCSIInitiator_SessionClass "
+ "WHERE TargetName='%s'" % 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 execute_log_out(self, session_id):
+ """Executes log out of the session described by its session ID."""
+ self.execute('iscsicli.exe ' + 'logouttarget ' + session_id)
diff --git a/nova/virt/hyperv/volumeutilsV2.py b/nova/virt/hyperv/volumeutilsV2.py
deleted file mode 100644
index 6f5bcdac9..000000000
--- a/nova/virt/hyperv/volumeutilsV2.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# 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 for Windows 2012
-"""
-import time
-
-from nova.openstack.common import cfg
-from nova.openstack.common import log as logging
-from nova.virt.hyperv import basevolumeutils
-
-LOG = logging.getLogger(__name__)
-CONF = cfg.CONF
-
-
-class VolumeUtilsV2(basevolumeutils.BaseVolumeUtils):
-
- def __init__(self, conn_storage, conn_wmi):
- self._conn_storage = conn_storage
- self._conn_wmi = conn_wmi
-
- 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
- portal = self._conn_storage.__getattr__("MSFT_iSCSITargetPortal")
- portal.New(TargetPortalAddress=target_address,
- TargetPortalPortNumber=target_port)
- #Connecting to the target
- target = self._conn_storage.__getattr__("MSFT_iSCSITarget")
- target.Connect(NodeAddress=target_iqn,
- IsPersistent=True)
- #Waiting the disk to be mounted. Research this
- time.sleep(CONF.hyperv_wait_between_attach_retry)
-
- def logout_storage_target(self, target_iqn):
- """Logs out storage target through its session id."""
-
- target = self._conn_storage.MSFT_iSCSITarget(
- NodeAddress=target_iqn)[0]
- if target.IsConnected:
- session = self._conn_storage.MSFT_iSCSISession(
- TargetNodeAddress=target_iqn)[0]
- if session.IsPersistent:
- session.Unregister()
- target.Disconnect()
-
- def execute_log_out(self, session_id):
- session = self._conn_wmi.MSiSCSIInitiator_SessionClass(
- SessionId=session_id)[0]
- self.logout_storage_target(session.TargetName)
diff --git a/nova/virt/hyperv/volumeutilsv2.py b/nova/virt/hyperv/volumeutilsv2.py
new file mode 100644
index 000000000..8322d31d3
--- /dev/null
+++ b/nova/virt/hyperv/volumeutilsv2.py
@@ -0,0 +1,75 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Pedro Navarro Perez
+# Copyright 2013 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.
+
+"""
+Helper methods for operations related to the management of volumes
+and storage repositories on Windows Server 2012 and above
+"""
+import sys
+import time
+
+if sys.platform == 'win32':
+ import wmi
+
+from nova.openstack.common import cfg
+from nova.openstack.common import log as logging
+from nova.virt.hyperv import basevolumeutils
+
+LOG = logging.getLogger(__name__)
+CONF = cfg.CONF
+
+
+class VolumeUtilsV2(basevolumeutils.BaseVolumeUtils):
+ def __init__(self):
+ super(VolumeUtilsV2, self).__init__()
+
+ storage_namespace = '//./root/microsoft/windows/storage'
+ if sys.platform == 'win32':
+ self._conn_storage = wmi.WMI(moniker=storage_namespace)
+
+ 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
+ portal = self._conn_storage.MSFT_iSCSITargetPortal
+ portal.New(TargetPortalAddress=target_address,
+ TargetPortalPortNumber=target_port)
+ #Connecting to the target
+ target = self._conn_storage.MSFT_iSCSITarget
+ target.Connect(NodeAddress=target_iqn,
+ IsPersistent=True)
+ #Waiting the disk to be mounted. Research this
+ time.sleep(CONF.hyperv_wait_between_attach_retry)
+
+ def logout_storage_target(self, target_iqn):
+ """Logs out storage target through its session id."""
+
+ target = self._conn_storage.MSFT_iSCSITarget(NodeAddress=target_iqn)[0]
+ if target.IsConnected:
+ session = self._conn_storage.MSFT_iSCSISession(
+ TargetNodeAddress=target_iqn)[0]
+ if session.IsPersistent:
+ session.Unregister()
+ target.Disconnect()
+
+ def execute_log_out(self, session_id):
+ session = self._conn_wmi.MSiSCSIInitiator_SessionClass(
+ SessionId=session_id)[0]
+ self.logout_storage_target(session.TargetName)
diff --git a/nova/virt/libvirt/__init__.py b/nova/virt/libvirt/__init__.py
index 00f4fd6b0..535d6c729 100644
--- a/nova/virt/libvirt/__init__.py
+++ b/nova/virt/libvirt/__init__.py
@@ -14,4 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from nova.virt.libvirt.driver import LibvirtDriver
+from nova.virt.libvirt import driver
+
+LibvirtDriver = driver.LibvirtDriver
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
index e4da5cbde..9ed7a054c 100644
--- a/nova/virt/libvirt/driver.py
+++ b/nova/virt/libvirt/driver.py
@@ -47,6 +47,7 @@ import os
import shutil
import sys
import tempfile
+import time
import uuid
from eventlet import greenthread
@@ -140,7 +141,7 @@ libvirt_opts = [
'raw, qcow2, vmdk, vdi). '
'Defaults to same as source image'),
cfg.StrOpt('libvirt_vif_driver',
- default='nova.virt.libvirt.vif.LibvirtBridgeDriver',
+ default='nova.virt.libvirt.vif.LibvirtGenericVIFDriver',
help='The libvirt VIF driver to configure the VIFs.'),
cfg.ListOpt('libvirt_volume_drivers',
default=[
@@ -254,6 +255,10 @@ MIN_LIBVIRT_VERSION = (0, 9, 6)
# When the above version matches/exceeds this version
# delete it & corresponding code using it
MIN_LIBVIRT_HOST_CPU_VERSION = (0, 9, 10)
+# Live snapshot requirements
+REQ_HYPERVISOR_LIVESNAPSHOT = "QEMU"
+MIN_LIBVIRT_LIVESNAPSHOT_VERSION = (1, 0, 0)
+MIN_QEMU_LIVESNAPSHOT_VERSION = (1, 3, 0)
def _get_eph_disk(ephemeral):
@@ -325,16 +330,29 @@ class LibvirtDriver(driver.ComputeDriver):
self._host_state = HostState(self.virtapi, self.read_only)
return self._host_state
- def has_min_version(self, ver):
- libvirt_version = self._conn.getLibVersion()
-
+ def has_min_version(self, lv_ver=None, hv_ver=None, hv_type=None):
def _munge_version(ver):
return ver[0] * 1000000 + ver[1] * 1000 + ver[2]
- if libvirt_version < _munge_version(ver):
- return False
+ try:
+ if lv_ver is not None:
+ libvirt_version = self._conn.getLibVersion()
+ if libvirt_version < _munge_version(lv_ver):
+ return False
- return True
+ if hv_ver is not None:
+ hypervisor_version = self._conn.getVersion()
+ if hypervisor_version < _munge_version(hv_ver):
+ return False
+
+ if hv_type is not None:
+ hypervisor_type = self._conn.getType()
+ if hypervisor_type != hv_type:
+ return False
+
+ return True
+ except Exception:
+ return False
def init_host(self, host):
if not self.has_min_version(MIN_LIBVIRT_VERSION):
@@ -806,35 +824,64 @@ class LibvirtDriver(driver.ComputeDriver):
(state, _max_mem, _mem, _cpus, _t) = virt_dom.info()
state = LIBVIRT_POWER_STATE[state]
+ # NOTE(rmk): Live snapshots require QEMU 1.3 and Libvirt 1.0.0.
+ # These restrictions can be relaxed as other configurations
+ # can be validated.
+ if self.has_min_version(MIN_LIBVIRT_LIVESNAPSHOT_VERSION,
+ MIN_QEMU_LIVESNAPSHOT_VERSION,
+ REQ_HYPERVISOR_LIVESNAPSHOT):
+ live_snapshot = True
+ else:
+ live_snapshot = False
+
+ # NOTE(rmk): We cannot perform live snapshots when a managedSave
+ # file is present, so we will use the cold/legacy method
+ # for instances which are shutdown.
+ if state == power_state.SHUTDOWN:
+ live_snapshot = False
+
# NOTE(dkang): managedSave does not work for LXC
- if CONF.libvirt_type != 'lxc':
+ if CONF.libvirt_type != 'lxc' and not live_snapshot:
if state == power_state.RUNNING or state == power_state.PAUSED:
virt_dom.managedSave(0)
- # Make the snapshot
- snapshot = self.image_backend.snapshot(disk_path, snapshot_name,
- image_type=source_format)
+ if live_snapshot:
+ LOG.info(_("Beginning live snapshot process"),
+ instance=instance)
+ else:
+ LOG.info(_("Beginning cold snapshot process"),
+ instance=instance)
+ snapshot = self.image_backend.snapshot(disk_path, snapshot_name,
+ image_type=source_format)
+ snapshot.create()
- snapshot.create()
update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
-
- # Export the snapshot to a raw image
snapshot_directory = CONF.libvirt_snapshots_directory
fileutils.ensure_tree(snapshot_directory)
with utils.tempdir(dir=snapshot_directory) as tmpdir:
try:
out_path = os.path.join(tmpdir, snapshot_name)
- snapshot.extract(out_path, image_format)
+ if live_snapshot:
+ # NOTE (rmk): libvirt needs to be able to write to the
+ # temp directory, which is owned nova.
+ utils.execute('chmod', '777', tmpdir, run_as_root=True)
+ self._live_snapshot(virt_dom, disk_path, out_path,
+ image_format)
+ else:
+ snapshot.extract(out_path, image_format)
finally:
- snapshot.delete()
+ if not live_snapshot:
+ snapshot.delete()
# NOTE(dkang): because previous managedSave is not called
# for LXC, _create_domain must not be called.
- if CONF.libvirt_type != 'lxc':
+ if CONF.libvirt_type != 'lxc' and not live_snapshot:
if state == power_state.RUNNING:
self._create_domain(domain=virt_dom)
elif state == power_state.PAUSED:
self._create_domain(domain=virt_dom,
launch_flags=libvirt.VIR_DOMAIN_START_PAUSED)
+ LOG.info(_("Snapshot extracted, beginning image upload"),
+ instance=instance)
# Upload that image to the image service
@@ -845,6 +892,72 @@ class LibvirtDriver(driver.ComputeDriver):
image_href,
metadata,
image_file)
+ LOG.info(_("Snapshot image upload complete"),
+ instance=instance)
+
+ def _live_snapshot(self, domain, disk_path, out_path, image_format):
+ """Snapshot an instance without downtime."""
+ # Save a copy of the domain's running XML file
+ xml = domain.XMLDesc(0)
+
+ # Abort is an idempotent operation, so make sure any block
+ # jobs which may have failed are ended.
+ try:
+ domain.blockJobAbort(disk_path, 0)
+ except Exception:
+ pass
+
+ def _wait_for_block_job(domain, disk_path):
+ status = domain.blockJobInfo(disk_path, 0)
+ try:
+ cur = status.get('cur', 0)
+ end = status.get('end', 0)
+ except Exception:
+ return False
+
+ if cur == end and cur != 0 and end != 0:
+ return False
+ else:
+ return True
+
+ # NOTE (rmk): We are using shallow rebases as a workaround to a bug
+ # in QEMU 1.3. In order to do this, we need to create
+ # a destination image with the original backing file
+ # and matching size of the instance root disk.
+ src_disk_size = libvirt_utils.get_disk_size(disk_path)
+ src_back_path = libvirt_utils.get_disk_backing_file(disk_path,
+ basename=False)
+ disk_delta = out_path + '.delta'
+ libvirt_utils.create_cow_image(src_back_path, disk_delta,
+ src_disk_size)
+
+ try:
+ # NOTE (rmk): blockRebase cannot be executed on persistent
+ # domains, so we need to temporarily undefine it.
+ # If any part of this block fails, the domain is
+ # re-defined regardless.
+ if domain.isPersistent():
+ domain.undefine()
+
+ # NOTE (rmk): Establish a temporary mirror of our root disk and
+ # issue an abort once we have a complete copy.
+ domain.blockRebase(disk_path, disk_delta, 0,
+ libvirt.VIR_DOMAIN_BLOCK_REBASE_COPY |
+ libvirt.VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
+ libvirt.VIR_DOMAIN_BLOCK_REBASE_SHALLOW)
+
+ while _wait_for_block_job(domain, disk_path):
+ time.sleep(0.5)
+
+ domain.blockJobAbort(disk_path, 0)
+ libvirt_utils.chown(disk_delta, os.getuid())
+ finally:
+ self._conn.defineXML(xml)
+
+ # Convert the delta (CoW) image with a backing file to a flat
+ # image with no backing file.
+ libvirt_utils.extract_snapshot(disk_delta, 'qcow2', None,
+ out_path, image_format)
def reboot(self, instance, network_info, reboot_type='SOFT',
block_device_info=None):
@@ -874,6 +987,7 @@ class LibvirtDriver(driver.ComputeDriver):
dom = self._lookup_by_name(instance["name"])
(state, _max_mem, _mem, _cpus, _t) = dom.info()
state = LIBVIRT_POWER_STATE[state]
+ old_domid = dom.ID()
# NOTE(vish): This check allows us to reboot an instance that
# is already shutdown.
if state == power_state.RUNNING:
@@ -882,8 +996,10 @@ class LibvirtDriver(driver.ComputeDriver):
# FLAG defines depending on how long the get_info
# call takes to return.
for x in xrange(CONF.libvirt_wait_soft_reboot_seconds):
+ dom = self._lookup_by_name(instance["name"])
(state, _max_mem, _mem, _cpus, _t) = dom.info()
state = LIBVIRT_POWER_STATE[state]
+ new_domid = dom.ID()
if state in [power_state.SHUTDOWN,
power_state.CRASHED]:
@@ -894,6 +1010,10 @@ class LibvirtDriver(driver.ComputeDriver):
instance)
timer.start(interval=0.5).wait()
return True
+ elif old_domid != new_domid:
+ LOG.info(_("Instance may have been rebooted during soft "
+ "reboot, so return now."), instance=instance)
+ return True
greenthread.sleep(1)
return False
@@ -966,11 +1086,6 @@ class LibvirtDriver(driver.ComputeDriver):
def resume_state_on_host_boot(self, context, instance, network_info,
block_device_info=None):
"""resume guest state when a host is booted."""
- xml = self._get_existing_domain_xml(instance, network_info,
- block_device_info)
- self._create_domain_and_network(xml, instance, network_info,
- block_device_info)
-
# Check if the instance is running already and avoid doing
# anything if it is.
if self.instance_exists(instance['name']):
@@ -1423,22 +1538,21 @@ class LibvirtDriver(driver.ComputeDriver):
injection_path = image('disk').path
img_id = instance['image_ref']
- for injection in ('metadata', 'key', 'net', 'admin_pass',
- 'files'):
- if locals()[injection]:
- LOG.info(_('Injecting %(injection)s into image'
+ for inject in ('key', 'net', 'metadata', 'admin_pass', 'files'):
+ if locals()[inject]:
+ LOG.info(_('Injecting %(inject)s into image'
' %(img_id)s'), locals(), instance=instance)
try:
disk.inject_data(injection_path,
key, net, metadata, admin_pass, files,
partition=target_partition,
- use_cow=CONF.use_cow_images)
-
+ use_cow=CONF.use_cow_images,
+ mandatory=('files',))
except Exception as e:
- # This could be a windows image, or a vmdk format disk
- LOG.warn(_('Ignoring error injecting data into image '
- '%(img_id)s (%(e)s)') % locals(),
- instance=instance)
+ LOG.error(_('Error injecting data into image '
+ '%(img_id)s (%(e)s)') % locals(),
+ instance=instance)
+ raise
if CONF.libvirt_type == 'uml':
libvirt_utils.chown(image('disk').path, 'root')
@@ -2831,7 +2945,15 @@ class LibvirtDriver(driver.ComputeDriver):
disk_info = []
virt_dom = self._lookup_by_name(instance_name)
- xml = virt_dom.XMLDesc(0)
+ try:
+ xml = virt_dom.XMLDesc(0)
+ except libvirt.libvirtError as ex:
+ error_code = ex.get_error_code()
+ msg = _("Error from libvirt while getting description of "
+ "%(instance_name)s: [Error Code %(error_code)s] "
+ "%(ex)s") % locals()
+ LOG.warn(msg)
+ raise exception.InstanceNotFound(instance_id=instance_name)
doc = etree.fromstring(xml)
disk_nodes = doc.findall('.//devices/disk')
path_nodes = doc.findall('.//devices/disk/source')
diff --git a/nova/virt/libvirt/firewall.py b/nova/virt/libvirt/firewall.py
index 3323b8f1d..5b712cf42 100644
--- a/nova/virt/libvirt/firewall.py
+++ b/nova/virt/libvirt/firewall.py
@@ -29,11 +29,7 @@ LOG = logging.getLogger(__name__)
CONF = cfg.CONF
CONF.import_opt('use_ipv6', 'nova.netconf')
-try:
- import libvirt
-except ImportError:
- LOG.warn(_("Libvirt module could not be loaded. NWFilterFirewall will "
- "not work correctly."))
+libvirt = None
class NWFilterFirewall(base_firewall.FirewallDriver):
@@ -47,6 +43,13 @@ class NWFilterFirewall(base_firewall.FirewallDriver):
def __init__(self, virtapi, get_connection, **kwargs):
super(NWFilterFirewall, self).__init__(virtapi)
+ global libvirt
+ if libvirt is None:
+ try:
+ libvirt = __import__('libvirt')
+ except ImportError:
+ LOG.warn(_("Libvirt module could not be loaded. "
+ "NWFilterFirewall will not work correctly."))
self._libvirt_get_connection = get_connection
self.static_filters_configured = False
self.handle_security_groups = False
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
index 4b3517da7..88110055c 100644
--- a/nova/virt/libvirt/utils.py
+++ b/nova/virt/libvirt/utils.py
@@ -63,7 +63,7 @@ def create_image(disk_format, path, size):
execute('qemu-img', 'create', '-f', disk_format, path, size)
-def create_cow_image(backing_file, path):
+def create_cow_image(backing_file, path, size=None):
"""Create COW image
Creates a COW image with the given backing file
@@ -89,6 +89,8 @@ def create_cow_image(backing_file, path):
# cow_opts += ['preallocation=%s' % base_details['preallocation']]
if base_details and base_details.encryption:
cow_opts += ['encryption=%s' % base_details.encryption]
+ if size is not None:
+ cow_opts += ['size=%s' % size]
if cow_opts:
# Format as a comma separated list
csv_opts = ",".join(cow_opts)
@@ -273,7 +275,7 @@ def pick_disk_driver_name(is_block_dev=False):
if is_block_dev:
return "phy"
else:
- return "file"
+ return "tap"
elif CONF.libvirt_type in ('kvm', 'qemu'):
return "qemu"
else:
@@ -292,14 +294,14 @@ def get_disk_size(path):
return int(size)
-def get_disk_backing_file(path):
+def get_disk_backing_file(path, basename=True):
"""Get the backing file of a disk image
:param path: Path to the disk image
:returns: a path to the image's backing store
"""
backing_file = images.qemu_img_info(path).backing_file
- if backing_file:
+ if backing_file and basename:
backing_file = os.path.basename(backing_file)
return backing_file
@@ -403,16 +405,16 @@ def extract_snapshot(disk_path, source_fmt, snapshot_name, out_path, dest_fmt):
# NOTE(markmc): ISO is just raw to qemu-img
if dest_fmt == 'iso':
dest_fmt = 'raw'
- qemu_img_cmd = ('qemu-img',
- 'convert',
- '-f',
- source_fmt,
- '-O',
- dest_fmt,
- '-s',
- snapshot_name,
- disk_path,
- out_path)
+
+ qemu_img_cmd = ('qemu-img', 'convert', '-f', source_fmt, '-O',
+ dest_fmt, '-s', snapshot_name, disk_path, out_path)
+
+ # When snapshot name is omitted we do a basic convert, which
+ # is used by live snapshots.
+ if snapshot_name is None:
+ qemu_img_cmd = ('qemu-img', 'convert', '-f', source_fmt, '-O',
+ dest_fmt, disk_path, out_path)
+
execute(*qemu_img_cmd)
diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py
index 83d43a6db..45c299830 100644
--- a/nova/virt/libvirt/vif.py
+++ b/nova/virt/libvirt/vif.py
@@ -28,7 +28,7 @@ from nova import utils
from nova.virt.libvirt import config as vconfig
from nova.virt.libvirt import designer
-from nova.virt import netutils
+
LOG = logging.getLogger(__name__)
libvirt_vif_opts = [
@@ -72,19 +72,22 @@ class LibvirtBaseVIFDriver(object):
return conf
+ def plug(self, instance, vif):
+ pass
+
+ def unplug(self, instance, vif):
+ pass
+
-class LibvirtBridgeDriver(LibvirtBaseVIFDriver):
- """VIF driver for Linux bridge."""
+class LibvirtGenericVIFDriver(LibvirtBaseVIFDriver):
+ """Generic VIF driver for libvirt networking."""
def get_bridge_name(self, network):
return network['bridge']
- def get_config(self, instance, network, mapping):
+ def get_config_bridge(self, instance, network, mapping):
"""Get VIF configurations for bridge type."""
-
- mac_id = mapping['mac'].replace(':', '')
-
- conf = super(LibvirtBridgeDriver,
+ conf = super(LibvirtGenericVIFDriver,
self).get_config(instance,
network,
mapping)
@@ -93,6 +96,7 @@ class LibvirtBridgeDriver(LibvirtBaseVIFDriver):
conf, self.get_bridge_name(network),
self.get_vif_devname(mapping))
+ mac_id = mapping['mac'].replace(':', '')
name = "nova-instance-" + instance['name'] + "-" + mac_id
primary_addr = mapping['ips'][0]['ip']
dhcp_server = ra_server = ipv4_cidr = ipv6_cidr = None
@@ -112,8 +116,29 @@ class LibvirtBridgeDriver(LibvirtBaseVIFDriver):
return conf
- def plug(self, instance, vif):
+ def get_config(self, instance, network, mapping):
+ vif_type = mapping.get('vif_type')
+
+ LOG.debug(_("vif_type=%(vif_type)s instance=%(instance)s "
+ "network=%(network)s mapping=%(mapping)s")
+ % locals())
+
+ if vif_type is None:
+ raise exception.NovaException(
+ _("vif_type parameter must be present "
+ "for this vif_driver implementation"))
+
+ if vif_type == network_model.VIF_TYPE_BRIDGE:
+ return self.get_config_bridge(instance, network, mapping)
+ else:
+ raise exception.NovaException(
+ _("Unexpected vif_type=%s") % vif_type)
+
+ def plug_bridge(self, instance, vif):
"""Ensure that the bridge exists, and add VIF to it."""
+ super(LibvirtGenericVIFDriver,
+ self).plug(instance, vif)
+
network, mapping = vif
if (not network.get('multi_host') and
mapping.get('should_create_bridge')):
@@ -135,9 +160,71 @@ class LibvirtBridgeDriver(LibvirtBaseVIFDriver):
self.get_bridge_name(network),
iface)
- def unplug(self, instance, vif):
+ def plug(self, instance, vif):
+ network, mapping = vif
+ vif_type = mapping.get('vif_type')
+
+ LOG.debug(_("vif_type=%(vif_type)s instance=%(instance)s "
+ "network=%(network)s mapping=%(mapping)s")
+ % locals())
+
+ if vif_type is None:
+ raise exception.NovaException(
+ _("vif_type parameter must be present "
+ "for this vif_driver implementation"))
+
+ if vif_type == network_model.VIF_TYPE_BRIDGE:
+ self.plug_bridge(instance, vif)
+ else:
+ raise exception.NovaException(
+ _("Unexpected vif_type=%s") % vif_type)
+
+ def unplug_bridge(self, instance, vif):
"""No manual unplugging required."""
- pass
+ super(LibvirtGenericVIFDriver,
+ self).unplug(instance, vif)
+
+ def unplug(self, instance, vif):
+ network, mapping = vif
+ vif_type = mapping.get('vif_type')
+
+ LOG.debug(_("vif_type=%(vif_type)s instance=%(instance)s "
+ "network=%(network)s mapping=%(mapping)s")
+ % locals())
+
+ if vif_type is None:
+ raise exception.NovaException(
+ _("vif_type parameter must be present "
+ "for this vif_driver implementation"))
+
+ if vif_type == network_model.VIF_TYPE_BRIDGE:
+ self.unplug_bridge(instance, vif)
+ else:
+ raise exception.NovaException(
+ _("Unexpected vif_type=%s") % vif_type)
+
+
+class LibvirtBridgeDriver(LibvirtGenericVIFDriver):
+ """Deprecated in favour of LibvirtGenericVIFDriver.
+ Retained in Grizzly for compatibility with Quantum
+ drivers which do not yet report 'vif_type' port binding.
+ To be removed in Hxxxx."""
+
+ def __init__(self):
+ LOG.deprecated(
+ _("LibvirtBridgeDriver is deprecated and "
+ "will be removed in the Hxxxx release. Please "
+ "update the 'libvirt_vif_driver' config parameter "
+ "to use the LibvirtGenericVIFDriver class instead"))
+
+ def get_config(self, instance, network, mapping):
+ return self.get_config_bridge(instance, network, mapping)
+
+ def plug(self, instance, vif):
+ self.plug_bridge(instance, vif)
+
+ def unplug(self, instance, vif):
+ self.unplug_bridge(instance, vif)
class LibvirtOpenVswitchDriver(LibvirtBaseVIFDriver):
@@ -150,6 +237,9 @@ class LibvirtOpenVswitchDriver(LibvirtBaseVIFDriver):
def get_bridge_name(self, network):
return network.get('bridge') or CONF.libvirt_ovs_bridge
+ def get_ovs_interfaceid(self, mapping):
+ return mapping.get('ovs_interfaceid') or mapping['vif_uuid']
+
def get_config(self, instance, network, mapping):
dev = self.get_vif_devname(mapping)
@@ -162,55 +252,26 @@ class LibvirtOpenVswitchDriver(LibvirtBaseVIFDriver):
return conf
- def create_ovs_vif_port(self, bridge, dev, iface_id, mac, instance_id):
- utils.execute('ovs-vsctl', '--', '--may-exist', 'add-port',
- bridge, dev,
- '--', 'set', 'Interface', dev,
- 'external-ids:iface-id=%s' % iface_id,
- 'external-ids:iface-status=active',
- 'external-ids:attached-mac=%s' % mac,
- 'external-ids:vm-uuid=%s' % instance_id,
- run_as_root=True)
-
- def delete_ovs_vif_port(self, bridge, dev):
- utils.execute('ovs-vsctl', 'del-port', bridge, dev,
- run_as_root=True)
- utils.execute('ip', 'link', 'delete', dev, run_as_root=True)
-
def plug(self, instance, vif):
network, mapping = vif
- iface_id = mapping['vif_uuid']
+ iface_id = self.get_ovs_interfaceid(mapping)
dev = self.get_vif_devname(mapping)
- if not linux_net.device_exists(dev):
- # Older version of the command 'ip' from the iproute2 package
- # don't have support for the tuntap option (lp:882568). If it
- # turns out we're on an old version we work around this by using
- # tunctl.
- try:
- # First, try with 'ip'
- utils.execute('ip', 'tuntap', 'add', dev, 'mode', 'tap',
- run_as_root=True)
- except exception.ProcessExecutionError:
- # Second option: tunctl
- utils.execute('tunctl', '-b', '-t', dev, run_as_root=True)
- utils.execute('ip', 'link', 'set', dev, 'up', run_as_root=True)
-
- self.create_ovs_vif_port(self.get_bridge_name(network),
- dev, iface_id, mapping['mac'],
- instance['uuid'])
+ linux_net.create_tap_dev(dev)
+ linux_net.create_ovs_vif_port(self.get_bridge_name(network),
+ dev, iface_id, mapping['mac'],
+ instance['uuid'])
def unplug(self, instance, vif):
"""Unplug the VIF by deleting the port from the bridge."""
try:
network, mapping = vif
- self.delete_ovs_vif_port(self.get_bridge_name(network),
- self.get_vif_devname(mapping))
+ linux_net.delete_ovs_vif_port(self.get_bridge_name(network),
+ self.get_vif_devname(mapping))
except exception.ProcessExecutionError:
LOG.exception(_("Failed while unplugging vif"), instance=instance)
-class LibvirtHybridOVSBridgeDriver(LibvirtBridgeDriver,
- LibvirtOpenVswitchDriver):
+class LibvirtHybridOVSBridgeDriver(LibvirtBridgeDriver):
"""VIF driver that uses OVS + Linux Bridge for iptables compatibility.
Enables the use of OVS-based Quantum plugins while at the same
@@ -229,6 +290,9 @@ class LibvirtHybridOVSBridgeDriver(LibvirtBridgeDriver,
def get_bridge_name(self, network):
return network.get('bridge') or CONF.libvirt_ovs_bridge
+ def get_ovs_interfaceid(self, mapping):
+ return mapping.get('ovs_interfaceid') or mapping['vif_uuid']
+
def get_config(self, instance, network, mapping):
br_name = self.get_br_name(mapping['vif_uuid'])
network['bridge'] = br_name
@@ -247,9 +311,9 @@ class LibvirtHybridOVSBridgeDriver(LibvirtBridgeDriver,
"""
network, mapping = vif
- iface_id = mapping['vif_uuid']
- br_name = self.get_br_name(iface_id)
- v1_name, v2_name = self.get_veth_pair_names(iface_id)
+ iface_id = self.get_ovs_interfaceid(mapping)
+ br_name = self.get_br_name(mapping['vif_uuid'])
+ v1_name, v2_name = self.get_veth_pair_names(mapping['vif_uuid'])
if not linux_net.device_exists(br_name):
utils.execute('brctl', 'addbr', br_name, run_as_root=True)
@@ -258,9 +322,9 @@ class LibvirtHybridOVSBridgeDriver(LibvirtBridgeDriver,
linux_net._create_veth_pair(v1_name, v2_name)
utils.execute('ip', 'link', 'set', br_name, 'up', run_as_root=True)
utils.execute('brctl', 'addif', br_name, v1_name, run_as_root=True)
- self.create_ovs_vif_port(self.get_bridge_name(network),
- v2_name, iface_id, mapping['mac'],
- instance['uuid'])
+ linux_net.create_ovs_vif_port(self.get_bridge_name(network),
+ v2_name, iface_id, mapping['mac'],
+ instance['uuid'])
def unplug(self, instance, vif):
"""UnPlug using hybrid strategy
@@ -270,16 +334,16 @@ class LibvirtHybridOVSBridgeDriver(LibvirtBridgeDriver,
"""
try:
network, mapping = vif
- iface_id = mapping['vif_uuid']
- br_name = self.get_br_name(iface_id)
- v1_name, v2_name = self.get_veth_pair_names(iface_id)
+ br_name = self.get_br_name(mapping['vif_uuid'])
+ v1_name, v2_name = self.get_veth_pair_names(mapping['vif_uuid'])
utils.execute('brctl', 'delif', br_name, v1_name, run_as_root=True)
utils.execute('ip', 'link', 'set', br_name, 'down',
run_as_root=True)
utils.execute('brctl', 'delbr', br_name, run_as_root=True)
- self.delete_ovs_vif_port(self.get_bridge_name(network), v2_name)
+ linux_net.delete_ovs_vif_port(self.get_bridge_name(network),
+ v2_name)
except exception.ProcessExecutionError:
LOG.exception(_("Failed while unplugging vif"), instance=instance)
@@ -291,6 +355,9 @@ class LibvirtOpenVswitchVirtualPortDriver(LibvirtBaseVIFDriver):
def get_bridge_name(self, network):
return network.get('bridge') or CONF.libvirt_ovs_bridge
+ def get_ovs_interfaceid(self, mapping):
+ return mapping.get('ovs_interfaceid') or mapping['vif_uuid']
+
def get_config(self, instance, network, mapping):
"""Pass data required to create OVS virtual port element."""
conf = super(LibvirtOpenVswitchVirtualPortDriver,
@@ -299,7 +366,8 @@ class LibvirtOpenVswitchVirtualPortDriver(LibvirtBaseVIFDriver):
mapping)
designer.set_vif_host_backend_ovs_config(
- conf, self.get_bridge_name(network), mapping['vif_uuid'],
+ conf, self.get_bridge_name(network),
+ self.get_ovs_interfaceid(mapping),
self.get_vif_devname(mapping))
return conf
diff --git a/nova/virt/libvirt/volume.py b/nova/virt/libvirt/volume.py
index f9a948fb5..d02db22f3 100644
--- a/nova/virt/libvirt/volume.py
+++ b/nova/virt/libvirt/volume.py
@@ -47,19 +47,19 @@ CONF = cfg.CONF
CONF.register_opts(volume_opts)
-class LibvirtVolumeDriver(object):
+class LibvirtBaseVolumeDriver(object):
"""Base class for volume drivers."""
- def __init__(self, connection):
+ def __init__(self, connection, is_block_dev):
self.connection = connection
+ self.is_block_dev = is_block_dev
def connect_volume(self, connection_info, mount_device):
"""Connect the volume. Returns xml for libvirt."""
+
conf = vconfig.LibvirtConfigGuestDisk()
- conf.source_type = "block"
- conf.driver_name = virtutils.pick_disk_driver_name(is_block_dev=True)
+ conf.driver_name = virtutils.pick_disk_driver_name(self.is_block_dev)
conf.driver_format = "raw"
conf.driver_cache = "none"
- conf.source_path = connection_info['data']['device_path']
conf.target_dev = mount_device
conf.target_bus = "virtio"
conf.serial = connection_info.get('serial')
@@ -70,37 +70,49 @@ class LibvirtVolumeDriver(object):
pass
-class LibvirtFakeVolumeDriver(LibvirtVolumeDriver):
- """Driver to attach Network volumes to libvirt."""
+class LibvirtVolumeDriver(LibvirtBaseVolumeDriver):
+ """Class for volumes backed by local file."""
+ def __init__(self, connection):
+ super(LibvirtVolumeDriver,
+ self).__init__(connection, is_block_dev=True)
def connect_volume(self, connection_info, mount_device):
- conf = vconfig.LibvirtConfigGuestDisk()
+ """Connect the volume to a local device."""
+ conf = super(LibvirtVolumeDriver,
+ self).connect_volume(connection_info, mount_device)
+ conf.source_type = "block"
+ conf.source_path = connection_info['data']['device_path']
+ return conf
+
+
+class LibvirtFakeVolumeDriver(LibvirtBaseVolumeDriver):
+ """Driver to attach fake volumes to libvirt."""
+ def __init__(self, connection):
+ super(LibvirtFakeVolumeDriver,
+ self).__init__(connection, is_block_dev=True)
+
+ def connect_volume(self, connection_info, mount_device):
+ """Connect the volume to a fake device."""
+ conf = super(LibvirtFakeVolumeDriver,
+ self).connect_volume(connection_info, mount_device)
conf.source_type = "network"
- conf.driver_name = "qemu"
- conf.driver_format = "raw"
- conf.driver_cache = "none"
conf.source_protocol = "fake"
conf.source_host = "fake"
- conf.target_dev = mount_device
- conf.target_bus = "virtio"
- conf.serial = connection_info.get('serial')
return conf
-class LibvirtNetVolumeDriver(LibvirtVolumeDriver):
+class LibvirtNetVolumeDriver(LibvirtBaseVolumeDriver):
"""Driver to attach Network volumes to libvirt."""
+ def __init__(self, connection):
+ super(LibvirtNetVolumeDriver,
+ self).__init__(connection, is_block_dev=False)
def connect_volume(self, connection_info, mount_device):
- conf = vconfig.LibvirtConfigGuestDisk()
+ conf = super(LibvirtNetVolumeDriver,
+ self).connect_volume(connection_info, mount_device)
conf.source_type = "network"
- conf.driver_name = virtutils.pick_disk_driver_name(is_block_dev=False)
- conf.driver_format = "raw"
- conf.driver_cache = "none"
conf.source_protocol = connection_info['driver_volume_type']
conf.source_host = connection_info['data']['name']
- conf.target_dev = mount_device
- conf.target_bus = "virtio"
- conf.serial = connection_info.get('serial')
netdisk_properties = connection_info['data']
auth_enabled = netdisk_properties.get('auth_enabled')
if (conf.source_protocol == 'rbd' and
@@ -118,8 +130,11 @@ class LibvirtNetVolumeDriver(LibvirtVolumeDriver):
return conf
-class LibvirtISCSIVolumeDriver(LibvirtVolumeDriver):
+class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver):
"""Driver to attach Network volumes to libvirt."""
+ def __init__(self, connection):
+ super(LibvirtISCSIVolumeDriver,
+ self).__init__(connection, is_block_dev=False)
def _run_iscsiadm(self, iscsi_properties, iscsi_command, **kwargs):
check_exit_code = kwargs.pop('check_exit_code', 0)
@@ -141,6 +156,9 @@ class LibvirtISCSIVolumeDriver(LibvirtVolumeDriver):
@lockutils.synchronized('connect_volume', 'nova-')
def connect_volume(self, connection_info, mount_device):
"""Attach the volume to instance_name."""
+ conf = super(LibvirtISCSIVolumeDriver,
+ self).connect_volume(connection_info, mount_device)
+
iscsi_properties = connection_info['data']
# NOTE(vish): If we are on the same host as nova volume, the
# discovery makes the target so we don't need to
@@ -204,15 +222,15 @@ class LibvirtISCSIVolumeDriver(LibvirtVolumeDriver):
"(after %(tries)s rescans)") %
locals())
- connection_info['data']['device_path'] = host_device
- sup = super(LibvirtISCSIVolumeDriver, self)
- return sup.connect_volume(connection_info, mount_device)
+ conf.source_type = "block"
+ conf.source_path = host_device
+ return conf
@lockutils.synchronized('connect_volume', 'nova-')
def disconnect_volume(self, connection_info, mount_device):
"""Detach the volume from instance_name."""
- sup = super(LibvirtISCSIVolumeDriver, self)
- sup.disconnect_volume(connection_info, mount_device)
+ super(LibvirtISCSIVolumeDriver,
+ self).disconnect_volume(connection_info, mount_device)
iscsi_properties = connection_info['data']
# NOTE(vish): Only disconnect from the target if no luns from the
# target are in use.
diff --git a/nova/virt/libvirt/volume_nfs.py b/nova/virt/libvirt/volume_nfs.py
index b5083937d..70bb8c38f 100644
--- a/nova/virt/libvirt/volume_nfs.py
+++ b/nova/virt/libvirt/volume_nfs.py
@@ -38,27 +38,24 @@ CONF = cfg.CONF
CONF.register_opts(volume_opts)
-class NfsVolumeDriver(volume.LibvirtVolumeDriver):
+class NfsVolumeDriver(volume.LibvirtBaseVolumeDriver):
"""Class implements libvirt part of volume driver for NFS."""
- def __init__(self, *args, **kwargs):
- """Create back-end to nfs and check connection."""
- super(NfsVolumeDriver, self).__init__(*args, **kwargs)
+ def __init__(self, connection):
+ """Create back-end to nfs."""
+ super(NfsVolumeDriver,
+ self).__init__(connection, is_block_dev=False)
def connect_volume(self, connection_info, mount_device):
"""Connect the volume. Returns xml for libvirt."""
+ conf = super(NfsVolumeDriver,
+ self).connect_volume(connection_info, mount_device)
path = self._ensure_mounted(connection_info['data']['export'])
path = os.path.join(path, connection_info['data']['name'])
- connection_info['data']['device_path'] = path
- conf = super(NfsVolumeDriver, self).connect_volume(connection_info,
- mount_device)
conf.source_type = 'file'
+ conf.source_path = path
return conf
- def disconnect_volume(self, connection_info, mount_device):
- """Disconnect the volume."""
- pass
-
def _ensure_mounted(self, nfs_export):
"""
@type nfs_export: string
diff --git a/nova/virt/powervm/__init__.py b/nova/virt/powervm/__init__.py
index 83bbcd289..1b63f8310 100644
--- a/nova/virt/powervm/__init__.py
+++ b/nova/virt/powervm/__init__.py
@@ -26,4 +26,6 @@ refer to the IBM Redbook[1] publication.
May 2011. <http://www.redbooks.ibm.com/abstracts/sg247940.html>
"""
-from nova.virt.powervm.driver import PowerVMDriver
+from nova.virt.powervm import driver
+
+PowerVMDriver = driver.PowerVMDriver
diff --git a/nova/virt/powervm/blockdev.py b/nova/virt/powervm/blockdev.py
index fb3a0210c..76caca1b9 100644
--- a/nova/virt/powervm/blockdev.py
+++ b/nova/virt/powervm/blockdev.py
@@ -18,16 +18,11 @@ import hashlib
import os
import re
-from eventlet import greenthread
-
-from nova import utils
-
from nova.image import glance
-
from nova.openstack.common import cfg
from nova.openstack.common import excutils
from nova.openstack.common import log as logging
-
+from nova import utils
from nova.virt import images
from nova.virt.powervm import command
from nova.virt.powervm import common
diff --git a/nova/virt/powervm/driver.py b/nova/virt/powervm/driver.py
index ccba3cf73..0ce313535 100644
--- a/nova/virt/powervm/driver.py
+++ b/nova/virt/powervm/driver.py
@@ -14,19 +14,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-import os
import time
-from nova.compute import task_states
-from nova.compute import vm_states
-
-from nova import context as nova_context
-
from nova.image import glance
-
from nova.openstack.common import cfg
from nova.openstack.common import log as logging
-
from nova.virt import driver
from nova.virt.powervm import operator
diff --git a/nova/virt/powervm/operator.py b/nova/virt/powervm/operator.py
index 5a4a2938b..87da30a14 100644
--- a/nova/virt/powervm/operator.py
+++ b/nova/virt/powervm/operator.py
@@ -372,7 +372,7 @@ class BaseOperator(object):
"""
cmd = self.command.lssyscfg('-r %s --filter "lpar_names=%s"'
% (resource_type, instance_name))
- output = self.run_command(cmd)
+ output = self.run_vios_command(cmd)
if not output:
return None
lpar = LPAR.load_from_conf_data(output[0])
@@ -383,7 +383,8 @@ class BaseOperator(object):
:returns: list -- list with instances names.
"""
- lpar_names = self.run_command(self.command.lssyscfg('-r lpar -F name'))
+ lpar_names = self.run_vios_command(self.command.lssyscfg(
+ '-r lpar -F name'))
if not lpar_names:
return []
return lpar_names
@@ -394,14 +395,15 @@ class BaseOperator(object):
:param lpar: LPAR object
"""
conf_data = lpar.to_string()
- self.run_command(self.command.mksyscfg('-r lpar -i "%s"' % conf_data))
+ self.run_vios_command(self.command.mksyscfg('-r lpar -i "%s"' %
+ conf_data))
def start_lpar(self, instance_name):
"""Start a LPAR instance.
:param instance_name: LPAR instance name
"""
- self.run_command(self.command.chsysstate('-r lpar -o on -n %s'
+ self.run_vios_command(self.command.chsysstate('-r lpar -o on -n %s'
% instance_name))
def stop_lpar(self, instance_name, timeout=30):
@@ -413,7 +415,7 @@ class BaseOperator(object):
"""
cmd = self.command.chsysstate('-r lpar -o shutdown --immed -n %s' %
instance_name)
- self.run_command(cmd)
+ self.run_vios_command(cmd)
# poll instance until stopped or raise exception
lpar_obj = self.get_lpar(instance_name)
@@ -435,7 +437,7 @@ class BaseOperator(object):
:param instance_name: LPAR instance name
"""
- self.run_command(self.command.rmsyscfg('-r lpar -n %s'
+ self.run_vios_command(self.command.rmsyscfg('-r lpar -n %s'
% instance_name))
def get_vhost_by_instance_id(self, instance_id):
@@ -446,7 +448,7 @@ class BaseOperator(object):
"""
instance_hex_id = '%#010x' % int(instance_id)
cmd = self.command.lsmap('-all -field clientid svsa -fmt :')
- output = self.run_command(cmd)
+ output = self.run_vios_command(cmd)
vhosts = dict(item.split(':') for item in list(output))
if instance_hex_id in vhosts:
@@ -463,10 +465,10 @@ class BaseOperator(object):
:returns: id of the virtual ethernet adapter.
"""
cmd = self.command.lsmap('-all -net -field sea -fmt :')
- output = self.run_command(cmd)
+ output = self.run_vios_command(cmd)
sea = output[0]
cmd = self.command.lsdev('-dev %s -attr pvid' % sea)
- output = self.run_command(cmd)
+ output = self.run_vios_command(cmd)
# Returned output looks like this: ['value', '', '1']
if output:
return output[2]
@@ -478,7 +480,7 @@ class BaseOperator(object):
:returns: string -- hostname
"""
- output = self.run_command(self.command.hostname())
+ output = self.run_vios_command(self.command.hostname())
return output[0]
def get_disk_name_by_vhost(self, vhost):
@@ -488,7 +490,7 @@ class BaseOperator(object):
:returns: string -- disk name
"""
cmd = self.command.lsmap('-vadapter %s -field backing -fmt :' % vhost)
- output = self.run_command(cmd)
+ output = self.run_vios_command(cmd)
if output:
return output[0]
@@ -501,7 +503,7 @@ class BaseOperator(object):
:param vhost: the vhost name
"""
cmd = self.command.mkvdev('-vdev %s -vadapter %s') % (disk, vhost)
- self.run_command(cmd)
+ self.run_vios_command(cmd)
def get_memory_info(self):
"""Get memory info.
@@ -510,7 +512,7 @@ class BaseOperator(object):
"""
cmd = self.command.lshwres(
'-r mem --level sys -F configurable_sys_mem,curr_avail_sys_mem')
- output = self.run_command(cmd)
+ output = self.run_vios_command(cmd)
total_mem, avail_mem = output[0].split(',')
return {'total_mem': int(total_mem),
'avail_mem': int(avail_mem)}
@@ -523,7 +525,7 @@ class BaseOperator(object):
cmd = self.command.lshwres(
'-r proc --level sys -F '
'configurable_sys_proc_units,curr_avail_sys_proc_units')
- output = self.run_command(cmd)
+ output = self.run_vios_command(cmd)
total_procs, avail_procs = output[0].split(',')
return {'total_procs': float(total_procs),
'avail_procs': float(avail_procs)}
@@ -533,12 +535,12 @@ class BaseOperator(object):
:returns: tuple - disk info (disk_total, disk_used, disk_avail)
"""
- vgs = self.run_command(self.command.lsvg())
+ vgs = self.run_vios_command(self.command.lsvg())
(disk_total, disk_used, disk_avail) = [0, 0, 0]
for vg in vgs:
cmd = self.command.lsvg('%s -field totalpps usedpps freepps -fmt :'
% vg)
- output = self.run_command(cmd)
+ output = self.run_vios_command(cmd)
# Output example:
# 1271 (10168 megabytes):0 (0 megabytes):1271 (10168 megabytes)
(d_total, d_used, d_avail) = re.findall(r'(\d+) megabytes',
@@ -551,7 +553,7 @@ class BaseOperator(object):
'disk_used': disk_used,
'disk_avail': disk_avail}
- def run_command(self, cmd, check_exit_code=True):
+ def run_vios_command(self, cmd, check_exit_code=True):
"""Run a remote command using an active ssh connection.
:param command: String with the command to run.
@@ -561,7 +563,7 @@ class BaseOperator(object):
check_exit_code=check_exit_code)
return stdout.strip().splitlines()
- def run_command_as_root(self, command, check_exit_code=True):
+ def run_vios_command_as_root(self, command, check_exit_code=True):
"""Run a remote command as root using an active ssh connection.
:param command: List of commands.
diff --git a/nova/virt/vmwareapi/__init__.py b/nova/virt/vmwareapi/__init__.py
index 66e7d9b02..37d816f8c 100644
--- a/nova/virt/vmwareapi/__init__.py
+++ b/nova/virt/vmwareapi/__init__.py
@@ -18,4 +18,6 @@
:mod:`vmwareapi` -- Nova support for VMware ESX/ESXi Server through VMware API.
"""
# NOTE(sdague) for nicer compute_driver specification
-from nova.virt.vmwareapi.driver import VMwareESXDriver
+from nova.virt.vmwareapi import driver
+
+VMwareESXDriver = driver.VMwareESXDriver
diff --git a/nova/virt/vmwareapi/driver.py b/nova/virt/vmwareapi/driver.py
index 986c4ef28..67822f2c9 100644
--- a/nova/virt/vmwareapi/driver.py
+++ b/nova/virt/vmwareapi/driver.py
@@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
@@ -20,16 +21,19 @@ A connection to the VMware ESX platform.
**Related Flags**
-:vmwareapi_host_ip: IPAddress of VMware ESX server.
-:vmwareapi_host_username: Username for connection to VMware ESX Server.
-:vmwareapi_host_password: Password for connection to VMware ESX Server.
-:vmwareapi_task_poll_interval: The interval (seconds) used for polling of
- remote tasks
- (default: 1.0).
-:vmwareapi_api_retry_count: The API retry count in case of failure such as
- network failures (socket errors etc.)
- (default: 10).
-
+:vmwareapi_host_ip: IP address of VMware ESX server.
+:vmwareapi_host_username: Username for connection to VMware ESX Server.
+:vmwareapi_host_password: Password for connection to VMware ESX Server.
+:vmwareapi_task_poll_interval: The interval (seconds) used for polling of
+ remote tasks
+ (default: 5.0).
+:vmwareapi_api_retry_count: The API retry count in case of failure such as
+ network failures (socket errors etc.)
+ (default: 10).
+:vnc_port: VNC starting port (default: 5900)
+:vnc_port_total: Total number of VNC ports (default: 10000)
+:vnc_password: VNC password
+:use_linked_clone: Whether to use linked clone (default: True)
"""
import time
@@ -38,13 +42,16 @@ from eventlet import event
from nova import exception
from nova.openstack.common import cfg
+from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
from nova import utils
from nova.virt import driver
from nova.virt.vmwareapi import error_util
+from nova.virt.vmwareapi import host
from nova.virt.vmwareapi import vim
from nova.virt.vmwareapi import vim_util
from nova.virt.vmwareapi import vmops
+from nova.virt.vmwareapi import volumeops
LOG = logging.getLogger(__name__)
@@ -52,7 +59,7 @@ LOG = logging.getLogger(__name__)
vmwareapi_opts = [
cfg.StrOpt('vmwareapi_host_ip',
default=None,
- help='URL for connection to VMware ESX host.Required if '
+ help='URL for connection to VMware ESX host. Required if '
'compute_driver is vmwareapi.VMwareESXDriver.'),
cfg.StrOpt('vmwareapi_host_username',
default=None,
@@ -75,6 +82,18 @@ vmwareapi_opts = [
'socket error, etc. '
'Used only if compute_driver is '
'vmwareapi.VMwareESXDriver.'),
+ cfg.IntOpt('vnc_port',
+ default=5900,
+ help='VNC starting port'),
+ cfg.IntOpt('vnc_port_total',
+ default=10000,
+ help='Total number of VNC ports'),
+ cfg.StrOpt('vnc_password',
+ default=None,
+ help='VNC password'),
+ cfg.BoolOpt('use_linked_clone',
+ default=True,
+ help='Whether to use linked clone'),
]
CONF = cfg.CONF
@@ -99,19 +118,31 @@ class VMwareESXDriver(driver.ComputeDriver):
def __init__(self, virtapi, read_only=False, scheme="https"):
super(VMwareESXDriver, self).__init__(virtapi)
- host_ip = CONF.vmwareapi_host_ip
+ self._host_ip = CONF.vmwareapi_host_ip
host_username = CONF.vmwareapi_host_username
host_password = CONF.vmwareapi_host_password
api_retry_count = CONF.vmwareapi_api_retry_count
- if not host_ip or host_username is None or host_password is None:
+ if not self._host_ip or host_username is None or host_password is None:
raise Exception(_("Must specify vmwareapi_host_ip,"
"vmwareapi_host_username "
"and vmwareapi_host_password to use"
"compute_driver=vmwareapi.VMwareESXDriver"))
- session = VMwareAPISession(host_ip, host_username, host_password,
- api_retry_count, scheme=scheme)
- self._vmops = vmops.VMwareVMOps(session)
+ self._session = VMwareAPISession(self._host_ip,
+ host_username, host_password,
+ api_retry_count, scheme=scheme)
+ self._volumeops = volumeops.VMwareVolumeOps(self._session)
+ self._vmops = vmops.VMwareVMOps(self._session, self.virtapi,
+ self._volumeops)
+ self._host = host.Host(self._session)
+ self._host_state = None
+
+ @property
+ def host_state(self):
+ if not self._host_state:
+ self._host_state = host.HostState(self._session,
+ self._host_ip)
+ return self._host_state
def init_host(self, host):
"""Do the initialization that needs to be done."""
@@ -128,7 +159,8 @@ class VMwareESXDriver(driver.ComputeDriver):
def spawn(self, context, instance, image_meta, injected_files,
admin_password, network_info=None, block_device_info=None):
"""Create VM instance."""
- self._vmops.spawn(context, instance, image_meta, network_info)
+ self._vmops.spawn(context, instance, image_meta, network_info,
+ block_device_info)
def snapshot(self, context, instance, name, update_task_state):
"""Create snapshot from a running VM instance."""
@@ -160,6 +192,61 @@ class VMwareESXDriver(driver.ComputeDriver):
"""Resume the suspended VM instance."""
self._vmops.resume(instance)
+ def rescue(self, context, instance, network_info, image_meta,
+ rescue_password):
+ """Rescue the specified instance."""
+ self._vmops.rescue(context, instance, network_info, image_meta)
+
+ def unrescue(self, instance, network_info):
+ """Unrescue the specified instance."""
+ self._vmops.unrescue(instance)
+
+ def power_off(self, instance):
+ """Power off the specified instance."""
+ self._vmops.power_off(instance)
+
+ def power_on(self, instance):
+ """Power on the specified instance."""
+ self._vmops.power_on(instance)
+
+ def migrate_disk_and_power_off(self, context, instance, dest,
+ instance_type, network_info,
+ block_device_info=None):
+ """
+ Transfers the disk of a running instance in multiple phases, turning
+ off the instance before the end.
+ """
+ return self._vmops.migrate_disk_and_power_off(context, instance,
+ dest, instance_type)
+
+ def confirm_migration(self, migration, instance, network_info):
+ """Confirms a resize, destroying the source VM."""
+ self._vmops.confirm_migration(migration, instance, network_info)
+
+ def finish_revert_migration(self, instance, network_info,
+ block_device_info=None):
+ """Finish reverting a resize, powering back on the instance."""
+ self._vmops.finish_revert_migration(instance)
+
+ def finish_migration(self, context, migration, instance, disk_info,
+ network_info, image_meta, resize_instance=False,
+ block_device_info=None):
+ """Completes a resize, turning on the migrated instance."""
+ self._vmops.finish_migration(context, migration, instance, disk_info,
+ network_info, image_meta, resize_instance)
+
+ def live_migration(self, context, instance_ref, dest,
+ post_method, recover_method, block_migration=False,
+ migrate_data=None):
+ """Live migration of an instance to another host."""
+ self._vmops.live_migration(context, instance_ref, dest,
+ post_method, recover_method,
+ block_migration)
+
+ def poll_rebooting_instances(self, timeout, instances):
+ """Poll for rebooting instances."""
+ self._vmops.poll_rebooting_instances(timeout, instances)
+
def get_info(self, instance):
"""Return info about the VM instance."""
return self._vmops.get_info(instance)
@@ -172,23 +259,29 @@ class VMwareESXDriver(driver.ComputeDriver):
"""Return snapshot of console."""
return self._vmops.get_console_output(instance)
- def get_volume_connector(self, _instance):
+ def get_vnc_console(self, instance):
+ """Return link to instance's VNC console."""
+ return self._vmops.get_vnc_console(instance)
+
+ def get_volume_connector(self, instance):
"""Return volume connector information."""
- # TODO(vish): When volume attaching is supported, return the
- # proper initiator iqn and host.
- return {
- 'ip': CONF.vmwareapi_host_ip,
- 'initiator': None,
- 'host': None
- }
+ return self._volumeops.get_volume_connector(instance)
+
+ def get_host_ip_addr(self):
+ """Retrieves the IP address of the ESX host."""
+ return self._host_ip
def attach_volume(self, connection_info, instance, mountpoint):
"""Attach volume storage to VM instance."""
- pass
+ return self._volumeops.attach_volume(connection_info,
+ instance,
+ mountpoint)
def detach_volume(self, connection_info, instance, mountpoint):
"""Detach volume storage to VM instance."""
- pass
+ return self._volumeops.detach_volume(connection_info,
+ instance,
+ mountpoint)
def get_console_pool_info(self, console_type):
"""Get info about the host on which the VM resides."""
@@ -197,8 +290,57 @@ class VMwareESXDriver(driver.ComputeDriver):
'password': CONF.vmwareapi_host_password}
def get_available_resource(self, nodename):
- """This method is supported only by libvirt."""
- return
+ """Retrieve resource info.
+
+ This method is called when nova-compute launches, and
+ as part of a periodic task.
+
+ :returns: dictionary describing resources
+
+ """
+ host_stats = self.get_host_stats(refresh=True)
+
+ # Updating host information
+ dic = {'vcpus': host_stats["vcpus"],
+ 'memory_mb': host_stats['host_memory_total'],
+ 'local_gb': host_stats['disk_total'],
+ 'vcpus_used': 0,
+ 'memory_mb_used': host_stats['host_memory_total'] -
+ host_stats['host_memory_free'],
+ 'local_gb_used': host_stats['disk_used'],
+ 'hypervisor_type': host_stats['hypervisor_type'],
+ 'hypervisor_version': host_stats['hypervisor_version'],
+ 'hypervisor_hostname': host_stats['hypervisor_hostname'],
+ 'cpu_info': jsonutils.dumps(host_stats['cpu_info'])}
+
+ return dic
+
+ def update_host_status(self):
+ """Update the status info of the host, and return those values
+ to the calling program."""
+ return self.host_state.update_status()
+
+ def get_host_stats(self, refresh=False):
+ """Return the current state of the host. If 'refresh' is
+ True, run the update first."""
+ return self.host_state.get_host_stats(refresh=refresh)
+
+ def host_power_action(self, host, action):
+ """Reboots, shuts down or powers up the host."""
+ return self._host.host_power_action(host, action)
+
+ def host_maintenance_mode(self, host, mode):
+ """Start/Stop host maintenance window. On start, it triggers
+ guest VMs evacuation."""
+ return self._host.host_maintenance_mode(host, mode)
+
+ def set_host_enabled(self, host, enabled):
+ """Sets the specified host's ability to accept new instances."""
+ return self._host.set_host_enabled(host, enabled)
+
+ def inject_network_info(self, instance, network_info):
+ """inject network info for specified instance."""
+ self._vmops.inject_network_info(instance, network_info)
def plug_vifs(self, instance, network_info):
"""Plug VIFs into networks."""
@@ -208,6 +350,16 @@ class VMwareESXDriver(driver.ComputeDriver):
"""Unplug VIFs from networks."""
self._vmops.unplug_vifs(instance, network_info)
+ def list_interfaces(self, instance_name):
+ """
+ Return the IDs of all the virtual network interfaces attached to the
+ specified instance, as a list. These IDs are opaque to the caller
+ (they are only useful for giving back to this layer as a parameter to
+ interface_stats). These IDs only need to be unique for a given
+ instance.
+ """
+ return self._vmops.list_interfaces(instance_name)
+
class VMwareAPISession(object):
"""
diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py
index 3f5041c22..692e5f253 100644
--- a/nova/virt/vmwareapi/fake.py
+++ b/nova/virt/vmwareapi/fake.py
@@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
@@ -140,16 +141,30 @@ class DataObject(object):
class VirtualDisk(DataObject):
"""
- Virtual Disk class. Does nothing special except setting
- __class__.__name__ to 'VirtualDisk'. Refer place where __class__.__name__
- is used in the code.
+ Virtual Disk class.
"""
- pass
+
+ def __init__(self):
+ super(VirtualDisk, self).__init__()
+ self.key = 0
+ self.unitNumber = 0
class VirtualDiskFlatVer2BackingInfo(DataObject):
"""VirtualDiskFlatVer2BackingInfo class."""
- pass
+
+ def __init__(self):
+ super(VirtualDiskFlatVer2BackingInfo, self).__init__()
+ self.thinProvisioned = False
+ self.eagerlyScrub = False
+
+
+class VirtualDiskRawDiskMappingVer1BackingInfo(DataObject):
+ """VirtualDiskRawDiskMappingVer1BackingInfo class."""
+
+ def __init__(self):
+ super(VirtualDiskRawDiskMappingVer1BackingInfo, self).__init__()
+ self.lunUuid = ""
class VirtualLsiLogicController(DataObject):
@@ -157,6 +172,14 @@ class VirtualLsiLogicController(DataObject):
pass
+class VirtualPCNet32(DataObject):
+ """VirtualPCNet32 class."""
+
+ def __init__(self):
+ super(VirtualPCNet32, self).__init__()
+ self.key = 4000
+
+
class VirtualMachine(ManagedObject):
"""Virtual Machine class."""
@@ -177,7 +200,7 @@ class VirtualMachine(ManagedObject):
self.set("config.files.vmPathName", kwargs.get("vmPathName"))
self.set("summary.config.numCpu", kwargs.get("numCpu", 1))
self.set("summary.config.memorySizeMB", kwargs.get("mem", 1))
- self.set("config.hardware.device", kwargs.get("virtual_disk", None))
+ self.set("config.hardware.device", kwargs.get("virtual_device", None))
self.set("config.extraConfig", kwargs.get("extra_config", None))
def reconfig(self, factory, val):
@@ -201,7 +224,9 @@ class VirtualMachine(ManagedObject):
controller = VirtualLsiLogicController()
controller.key = controller_key
- self.set("config.hardware.device", [disk, controller])
+ nic = VirtualPCNet32()
+
+ self.set("config.hardware.device", [disk, controller, nic])
except AttributeError:
# Case of Reconfig of VM to set extra params
self.set("config.extraConfig", val.extraConfig)
@@ -230,6 +255,8 @@ class Datastore(ManagedObject):
super(Datastore, self).__init__("Datastore")
self.set("summary.type", "VMFS")
self.set("summary.name", "fake-ds")
+ self.set("summary.capacity", 1024 * 1024 * 1024)
+ self.set("summary.freeSpace", 500 * 1024 * 1024)
class HostNetworkSystem(ManagedObject):
@@ -260,6 +287,29 @@ class HostSystem(ManagedObject):
host_net_sys = _db_content["HostNetworkSystem"][host_net_key].obj
self.set("configManager.networkSystem", host_net_sys)
+ summary = DataObject()
+ hardware = DataObject()
+ hardware.numCpuCores = 8
+ hardware.numCpuPkgs = 2
+ hardware.numCpuThreads = 16
+ hardware.vendor = "Intel"
+ hardware.cpuModel = "Intel(R) Xeon(R)"
+ hardware.memorySize = 1024 * 1024 * 1024
+ summary.hardware = hardware
+
+ quickstats = DataObject()
+ quickstats.overallMemoryUsage = 500
+ summary.quickStats = quickstats
+
+ product = DataObject()
+ product.name = "VMware ESXi"
+ product.version = "5.0.0"
+ config = DataObject()
+ config.product = product
+ summary.config = config
+
+ self.set("summary", summary)
+
if _db_content.get("Network", None) is None:
create_network()
net_ref = _db_content["Network"][_db_content["Network"].keys()[0]].obj
@@ -574,6 +624,11 @@ class FakeVim(object):
"""Fakes a return."""
return
+ def _just_return_task(self, method):
+ """Fakes a task return."""
+ task_mdo = create_task(method, "success")
+ return task_mdo.obj
+
def _unregister_vm(self, method, *args, **kwargs):
"""Unregisters a VM from the Host System."""
vm_ref = args[0]
@@ -602,7 +657,7 @@ class FakeVim(object):
def _set_power_state(self, method, vm_ref, pwr_state="poweredOn"):
"""Sets power state for the VM."""
if _db_content.get("VirtualMachine", None) is None:
- raise exception.NotFound(_(" No Virtual Machine has been "
+ raise exception.NotFound(_("No Virtual Machine has been "
"registered yet"))
if vm_ref not in _db_content.get("VirtualMachine"):
raise exception.NotFound(_("Virtual Machine with ref %s is not "
@@ -697,6 +752,9 @@ class FakeVim(object):
elif attr_name == "DeleteVirtualDisk_Task":
return lambda *args, **kwargs: self._delete_disk(attr_name,
*args, **kwargs)
+ elif attr_name == "Destroy_Task":
+ return lambda *args, **kwargs: self._unregister_vm(attr_name,
+ *args, **kwargs)
elif attr_name == "UnregisterVM":
return lambda *args, **kwargs: self._unregister_vm(attr_name,
*args, **kwargs)
@@ -714,3 +772,15 @@ class FakeVim(object):
elif attr_name == "AddPortGroup":
return lambda *args, **kwargs: self._add_port_group(attr_name,
*args, **kwargs)
+ elif attr_name == "RebootHost_Task":
+ return lambda *args, **kwargs: self._just_return_task(attr_name)
+ elif attr_name == "ShutdownHost_Task":
+ return lambda *args, **kwargs: self._just_return_task(attr_name)
+ elif attr_name == "PowerDownHostToStandBy_Task":
+ return lambda *args, **kwargs: self._just_return_task(attr_name)
+ elif attr_name == "PowerUpHostFromStandBy_Task":
+ return lambda *args, **kwargs: self._just_return_task(attr_name)
+ elif attr_name == "EnterMaintenanceMode_Task":
+ return lambda *args, **kwargs: self._just_return_task(attr_name)
+ elif attr_name == "ExitMaintenanceMode_Task":
+ return lambda *args, **kwargs: self._just_return_task(attr_name)
diff --git a/nova/virt/vmwareapi/host.py b/nova/virt/vmwareapi/host.py
new file mode 100644
index 000000000..09b8f1fe3
--- /dev/null
+++ b/nova/virt/vmwareapi/host.py
@@ -0,0 +1,140 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2012 VMware, Inc.
+#
+# 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 host-related functions (start, reboot, etc).
+"""
+
+from nova import exception
+from nova.openstack.common import log as logging
+from nova.virt.vmwareapi import vim_util
+from nova.virt.vmwareapi import vm_util
+
+LOG = logging.getLogger(__name__)
+
+
+class Host(object):
+ """
+ Implements host related operations.
+ """
+ def __init__(self, session):
+ self._session = session
+
+ def host_power_action(self, host, action):
+ """Reboots or shuts down the host."""
+ host_mor = self._session._call_method(vim_util, "get_objects",
+ "HostSystem")[0].obj
+ LOG.debug(_("%(action)s %(host)s") % locals())
+ if action == "reboot":
+ host_task = self._session._call_method(
+ self._session._get_vim(),
+ "RebootHost_Task", host_mor,
+ force=False)
+ elif action == "shutdown":
+ host_task = self._session._call_method(
+ self._session._get_vim(),
+ "ShutdownHost_Task", host_mor,
+ force=False)
+ elif action == "startup":
+ host_task = self._session._call_method(
+ self._session._get_vim(),
+ "PowerUpHostFromStandBy_Task", host_mor,
+ timeoutSec=60)
+ self._session._wait_for_task(host, host_task)
+
+ def host_maintenance_mode(self, host, mode):
+ """Start/Stop host maintenance window. On start, it triggers
+ guest VMs evacuation."""
+ host_mor = self._session._call_method(vim_util, "get_objects",
+ "HostSystem")[0].obj
+ LOG.debug(_("Set maintenance mod on %(host)s to %(mode)s") % locals())
+ if mode:
+ host_task = self._session._call_method(
+ self._session._get_vim(),
+ "EnterMaintenanceMode_Task",
+ host_mor, timeout=0,
+ evacuatePoweredOffVms=True)
+ else:
+ host_task = self._session._call_method(
+ self._session._get_vim(),
+ "ExitMaintenanceMode_Task",
+ host_mor, timeout=0)
+ self._session._wait_for_task(host, host_task)
+
+ def set_host_enabled(self, _host, enabled):
+ """Sets the specified host's ability to accept new instances."""
+ pass
+
+
+class HostState(object):
+ """Manages information about the ESX host this compute
+ node is running on.
+ """
+ def __init__(self, session, host_name):
+ super(HostState, self).__init__()
+ self._session = session
+ self._host_name = host_name
+ self._stats = {}
+ self.update_status()
+
+ def get_host_stats(self, refresh=False):
+ """Return the current state of the host. If 'refresh' is
+ True, run the update first.
+ """
+ if refresh:
+ self.update_status()
+ return self._stats
+
+ def update_status(self):
+ """Update the current state of the host.
+ """
+ host_mor = self._session._call_method(vim_util, "get_objects",
+ "HostSystem")[0].obj
+ summary = self._session._call_method(vim_util,
+ "get_dynamic_property",
+ host_mor,
+ "HostSystem",
+ "summary")
+
+ if summary is None:
+ return
+
+ try:
+ ds = vm_util.get_datastore_ref_and_name(self._session)
+ except exception.DatastoreNotFound:
+ ds = (None, None, 0, 0)
+
+ data = {}
+ data["vcpus"] = summary.hardware.numCpuThreads
+ data["cpu_info"] = \
+ {"vendor": summary.hardware.vendor,
+ "model": summary.hardware.cpuModel,
+ "topology": {"cores": summary.hardware.numCpuCores,
+ "sockets": summary.hardware.numCpuPkgs,
+ "threads": summary.hardware.numCpuThreads}
+ }
+ data["disk_total"] = ds[2] / (1024 * 1024)
+ data["disk_available"] = ds[3] / (1024 * 1024)
+ data["disk_used"] = data["disk_total"] - data["disk_available"]
+ data["host_memory_total"] = summary.hardware.memorySize / (1024 * 1024)
+ data["host_memory_free"] = data["host_memory_total"] - \
+ summary.quickStats.overallMemoryUsage
+ data["hypervisor_type"] = summary.config.product.name
+ data["hypervisor_version"] = summary.config.product.version
+ data["hypervisor_hostname"] = self._host_name
+
+ self._stats = data
+ return data
diff --git a/nova/virt/vmwareapi/io_util.py b/nova/virt/vmwareapi/io_util.py
index 999e7a085..6a50c4d6e 100644
--- a/nova/virt/vmwareapi/io_util.py
+++ b/nova/virt/vmwareapi/io_util.py
@@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
@@ -57,6 +58,14 @@ class ThreadSafePipe(queue.LightQueue):
"""Put a data item in the pipe."""
self.put(data)
+ def seek(self, offset, whence=0):
+ """Set the file's current position at the offset."""
+ pass
+
+ def tell(self):
+ """Get size of the file to be read."""
+ return self.transfer_size
+
def close(self):
"""A place-holder to maintain consistency."""
pass
diff --git a/nova/virt/vmwareapi/network_util.py b/nova/virt/vmwareapi/network_util.py
index d2bdad0c1..f63d7f723 100644
--- a/nova/virt/vmwareapi/network_util.py
+++ b/nova/virt/vmwareapi/network_util.py
@@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
@@ -58,7 +59,11 @@ def get_network_with_the_name(session, network_name="vmnet0"):
if props.name == network_name:
network_obj['type'] = 'DistributedVirtualPortgroup'
network_obj['dvpg'] = props.key
- network_obj['dvsw'] = props.distributedVirtualSwitch.value
+ dvs_props = session._call_method(vim_util,
+ "get_dynamic_property",
+ props.distributedVirtualSwitch,
+ "VmwareDistributedVirtualSwitch", "uuid")
+ network_obj['dvsw'] = dvs_props
else:
props = session._call_method(vim_util,
"get_dynamic_property", network,
@@ -102,11 +107,11 @@ def get_vswitch_for_vlan_interface(session, vlan_interface):
def check_if_vlan_interface_exists(session, vlan_interface):
"""Checks if the vlan_inteface exists on the esx host."""
- host_net_system_mor = session._call_method(vim_util, "get_objects",
- "HostSystem", ["configManager.networkSystem"])[0].propSet[0].val
+ host_mor = session._call_method(vim_util, "get_objects",
+ "HostSystem")[0].obj
physical_nics_ret = session._call_method(vim_util,
- "get_dynamic_property", host_net_system_mor,
- "HostNetworkSystem", "networkInfo.pnic")
+ "get_dynamic_property", host_mor,
+ "HostSystem", "config.network.pnic")
# Meaning there are no physical nics on the host
if not physical_nics_ret:
return False
diff --git a/nova/virt/vmwareapi/read_write_util.py b/nova/virt/vmwareapi/read_write_util.py
index 39ea8e2e8..5dcdc6fdb 100644
--- a/nova/virt/vmwareapi/read_write_util.py
+++ b/nova/virt/vmwareapi/read_write_util.py
@@ -140,7 +140,7 @@ class VMwareHTTPWriteFile(VMwareHTTPFile):
self.conn.getresponse()
except Exception, excep:
LOG.debug(_("Exception during HTTP connection close in "
- "VMwareHTTpWrite. Exception is %s") % excep)
+ "VMwareHTTPWrite. Exception is %s") % excep)
super(VMwareHTTPWriteFile, self).close()
diff --git a/nova/virt/vmwareapi/vif.py b/nova/virt/vmwareapi/vif.py
index c5b524186..5684e6aa6 100644
--- a/nova/virt/vmwareapi/vif.py
+++ b/nova/virt/vmwareapi/vif.py
@@ -45,7 +45,7 @@ def ensure_vlan_bridge(self, session, network):
# Check if the vlan_interface physical network adapter exists on the
# host.
if not network_util.check_if_vlan_interface_exists(session,
- vlan_interface):
+ vlan_interface):
raise exception.NetworkAdapterNotFound(adapter=vlan_interface)
# Get the vSwitch associated with the Physical Adapter
diff --git a/nova/virt/vmwareapi/vm_util.py b/nova/virt/vmwareapi/vm_util.py
index e03b88804..af481b566 100644
--- a/nova/virt/vmwareapi/vm_util.py
+++ b/nova/virt/vmwareapi/vm_util.py
@@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
@@ -18,6 +19,10 @@
The VMware API VM utility module to build SOAP object specs.
"""
+import copy
+from nova import exception
+from nova.virt.vmwareapi import vim_util
+
def build_datastore_path(datastore_name, path):
"""Build the datastore compliant path."""
@@ -42,7 +47,7 @@ def get_vm_create_spec(client_factory, instance, data_store_name,
vif_infos, os_type="otherGuest"):
"""Builds the VM Create spec."""
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
- config_spec.name = instance.name
+ config_spec.name = instance['name']
config_spec.guestId = os_type
vm_file_info = client_factory.create('ns0:VirtualMachineFileInfo')
@@ -57,8 +62,8 @@ def get_vm_create_spec(client_factory, instance, data_store_name,
tools_info.beforeGuestReboot = True
config_spec.tools = tools_info
- config_spec.numCPUs = int(instance.vcpus)
- config_spec.memoryMB = int(instance.memory_mb)
+ config_spec.numCPUs = int(instance['vcpus'])
+ config_spec.memoryMB = int(instance['memory_mb'])
vif_spec_list = []
for vif_info in vif_infos:
@@ -71,9 +76,9 @@ def get_vm_create_spec(client_factory, instance, data_store_name,
return config_spec
-def create_controller_spec(client_factory, key):
+def create_controller_spec(client_factory, key, adapter_type="lsiLogic"):
"""
- Builds a Config Spec for the LSI Logic Controller's addition
+ Builds a Config Spec for the LSI or Bus Logic Controller's addition
which acts as the controller for the virtual hard disk to be attached
to the VM.
"""
@@ -81,11 +86,16 @@ def create_controller_spec(client_factory, key):
virtual_device_config = client_factory.create(
'ns0:VirtualDeviceConfigSpec')
virtual_device_config.operation = "add"
- virtual_lsi = client_factory.create('ns0:VirtualLsiLogicController')
- virtual_lsi.key = key
- virtual_lsi.busNumber = 0
- virtual_lsi.sharedBus = "noSharing"
- virtual_device_config.device = virtual_lsi
+ if adapter_type == "busLogic":
+ virtual_controller = client_factory.create(
+ 'ns0:VirtualBusLogicController')
+ else:
+ virtual_controller = client_factory.create(
+ 'ns0:VirtualLsiLogicController')
+ virtual_controller.key = key
+ virtual_controller.busNumber = 0
+ virtual_controller.sharedBus = "noSharing"
+ virtual_device_config.device = virtual_controller
return virtual_device_config
@@ -142,8 +152,15 @@ def create_network_spec(client_factory, vif_info):
return network_spec
-def get_vmdk_attach_config_spec(client_factory, disksize, file_path,
- adapter_type="lsiLogic"):
+def get_vmdk_attach_config_spec(client_factory,
+ adapter_type="lsiLogic",
+ disk_type="preallocated",
+ file_path=None,
+ disk_size=None,
+ linked_clone=False,
+ controller_key=None,
+ unit_number=None,
+ device_name=None):
"""Builds the vmdk attach config spec."""
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
@@ -152,15 +169,19 @@ def get_vmdk_attach_config_spec(client_factory, disksize, file_path,
device_config_spec = []
# For IDE devices, there are these two default controllers created in the
# VM having keys 200 and 201
- if adapter_type == "ide":
- controller_key = 200
- else:
- controller_key = -101
- controller_spec = create_controller_spec(client_factory,
- controller_key)
- device_config_spec.append(controller_spec)
+ if controller_key is None:
+ if adapter_type == "ide":
+ controller_key = 200
+ else:
+ controller_key = -101
+ controller_spec = create_controller_spec(client_factory,
+ controller_key,
+ adapter_type)
+ device_config_spec.append(controller_spec)
virtual_device_config_spec = create_virtual_disk_spec(client_factory,
- disksize, controller_key, file_path)
+ controller_key, disk_type, file_path,
+ disk_size, linked_clone,
+ unit_number, device_name)
device_config_spec.append(virtual_device_config_spec)
@@ -168,20 +189,45 @@ def get_vmdk_attach_config_spec(client_factory, disksize, file_path,
return config_spec
-def get_vmdk_file_path_and_adapter_type(client_factory, hardware_devices):
+def get_vmdk_detach_config_spec(client_factory, device):
+ """Builds the vmdk detach config spec."""
+ config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
+
+ device_config_spec = []
+ virtual_device_config_spec = delete_virtual_disk_spec(client_factory,
+ device)
+
+ device_config_spec.append(virtual_device_config_spec)
+
+ config_spec.deviceChange = device_config_spec
+ return config_spec
+
+
+def get_vmdk_path_and_adapter_type(hardware_devices):
"""Gets the vmdk file path and the storage adapter type."""
if hardware_devices.__class__.__name__ == "ArrayOfVirtualDevice":
hardware_devices = hardware_devices.VirtualDevice
vmdk_file_path = None
vmdk_controler_key = None
+ disk_type = None
+ unit_number = 0
adapter_type_dict = {}
for device in hardware_devices:
- if (device.__class__.__name__ == "VirtualDisk" and
- device.backing.__class__.__name__ ==
- "VirtualDiskFlatVer2BackingInfo"):
- vmdk_file_path = device.backing.fileName
- vmdk_controler_key = device.controllerKey
+ if device.__class__.__name__ == "VirtualDisk":
+ if device.backing.__class__.__name__ == \
+ "VirtualDiskFlatVer2BackingInfo":
+ vmdk_file_path = device.backing.fileName
+ vmdk_controler_key = device.controllerKey
+ if getattr(device.backing, 'thinProvisioned', False):
+ disk_type = "thin"
+ else:
+ if getattr(device.backing, 'eagerlyScrub', False):
+ disk_type = "eagerZeroedThick"
+ else:
+ disk_type = "preallocated"
+ if device.unitNumber > unit_number:
+ unit_number = device.unitNumber
elif device.__class__.__name__ == "VirtualLsiLogicController":
adapter_type_dict[device.key] = "lsiLogic"
elif device.__class__.__name__ == "VirtualBusLogicController":
@@ -193,28 +239,59 @@ def get_vmdk_file_path_and_adapter_type(client_factory, hardware_devices):
adapter_type = adapter_type_dict.get(vmdk_controler_key, "")
- return vmdk_file_path, adapter_type
+ return (vmdk_file_path, vmdk_controler_key, adapter_type,
+ disk_type, unit_number)
+
+
+def get_rdm_disk(hardware_devices, uuid):
+ """Gets the RDM disk key."""
+ if hardware_devices.__class__.__name__ == "ArrayOfVirtualDevice":
+ hardware_devices = hardware_devices.VirtualDevice
+
+ for device in hardware_devices:
+ if (device.__class__.__name__ == "VirtualDisk" and
+ device.backing.__class__.__name__ ==
+ "VirtualDiskRawDiskMappingVer1BackingInfo" and
+ device.backing.lunUuid == uuid):
+ return device
-def get_copy_virtual_disk_spec(client_factory, adapter_type="lsilogic"):
+def get_copy_virtual_disk_spec(client_factory, adapter_type="lsilogic",
+ disk_type="preallocated"):
"""Builds the Virtual Disk copy spec."""
dest_spec = client_factory.create('ns0:VirtualDiskSpec')
dest_spec.adapterType = adapter_type
- dest_spec.diskType = "thick"
+ dest_spec.diskType = disk_type
return dest_spec
-def get_vmdk_create_spec(client_factory, size_in_kb, adapter_type="lsiLogic"):
+def get_vmdk_create_spec(client_factory, size_in_kb, adapter_type="lsiLogic",
+ disk_type="preallocated"):
"""Builds the virtual disk create spec."""
create_vmdk_spec = client_factory.create('ns0:FileBackedVirtualDiskSpec')
create_vmdk_spec.adapterType = adapter_type
- create_vmdk_spec.diskType = "thick"
+ create_vmdk_spec.diskType = disk_type
create_vmdk_spec.capacityKb = size_in_kb
return create_vmdk_spec
-def create_virtual_disk_spec(client_factory, disksize, controller_key,
- file_path=None):
+def get_rdm_create_spec(client_factory, device, adapter_type="lsiLogic",
+ disk_type="rdmp"):
+ """Builds the RDM virtual disk create spec."""
+ create_vmdk_spec = client_factory.create('ns0:DeviceBackedVirtualDiskSpec')
+ create_vmdk_spec.adapterType = adapter_type
+ create_vmdk_spec.diskType = disk_type
+ create_vmdk_spec.device = device
+ return create_vmdk_spec
+
+
+def create_virtual_disk_spec(client_factory, controller_key,
+ disk_type="preallocated",
+ file_path=None,
+ disk_size=None,
+ linked_clone=False,
+ unit_number=None,
+ device_name=None):
"""
Builds spec for the creation of a new/ attaching of an already existing
Virtual Disk to the VM.
@@ -222,26 +299,40 @@ def create_virtual_disk_spec(client_factory, disksize, controller_key,
virtual_device_config = client_factory.create(
'ns0:VirtualDeviceConfigSpec')
virtual_device_config.operation = "add"
- if file_path is None:
+ if (file_path is None) or linked_clone:
virtual_device_config.fileOperation = "create"
virtual_disk = client_factory.create('ns0:VirtualDisk')
- disk_file_backing = client_factory.create(
- 'ns0:VirtualDiskFlatVer2BackingInfo')
- disk_file_backing.diskMode = "persistent"
- disk_file_backing.thinProvisioned = False
- if file_path is not None:
- disk_file_backing.fileName = file_path
+ if disk_type == "rdm" or disk_type == "rdmp":
+ disk_file_backing = client_factory.create(
+ 'ns0:VirtualDiskRawDiskMappingVer1BackingInfo')
+ disk_file_backing.compatibilityMode = "virtualMode" \
+ if disk_type == "rdm" else "physicalMode"
+ disk_file_backing.diskMode = "independent_persistent"
+ disk_file_backing.deviceName = device_name or ""
else:
- disk_file_backing.fileName = ""
+ disk_file_backing = client_factory.create(
+ 'ns0:VirtualDiskFlatVer2BackingInfo')
+ disk_file_backing.diskMode = "persistent"
+ if disk_type == "thin":
+ disk_file_backing.thinProvisioned = True
+ else:
+ if disk_type == "eagerZeroedThick":
+ disk_file_backing.eagerlyScrub = True
+ disk_file_backing.fileName = file_path or ""
connectable_spec = client_factory.create('ns0:VirtualDeviceConnectInfo')
connectable_spec.startConnected = True
connectable_spec.allowGuestControl = False
connectable_spec.connected = True
- virtual_disk.backing = disk_file_backing
+ if not linked_clone:
+ virtual_disk.backing = disk_file_backing
+ else:
+ virtual_disk.backing = copy.copy(disk_file_backing)
+ virtual_disk.backing.fileName = ""
+ virtual_disk.backing.parent = disk_file_backing
virtual_disk.connectable = connectable_spec
# The Server assigns a Key to the device. Here we pass a -ve random key.
@@ -249,14 +340,48 @@ def create_virtual_disk_spec(client_factory, disksize, controller_key,
# want a clash with the key that server might associate with the device
virtual_disk.key = -100
virtual_disk.controllerKey = controller_key
- virtual_disk.unitNumber = 0
- virtual_disk.capacityInKB = disksize
+ virtual_disk.unitNumber = unit_number or 0
+ virtual_disk.capacityInKB = disk_size or 0
virtual_device_config.device = virtual_disk
return virtual_device_config
+def delete_virtual_disk_spec(client_factory, device):
+ """
+ Builds spec for the deletion of an already existing Virtual Disk from VM.
+ """
+ virtual_device_config = client_factory.create(
+ 'ns0:VirtualDeviceConfigSpec')
+ virtual_device_config.operation = "remove"
+ virtual_device_config.fileOperation = "destroy"
+ virtual_device_config.device = device
+
+ return virtual_device_config
+
+
+def clone_vm_spec(client_factory, location,
+ power_on=False, snapshot=None, template=False):
+ """Builds the VM clone spec."""
+ clone_spec = client_factory.create('ns0:VirtualMachineCloneSpec')
+ clone_spec.location = location
+ clone_spec.powerOn = power_on
+ clone_spec.snapshot = snapshot
+ clone_spec.template = template
+ return clone_spec
+
+
+def relocate_vm_spec(client_factory, datastore=None, host=None,
+ disk_move_type="moveAllDiskBackingsAndAllowSharing"):
+ """Builds the VM relocation spec."""
+ rel_spec = client_factory.create('ns0:VirtualMachineRelocateSpec')
+ rel_spec.datastore = datastore
+ rel_spec.diskMoveType = disk_move_type
+ rel_spec.host = host
+ return rel_spec
+
+
def get_dummy_vm_create_spec(client_factory, name, data_store_name):
"""Builds the dummy VM create spec."""
config_spec = client_factory.create('ns0:VirtualMachineConfigSpec')
@@ -318,3 +443,66 @@ def get_add_vswitch_port_group_spec(client_factory, vswitch_name,
vswitch_port_group_spec.policy = policy
return vswitch_port_group_spec
+
+
+def get_vnc_config_spec(client_factory, port, password):
+ """Builds the vnc config spec."""
+ virtual_machine_config_spec = client_factory.create(
+ 'ns0:VirtualMachineConfigSpec')
+
+ opt_enabled = client_factory.create('ns0:OptionValue')
+ opt_enabled.key = "RemoteDisplay.vnc.enabled"
+ opt_enabled.value = "true"
+ opt_port = client_factory.create('ns0:OptionValue')
+ opt_port.key = "RemoteDisplay.vnc.port"
+ opt_port.value = port
+ opt_pass = client_factory.create('ns0:OptionValue')
+ opt_pass.key = "RemoteDisplay.vnc.password"
+ opt_pass.value = password
+ virtual_machine_config_spec.extraConfig = [opt_enabled, opt_port, opt_pass]
+ return virtual_machine_config_spec
+
+
+def search_datastore_spec(client_factory, file_name):
+ """Builds the datastore search spec."""
+ search_spec = client_factory.create('ns0:HostDatastoreBrowserSearchSpec')
+ search_spec.matchPattern = [file_name]
+ return search_spec
+
+
+def get_vm_ref_from_name(session, vm_name):
+ """Get reference to the VM with the name specified."""
+ vms = session._call_method(vim_util, "get_objects",
+ "VirtualMachine", ["name"])
+ for vm in vms:
+ if vm.propSet[0].val == vm_name:
+ return vm.obj
+ return None
+
+
+def get_datastore_ref_and_name(session):
+ """Get the datastore list and choose the first local storage."""
+ data_stores = session._call_method(vim_util, "get_objects",
+ "Datastore", ["summary.type", "summary.name",
+ "summary.capacity", "summary.freeSpace"])
+ for elem in data_stores:
+ ds_name = None
+ ds_type = None
+ ds_cap = None
+ ds_free = None
+ for prop in elem.propSet:
+ if prop.name == "summary.type":
+ ds_type = prop.val
+ elif prop.name == "summary.name":
+ ds_name = prop.val
+ elif prop.name == "summary.capacity":
+ ds_cap = prop.val
+ elif prop.name == "summary.freeSpace":
+ ds_free = prop.val
+ # Local storage identifier
+ if ds_type == "VMFS" or ds_type == "NFS":
+ data_store_name = ds_name
+ return elem.obj, data_store_name, ds_cap, ds_free
+
+ if data_store_name is None:
+ raise exception.DatastoreNotFound()
diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py
index 883e751a8..75f85454b 100644
--- a/nova/virt/vmwareapi/vmops.py
+++ b/nova/virt/vmwareapi/vmops.py
@@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
@@ -26,12 +27,16 @@ import urllib
import urllib2
import uuid
+from nova import block_device
+from nova.compute import api as compute
from nova.compute import power_state
from nova.compute import task_states
+from nova import context as nova_context
from nova import exception
from nova.openstack.common import cfg
-from nova.openstack.common import importutils
+from nova.openstack.common import excutils
from nova.openstack.common import log as logging
+from nova.virt import driver
from nova.virt.vmwareapi import network_util
from nova.virt.vmwareapi import vif as vmwarevif
from nova.virt.vmwareapi import vim_util
@@ -39,22 +44,45 @@ from nova.virt.vmwareapi import vm_util
from nova.virt.vmwareapi import vmware_images
+vmware_vif_opts = [
+ cfg.StrOpt('integration_bridge',
+ default='br-int',
+ help='Name of Integration Bridge'),
+ ]
+
+vmware_group = cfg.OptGroup(name='vmware',
+ title='VMware Options')
+
CONF = cfg.CONF
+CONF.register_group(vmware_group)
+CONF.register_opts(vmware_vif_opts, vmware_group)
+CONF.import_opt('vnc_enabled', 'nova.vnc')
LOG = logging.getLogger(__name__)
VMWARE_POWER_STATES = {
'poweredOff': power_state.SHUTDOWN,
'poweredOn': power_state.RUNNING,
- 'suspended': power_state.PAUSED}
+ 'suspended': power_state.SUSPENDED}
+VMWARE_PREFIX = 'vmware'
+
+
+RESIZE_TOTAL_STEPS = 4
class VMwareVMOps(object):
"""Management class for VM-related tasks."""
- def __init__(self, session):
+ def __init__(self, session, virtapi, volumeops):
"""Initializer."""
+ self.compute_api = compute.API()
self._session = session
+ self._virtapi = virtapi
+ self._volumeops = volumeops
+ self._instance_path_base = VMWARE_PREFIX + CONF.base_dir_name
+ self._default_root_device = 'vda'
+ self._rescue_suffix = '-rescue'
+ self._poll_rescue_last_ran = None
def list_instances(self):
"""Lists the VM instances that are registered with the ESX host."""
@@ -71,13 +99,14 @@ class VMwareVMOps(object):
vm_name = prop.val
elif prop.name == "runtime.connectionState":
conn_state = prop.val
- # Ignoring the oprhaned or inaccessible VMs
+ # Ignoring the orphaned or inaccessible VMs
if conn_state not in ["orphaned", "inaccessible"]:
lst_vm_names.append(vm_name)
LOG.debug(_("Got total of %s instances") % str(len(lst_vm_names)))
return lst_vm_names
- def spawn(self, context, instance, image_meta, network_info):
+ def spawn(self, context, instance, image_meta, network_info,
+ block_device_info=None):
"""
Creates a VM instance.
@@ -85,44 +114,28 @@ class VMwareVMOps(object):
1. Create a VM with no disk and the specifics in the instance object
like RAM size.
- 2. Create a dummy vmdk of the size of the disk file that is to be
- uploaded. This is required just to create the metadata file.
- 3. Delete the -flat.vmdk file created in the above step and retain
- the metadata .vmdk file.
- 4. Upload the disk file.
- 5. Attach the disk to the VM by reconfiguring the same.
- 6. Power on the VM.
+ 2. For flat disk
+ 2.1. Create a dummy vmdk of the size of the disk file that is to be
+ uploaded. This is required just to create the metadata file.
+ 2.2. Delete the -flat.vmdk file created in the above step and retain
+ the metadata .vmdk file.
+ 2.3. Upload the disk file.
+ 3. For sparse disk
+ 3.1. Upload the disk file to a -sparse.vmdk file.
+ 3.2. Copy/Clone the -sparse.vmdk file to a thin vmdk.
+ 3.3. Delete the -sparse.vmdk file.
+ 4. Attach the disk to the VM by reconfiguring the same.
+ 5. Power on the VM.
"""
- vm_ref = self._get_vm_ref_from_the_name(instance.name)
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
if vm_ref:
- raise exception.InstanceExists(name=instance.name)
+ raise exception.InstanceExists(name=instance['name'])
client_factory = self._session._get_vim().client.factory
service_content = self._session._get_vim().get_service_content()
-
- def _get_datastore_ref():
- """Get the datastore list and choose the first local storage."""
- data_stores = self._session._call_method(vim_util, "get_objects",
- "Datastore", ["summary.type", "summary.name"])
- for elem in data_stores:
- ds_name = None
- ds_type = None
- for prop in elem.propSet:
- if prop.name == "summary.type":
- ds_type = prop.val
- elif prop.name == "summary.name":
- ds_name = prop.val
- # Local storage identifier
- if ds_type == "VMFS":
- data_store_name = ds_name
- return data_store_name
-
- if data_store_name is None:
- msg = _("Couldn't get a local Datastore reference")
- LOG.error(msg, instance=instance)
- raise exception.NovaException(msg)
-
- data_store_name = _get_datastore_ref()
+ ds = vm_util.get_datastore_ref_and_name(self._session)
+ data_store_ref = ds[0]
+ data_store_name = ds[1]
def _get_image_properties():
"""
@@ -130,31 +143,21 @@ class VMwareVMOps(object):
repository.
"""
_image_info = vmware_images.get_vmdk_size_and_properties(context,
- instance.image_ref,
- instance)
+ instance['image_ref'],
+ instance)
image_size, image_properties = _image_info
vmdk_file_size_in_kb = int(image_size) / 1024
os_type = image_properties.get("vmware_ostype", "otherGuest")
adapter_type = image_properties.get("vmware_adaptertype",
"lsiLogic")
- return vmdk_file_size_in_kb, os_type, adapter_type
-
- vmdk_file_size_in_kb, os_type, adapter_type = _get_image_properties()
-
- def _get_vmfolder_and_res_pool_mors():
- """Get the Vm folder ref from the datacenter."""
- dc_objs = self._session._call_method(vim_util, "get_objects",
- "Datacenter", ["vmFolder"])
- # There is only one default datacenter in a standalone ESX host
- vm_folder_mor = dc_objs[0].propSet[0].val
+ disk_type = image_properties.get("vmware_disktype",
+ "preallocated")
+ return vmdk_file_size_in_kb, os_type, adapter_type, disk_type
- # Get the resource pool. Taking the first resource pool coming our
- # way. Assuming that is the default resource pool.
- res_pool_mor = self._session._call_method(vim_util, "get_objects",
- "ResourcePool")[0].obj
- return vm_folder_mor, res_pool_mor
+ (vmdk_file_size_in_kb, os_type, adapter_type,
+ disk_type) = _get_image_properties()
- vm_folder_mor, res_pool_mor = _get_vmfolder_and_res_pool_mors()
+ vm_folder_ref, res_pool_ref = self._get_vmfolder_and_res_pool_refs()
def _check_if_network_bridge_exists(network_name):
network_ref = network_util.get_network_with_the_name(
@@ -165,9 +168,12 @@ class VMwareVMOps(object):
def _get_vif_infos():
vif_infos = []
+ if network_info is None:
+ return vif_infos
for (network, mapping) in network_info:
mac_address = mapping['mac']
- network_name = network['bridge']
+ network_name = network['bridge'] or \
+ CONF.vmware.integration_bridge
if mapping.get('should_create_vlan'):
network_ref = vmwarevif.ensure_vlan_bridge(
self._session, network)
@@ -188,33 +194,29 @@ class VMwareVMOps(object):
def _execute_create_vm():
"""Create VM on ESX host."""
- LOG.debug(_("Creating VM on the ESX host"), instance=instance)
+ LOG.debug(_("Creating VM on the ESX host"), instance=instance)
# Create the VM on the ESX host
vm_create_task = self._session._call_method(
self._session._get_vim(),
- "CreateVM_Task", vm_folder_mor,
- config=config_spec, pool=res_pool_mor)
+ "CreateVM_Task", vm_folder_ref,
+ config=config_spec, pool=res_pool_ref)
self._session._wait_for_task(instance['uuid'], vm_create_task)
- LOG.debug(_("Created VM on the ESX host"), instance=instance)
+ LOG.debug(_("Created VM on the ESX host"), instance=instance)
_execute_create_vm()
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
# Set the machine.id parameter of the instance to inject
# the NIC configuration inside the VM
if CONF.flat_injected:
self._set_machine_id(client_factory, instance, network_info)
- # Naming the VM files in correspondence with the VM instance name
- # The flat vmdk file name
- flat_uploaded_vmdk_name = "%s/%s-flat.vmdk" % (instance.name,
- instance.name)
- # The vmdk meta-data file
- uploaded_vmdk_name = "%s/%s.vmdk" % (instance.name, instance.name)
- flat_uploaded_vmdk_path = vm_util.build_datastore_path(data_store_name,
- flat_uploaded_vmdk_name)
- uploaded_vmdk_path = vm_util.build_datastore_path(data_store_name,
- uploaded_vmdk_name)
+ # Set the vnc configuration of the instance, vnc port starts from 5900
+ if CONF.vnc_enabled:
+ vnc_port = self._get_vnc_port(vm_ref)
+ vnc_pass = CONF.vnc_password or ''
+ self._set_vnc_config(client_factory, instance, vnc_port, vnc_pass)
def _create_virtual_disk():
"""Create a virtual disk of the size of flat vmdk file."""
@@ -225,103 +227,186 @@ class VMwareVMOps(object):
# Here we assume thick provisioning and lsiLogic for the adapter
# type
LOG.debug(_("Creating Virtual Disk of size "
- "%(vmdk_file_size_in_kb)s KB and adapter type "
- "%(adapter_type)s on the ESX host local store"
- " %(data_store_name)s") %
+ "%(vmdk_file_size_in_kb)s KB and adapter type "
+ "%(adapter_type)s on the ESX host local store "
+ "%(data_store_name)s") %
{"vmdk_file_size_in_kb": vmdk_file_size_in_kb,
"adapter_type": adapter_type,
"data_store_name": data_store_name},
instance=instance)
vmdk_create_spec = vm_util.get_vmdk_create_spec(client_factory,
- vmdk_file_size_in_kb, adapter_type)
+ vmdk_file_size_in_kb, adapter_type,
+ disk_type)
vmdk_create_task = self._session._call_method(
self._session._get_vim(),
"CreateVirtualDisk_Task",
service_content.virtualDiskManager,
name=uploaded_vmdk_path,
- datacenter=self._get_datacenter_name_and_ref()[0],
+ datacenter=dc_ref,
spec=vmdk_create_spec)
self._session._wait_for_task(instance['uuid'], vmdk_create_task)
LOG.debug(_("Created Virtual Disk of size %(vmdk_file_size_in_kb)s"
- " KB on the ESX host local store "
- "%(data_store_name)s") %
+ " KB and type %(disk_type)s on "
+ "the ESX host local store %(data_store_name)s") %
{"vmdk_file_size_in_kb": vmdk_file_size_in_kb,
+ "disk_type": disk_type,
"data_store_name": data_store_name},
instance=instance)
- _create_virtual_disk()
-
- def _delete_disk_file():
- LOG.debug(_("Deleting the file %(flat_uploaded_vmdk_path)s "
+ def _delete_disk_file(vmdk_path):
+ LOG.debug(_("Deleting the file %(vmdk_path)s "
"on the ESX host local"
"store %(data_store_name)s") %
- {"flat_uploaded_vmdk_path": flat_uploaded_vmdk_path,
+ {"vmdk_path": vmdk_path,
"data_store_name": data_store_name},
instance=instance)
- # Delete the -flat.vmdk file created. .vmdk file is retained.
+ # Delete the vmdk file.
vmdk_delete_task = self._session._call_method(
self._session._get_vim(),
"DeleteDatastoreFile_Task",
service_content.fileManager,
- name=flat_uploaded_vmdk_path)
+ name=vmdk_path,
+ datacenter=dc_ref)
self._session._wait_for_task(instance['uuid'], vmdk_delete_task)
- LOG.debug(_("Deleted the file %(flat_uploaded_vmdk_path)s on the "
+ LOG.debug(_("Deleted the file %(vmdk_path)s on the "
"ESX host local store %(data_store_name)s") %
- {"flat_uploaded_vmdk_path": flat_uploaded_vmdk_path,
+ {"vmdk_path": vmdk_path,
"data_store_name": data_store_name},
instance=instance)
- _delete_disk_file()
-
- cookies = self._session._get_vim().client.options.transport.cookiejar
-
def _fetch_image_on_esx_datastore():
"""Fetch image from Glance to ESX datastore."""
LOG.debug(_("Downloading image file data %(image_ref)s to the ESX "
"data store %(data_store_name)s") %
- {'image_ref': instance.image_ref,
+ {'image_ref': instance['image_ref'],
'data_store_name': data_store_name},
instance=instance)
- # Upload the -flat.vmdk file whose meta-data file we just created
- # above
+ # For flat disk, upload the -flat.vmdk file whose meta-data file
+ # we just created above
+ # For sparse disk, upload the -sparse.vmdk file to be copied into
+ # a flat vmdk
+ upload_vmdk_name = sparse_uploaded_vmdk_name \
+ if disk_type == "sparse" else flat_uploaded_vmdk_name
vmware_images.fetch_image(
context,
- instance.image_ref,
+ instance['image_ref'],
instance,
host=self._session._host_ip,
- data_center_name=self._get_datacenter_name_and_ref()[1],
+ data_center_name=self._get_datacenter_ref_and_name()[1],
datastore_name=data_store_name,
cookies=cookies,
- file_path=flat_uploaded_vmdk_name)
- LOG.debug(_("Downloaded image file data %(image_ref)s to the ESX "
- "data store %(data_store_name)s") %
- {'image_ref': instance.image_ref,
+ file_path=upload_vmdk_name)
+ LOG.debug(_("Downloaded image file data %(image_ref)s to "
+ "%(upload_vmdk_name)s on the ESX data store "
+ "%(data_store_name)s") %
+ {'image_ref': instance['image_ref'],
+ 'upload_vmdk_name': upload_vmdk_name,
'data_store_name': data_store_name},
instance=instance)
- _fetch_image_on_esx_datastore()
- vm_ref = self._get_vm_ref_from_the_name(instance.name)
-
- def _attach_vmdk_to_the_vm():
- """
- Attach the vmdk uploaded to the VM. VM reconfigure is done
- to do so.
- """
- vmdk_attach_config_spec = vm_util.get_vmdk_attach_config_spec(
- client_factory,
- vmdk_file_size_in_kb, uploaded_vmdk_path,
- adapter_type)
- LOG.debug(_("Reconfiguring VM instance to attach the image disk"),
- instance=instance)
- reconfig_task = self._session._call_method(
- self._session._get_vim(),
- "ReconfigVM_Task", vm_ref,
- spec=vmdk_attach_config_spec)
- self._session._wait_for_task(instance['uuid'], reconfig_task)
- LOG.debug(_("Reconfigured VM instance to attach the image disk"),
+ def _copy_virtual_disk():
+ """Copy a sparse virtual disk to a thin virtual disk."""
+ # Copy a sparse virtual disk to a thin virtual disk. This is also
+ # done to generate the meta-data file whose specifics
+ # depend on the size of the disk, thin/thick provisioning and the
+ # storage adapter type.
+ LOG.debug(_("Copying Virtual Disk of size "
+ "%(vmdk_file_size_in_kb)s KB and adapter type "
+ "%(adapter_type)s on the ESX host local store "
+ "%(data_store_name)s to disk type %(disk_type)s") %
+ {"vmdk_file_size_in_kb": vmdk_file_size_in_kb,
+ "adapter_type": adapter_type,
+ "data_store_name": data_store_name,
+ "disk_type": disk_type},
instance=instance)
+ vmdk_copy_spec = vm_util.get_vmdk_create_spec(client_factory,
+ vmdk_file_size_in_kb, adapter_type,
+ disk_type)
+ vmdk_copy_task = self._session._call_method(
+ self._session._get_vim(),
+ "CopyVirtualDisk_Task",
+ service_content.virtualDiskManager,
+ sourceName=sparse_uploaded_vmdk_path,
+ sourceDatacenter=self._get_datacenter_ref_and_name()[0],
+ destName=uploaded_vmdk_path,
+ destSpec=vmdk_copy_spec)
+ self._session._wait_for_task(instance['uuid'], vmdk_copy_task)
+ LOG.debug(_("Copied Virtual Disk of size %(vmdk_file_size_in_kb)s"
+ " KB and type %(disk_type)s on "
+ "the ESX host local store %(data_store_name)s") %
+ {"vmdk_file_size_in_kb": vmdk_file_size_in_kb,
+ "disk_type": disk_type,
+ "data_store_name": data_store_name},
+ instance=instance)
- _attach_vmdk_to_the_vm()
+ ebs_root = self._volume_in_mapping(self._default_root_device,
+ block_device_info)
+
+ if not ebs_root:
+ linked_clone = CONF.use_linked_clone
+ if linked_clone:
+ upload_folder = self._instance_path_base
+ upload_name = instance['image_ref']
+ else:
+ upload_folder = instance['name']
+ upload_name = instance['name']
+
+ # The vmdk meta-data file
+ uploaded_vmdk_name = "%s/%s.vmdk" % (upload_folder, upload_name)
+ uploaded_vmdk_path = vm_util.build_datastore_path(data_store_name,
+ uploaded_vmdk_name)
+
+ if not (linked_clone and self._check_if_folder_file_exists(
+ data_store_ref, data_store_name,
+ upload_folder, upload_name + ".vmdk")):
+
+ # Naming the VM files in correspondence with the VM instance
+ # The flat vmdk file name
+ flat_uploaded_vmdk_name = "%s/%s-flat.vmdk" % (
+ upload_folder, upload_name)
+ # The sparse vmdk file name for sparse disk image
+ sparse_uploaded_vmdk_name = "%s/%s-sparse.vmdk" % (
+ upload_folder, upload_name)
+
+ flat_uploaded_vmdk_path = vm_util.build_datastore_path(
+ data_store_name,
+ flat_uploaded_vmdk_name)
+ sparse_uploaded_vmdk_path = vm_util.build_datastore_path(
+ data_store_name,
+ sparse_uploaded_vmdk_name)
+ dc_ref = self._get_datacenter_ref_and_name()[0]
+
+ if disk_type != "sparse":
+ # Create a flat virtual disk and retain the metadata file.
+ _create_virtual_disk()
+ _delete_disk_file(flat_uploaded_vmdk_path)
+
+ cookies = \
+ self._session._get_vim().client.options.transport.cookiejar
+ _fetch_image_on_esx_datastore()
+
+ if disk_type == "sparse":
+ # Copy the sparse virtual disk to a thin virtual disk.
+ disk_type = "thin"
+ _copy_virtual_disk()
+ _delete_disk_file(sparse_uploaded_vmdk_path)
+ else:
+ # linked clone base disk exists
+ if disk_type == "sparse":
+ disk_type = "thin"
+
+ # Attach the vmdk uploaded to the VM.
+ self._volumeops.attach_disk_to_vm(
+ vm_ref, instance,
+ adapter_type, disk_type, uploaded_vmdk_path,
+ vmdk_file_size_in_kb, linked_clone)
+ else:
+ # Attach the root disk to the VM.
+ root_disk = driver.block_device_info_get_mapping(
+ block_device_info)[0]
+ connection_info = root_disk['connection_info']
+ self._volumeops.attach_volume(connection_info, instance['name'],
+ self._default_root_device)
def _power_on_vm():
"""Power on the VM."""
@@ -349,9 +434,9 @@ class VMwareVMOps(object):
4. Now upload the -flat.vmdk file to the image store.
5. Delete the coalesced .vmdk and -flat.vmdk created.
"""
- vm_ref = self._get_vm_ref_from_the_name(instance.name)
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
if vm_ref is None:
- raise exception.InstanceNotFound(instance_id=instance.id)
+ raise exception.InstanceNotFound(instance_id=instance['uuid'])
client_factory = self._session._get_vim().client.factory
service_content = self._session._get_vim().get_service_content()
@@ -361,19 +446,19 @@ class VMwareVMOps(object):
hardware_devices = self._session._call_method(vim_util,
"get_dynamic_property", vm_ref,
"VirtualMachine", "config.hardware.device")
- _vmdk_info = vm_util.get_vmdk_file_path_and_adapter_type(
- client_factory, hardware_devices)
- vmdk_file_path_before_snapshot, adapter_type = _vmdk_info
+ (vmdk_file_path_before_snapshot, controller_key, adapter_type,
+ disk_type, unit_number) = vm_util.get_vmdk_path_and_adapter_type(
+ hardware_devices)
datastore_name = vm_util.split_datastore_path(
- vmdk_file_path_before_snapshot)[0]
+ vmdk_file_path_before_snapshot)[0]
os_type = self._session._call_method(vim_util,
"get_dynamic_property", vm_ref,
"VirtualMachine", "summary.config.guestId")
- return (vmdk_file_path_before_snapshot, adapter_type,
+ return (vmdk_file_path_before_snapshot, adapter_type, disk_type,
datastore_name, os_type)
- (vmdk_file_path_before_snapshot, adapter_type, datastore_name,
- os_type) = _get_vm_and_vmdk_attribs()
+ (vmdk_file_path_before_snapshot, adapter_type, disk_type,
+ datastore_name, os_type) = _get_vm_and_vmdk_attribs()
def _create_vm_snapshot():
# Create a snapshot of the VM
@@ -382,9 +467,9 @@ class VMwareVMOps(object):
snapshot_task = self._session._call_method(
self._session._get_vim(),
"CreateSnapshot_Task", vm_ref,
- name="%s-snapshot" % instance.name,
+ name="%s-snapshot" % instance['name'],
description="Taking Snapshot of the VM",
- memory=True,
+ memory=False,
quiesce=True)
self._session._wait_for_task(instance['uuid'], snapshot_task)
LOG.debug(_("Created Snapshot of the VM instance"),
@@ -424,13 +509,14 @@ class VMwareVMOps(object):
random_name = str(uuid.uuid4())
dest_vmdk_file_location = vm_util.build_datastore_path(datastore_name,
"vmware-tmp/%s.vmdk" % random_name)
- dc_ref = self._get_datacenter_name_and_ref()[0]
+ dc_ref = self._get_datacenter_ref_and_name()[0]
def _copy_vmdk_content():
# Copy the contents of the disk ( or disks, if there were snapshots
# done earlier) to a temporary vmdk file.
copy_spec = vm_util.get_copy_virtual_disk_spec(client_factory,
- adapter_type)
+ adapter_type,
+ disk_type)
LOG.debug(_('Copying disk data before snapshot of the VM'),
instance=instance)
copy_disk_task = self._session._call_method(
@@ -463,7 +549,7 @@ class VMwareVMOps(object):
adapter_type=adapter_type,
image_version=1,
host=self._session._host_ip,
- data_center_name=self._get_datacenter_name_and_ref()[1],
+ data_center_name=self._get_datacenter_ref_and_name()[1],
datastore_name=datastore_name,
cookies=cookies,
file_path="vmware-tmp/%s-flat.vmdk" % random_name)
@@ -496,9 +582,9 @@ class VMwareVMOps(object):
def reboot(self, instance, network_info):
"""Reboot a VM instance."""
- vm_ref = self._get_vm_ref_from_the_name(instance.name)
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
if vm_ref is None:
- raise exception.InstanceNotFound(instance_id=instance.id)
+ raise exception.InstanceNotFound(instance_id=instance['uuid'])
self.plug_vifs(instance, network_info)
@@ -539,6 +625,38 @@ class VMwareVMOps(object):
self._session._wait_for_task(instance['uuid'], reset_task)
LOG.debug(_("Did hard reboot of VM"), instance=instance)
+ def _delete(self, instance, network_info):
+ """
+ Destroy a VM instance. Steps followed are:
+ 1. Power off the VM, if it is in poweredOn state.
+ 2. Destroy the VM.
+ """
+ try:
+ vm_ref = vm_util.get_vm_ref_from_name(self._session,
+ instance['name'])
+ if vm_ref is None:
+ LOG.debug(_("instance not present"), instance=instance)
+ return
+
+ self.power_off(instance)
+
+ try:
+ LOG.debug(_("Destroying the VM"), instance=instance)
+ destroy_task = self._session._call_method(
+ self._session._get_vim(),
+ "Destroy_Task", vm_ref)
+ self._session._wait_for_task(instance['uuid'], destroy_task)
+ LOG.debug(_("Destroyed the VM"), instance=instance)
+ except Exception, excep:
+ LOG.warn(_("In vmwareapi:vmops:delete, got this exception"
+ " while destroying the VM: %s") % str(excep),
+ instance=instance)
+
+ if network_info:
+ self.unplug_vifs(instance, network_info)
+ except Exception, exc:
+ LOG.exception(exc, instance=instance)
+
def destroy(self, instance, network_info, destroy_disks=True):
"""
Destroy a VM instance. Steps followed are:
@@ -547,7 +665,8 @@ class VMwareVMOps(object):
3. Delete the contents of the folder holding the VM related data.
"""
try:
- vm_ref = self._get_vm_ref_from_the_name(instance.name)
+ vm_ref = vm_util.get_vm_ref_from_name(self._session,
+ instance['name'])
if vm_ref is None:
LOG.debug(_("instance not present"), instance=instance)
return
@@ -579,14 +698,15 @@ class VMwareVMOps(object):
try:
LOG.debug(_("Unregistering the VM"), instance=instance)
self._session._call_method(self._session._get_vim(),
- "UnregisterVM", vm_ref)
+ "UnregisterVM", vm_ref)
LOG.debug(_("Unregistered the VM"), instance=instance)
except Exception, excep:
LOG.warn(_("In vmwareapi:vmops:destroy, got this exception"
" while un-registering the VM: %s") % str(excep),
instance=instance)
- self.unplug_vifs(instance, network_info)
+ if network_info:
+ self.unplug_vifs(instance, network_info)
# Delete the folder holding the VM related content on
# the datastore.
@@ -604,7 +724,8 @@ class VMwareVMOps(object):
vim,
"DeleteDatastoreFile_Task",
vim.get_service_content().fileManager,
- name=dir_ds_compliant_path)
+ name=dir_ds_compliant_path,
+ datacenter=self._get_datacenter_ref_and_name()[0])
self._session._wait_for_task(instance['uuid'], delete_task)
LOG.debug(_("Deleted contents of the VM from "
"datastore %(datastore_name)s") %
@@ -629,9 +750,9 @@ class VMwareVMOps(object):
def suspend(self, instance):
"""Suspend the specified instance."""
- vm_ref = self._get_vm_ref_from_the_name(instance.name)
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
if vm_ref is None:
- raise exception.InstanceNotFound(instance_id=instance.id)
+ raise exception.InstanceNotFound(instance_id=instance['uuid'])
pwr_state = self._session._call_method(vim_util,
"get_dynamic_property", vm_ref,
@@ -645,17 +766,17 @@ class VMwareVMOps(object):
LOG.debug(_("Suspended the VM"), instance=instance)
# Raise Exception if VM is poweredOff
elif pwr_state == "poweredOff":
- reason = _("instance is powered off and can not be suspended.")
+ reason = _("instance is powered off and cannot be suspended.")
raise exception.InstanceSuspendFailure(reason=reason)
-
- LOG.debug(_("VM was already in suspended state. So returning "
- "without doing anything"), instance=instance)
+ else:
+ LOG.debug(_("VM was already in suspended state. So returning "
+ "without doing anything"), instance=instance)
def resume(self, instance):
"""Resume the specified instance."""
- vm_ref = self._get_vm_ref_from_the_name(instance.name)
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
if vm_ref is None:
- raise exception.InstanceNotFound(instance_id=instance.id)
+ raise exception.InstanceNotFound(instance_id=instance['uuid'])
pwr_state = self._session._call_method(vim_util,
"get_dynamic_property", vm_ref,
@@ -671,9 +792,263 @@ class VMwareVMOps(object):
reason = _("instance is not in a suspended state")
raise exception.InstanceResumeFailure(reason=reason)
+ def rescue(self, context, instance, network_info, image_meta):
+ """Rescue the specified instance.
+
+ - shutdown the instance VM.
+ - spawn a rescue VM (the vm name-label will be instance-N-rescue).
+
+ """
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
+ if vm_ref is None:
+ raise exception.InstanceNotFound(instance_id=instance['uuid'])
+
+ self.power_off(instance)
+ instance['name'] = instance['name'] + self._rescue_suffix
+ self.spawn(context, instance, image_meta, network_info)
+
+ # Attach vmdk to the rescue VM
+ hardware_devices = self._session._call_method(vim_util,
+ "get_dynamic_property", vm_ref,
+ "VirtualMachine", "config.hardware.device")
+ vmdk_path, controller_key, adapter_type, disk_type, unit_number \
+ = vm_util.get_vmdk_path_and_adapter_type(hardware_devices)
+ # Figure out the correct unit number
+ unit_number = unit_number + 1
+ rescue_vm_ref = vm_util.get_vm_ref_from_name(self._session,
+ instance['name'])
+ self._volumeops.attach_disk_to_vm(
+ rescue_vm_ref, instance,
+ adapter_type, disk_type, vmdk_path,
+ controller_key=controller_key,
+ unit_number=unit_number)
+
+ def unrescue(self, instance):
+ """Unrescue the specified instance."""
+ instance_orig_name = instance['name']
+ instance['name'] = instance['name'] + self._rescue_suffix
+ self.destroy(instance, None)
+ instance['name'] = instance_orig_name
+ self.power_on(instance)
+
+ def power_off(self, instance):
+ """Power off the specified instance."""
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
+ if vm_ref is None:
+ raise exception.InstanceNotFound(instance_id=instance['uuid'])
+
+ pwr_state = self._session._call_method(vim_util,
+ "get_dynamic_property", vm_ref,
+ "VirtualMachine", "runtime.powerState")
+ # Only PoweredOn VMs can be powered off.
+ if pwr_state == "poweredOn":
+ LOG.debug(_("Powering off the VM"), instance=instance)
+ poweroff_task = self._session._call_method(
+ self._session._get_vim(),
+ "PowerOffVM_Task", vm_ref)
+ self._session._wait_for_task(instance['uuid'], poweroff_task)
+ LOG.debug(_("Powered off the VM"), instance=instance)
+ # Raise Exception if VM is suspended
+ elif pwr_state == "suspended":
+ reason = _("instance is suspended and cannot be powered off.")
+ raise exception.InstancePowerOffFailure(reason=reason)
+ else:
+ LOG.debug(_("VM was already in powered off state. So returning "
+ "without doing anything"), instance=instance)
+
+ def power_on(self, instance):
+ """Power on the specified instance."""
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
+ if vm_ref is None:
+ raise exception.InstanceNotFound(instance_id=instance['uuid'])
+
+ pwr_state = self._session._call_method(vim_util,
+ "get_dynamic_property", vm_ref,
+ "VirtualMachine", "runtime.powerState")
+ if pwr_state == "poweredOn":
+ LOG.debug(_("VM was already in powered on state. So returning "
+ "without doing anything"), instance=instance)
+ # Only PoweredOff and Suspended VMs can be powered on.
+ else:
+ LOG.debug(_("Powering on the VM"), instance=instance)
+ poweron_task = self._session._call_method(
+ self._session._get_vim(),
+ "PowerOnVM_Task", vm_ref)
+ self._session._wait_for_task(instance['uuid'], poweron_task)
+ LOG.debug(_("Powered on the VM"), instance=instance)
+
+ def _get_orig_vm_name_label(self, instance):
+ return instance['name'] + '-orig'
+
+ def _update_instance_progress(self, context, instance, step, total_steps):
+ """Update instance progress percent to reflect current step number
+ """
+ # Divide the action's workflow into discrete steps and "bump" the
+ # instance's progress field as each step is completed.
+ #
+ # For a first cut this should be fine, however, for large VM images,
+ # the clone disk step begins to dominate the equation. A
+ # better approximation would use the percentage of the VM image that
+ # has been streamed to the destination host.
+ progress = round(float(step) / total_steps * 100)
+ instance_uuid = instance['uuid']
+ LOG.debug(_("Updating instance '%(instance_uuid)s' progress to"
+ " %(progress)d") % locals(), instance=instance)
+ self._virtapi.instance_update(context, instance_uuid,
+ {'progress': progress})
+
+ def migrate_disk_and_power_off(self, context, instance, dest,
+ instance_type):
+ """
+ Transfers the disk of a running instance in multiple phases, turning
+ off the instance before the end.
+ """
+ # 0. Zero out the progress to begin
+ self._update_instance_progress(context, instance,
+ step=0,
+ total_steps=RESIZE_TOTAL_STEPS)
+
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
+ if vm_ref is None:
+ raise exception.InstanceNotFound(instance_id=instance['name'])
+ host_ref = self._get_host_ref_from_name(dest)
+ if host_ref is None:
+ raise exception.HostNotFound(host=dest)
+
+ # 1. Power off the instance
+ self.power_off(instance)
+ self._update_instance_progress(context, instance,
+ step=1,
+ total_steps=RESIZE_TOTAL_STEPS)
+
+ # 2. Rename the original VM with suffix '-orig'
+ name_label = self._get_orig_vm_name_label(instance)
+ LOG.debug(_("Renaming the VM to %s") % name_label,
+ instance=instance)
+ rename_task = self._session._call_method(
+ self._session._get_vim(),
+ "Rename_Task", vm_ref, newName=name_label)
+ self._session._wait_for_task(instance['uuid'], rename_task)
+ LOG.debug(_("Renamed the VM to %s") % name_label,
+ instance=instance)
+ self._update_instance_progress(context, instance,
+ step=2,
+ total_steps=RESIZE_TOTAL_STEPS)
+
+ # Get the clone vm spec
+ ds_ref = vm_util.get_datastore_ref_and_name(self._session)[0]
+ client_factory = self._session._get_vim().client.factory
+ rel_spec = vm_util.relocate_vm_spec(client_factory, ds_ref, host_ref)
+ clone_spec = vm_util.clone_vm_spec(client_factory, rel_spec)
+ vm_folder_ref, res_pool_ref = self._get_vmfolder_and_res_pool_refs()
+
+ # 3. Clone VM on ESX host
+ LOG.debug(_("Cloning VM to host %s") % dest, instance=instance)
+ vm_clone_task = self._session._call_method(
+ self._session._get_vim(),
+ "CloneVM_Task", vm_ref,
+ folder=vm_folder_ref,
+ name=instance['name'],
+ spec=clone_spec)
+ self._session._wait_for_task(instance['uuid'], vm_clone_task)
+ LOG.debug(_("Cloned VM to host %s") % dest, instance=instance)
+ self._update_instance_progress(context, instance,
+ step=3,
+ total_steps=RESIZE_TOTAL_STEPS)
+
+ def confirm_migration(self, migration, instance, network_info):
+ """Confirms a resize, destroying the source VM."""
+ instance_name = self._get_orig_vm_name_label(instance)
+ # Destroy the original VM.
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance_name)
+ if vm_ref is None:
+ LOG.debug(_("instance not present"), instance=instance)
+ return
+
+ try:
+ LOG.debug(_("Destroying the VM"), instance=instance)
+ destroy_task = self._session._call_method(
+ self._session._get_vim(),
+ "Destroy_Task", vm_ref)
+ self._session._wait_for_task(instance['uuid'], destroy_task)
+ LOG.debug(_("Destroyed the VM"), instance=instance)
+ except Exception, excep:
+ LOG.warn(_("In vmwareapi:vmops:confirm_migration, got this "
+ "exception while destroying the VM: %s") % str(excep))
+
+ if network_info:
+ self.unplug_vifs(instance, network_info)
+
+ def finish_revert_migration(self, instance):
+ """Finish reverting a resize, powering back on the instance."""
+ # The original vm was suffixed with '-orig'; find it using
+ # the old suffix, remove the suffix, then power it back on.
+ name_label = self._get_orig_vm_name_label(instance)
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, name_label)
+ if vm_ref is None:
+ raise exception.InstanceNotFound(instance_id=name_label)
+
+ LOG.debug(_("Renaming the VM from %s") % name_label,
+ instance=instance)
+ rename_task = self._session._call_method(
+ self._session._get_vim(),
+ "Rename_Task", vm_ref, newName=instance['name'])
+ self._session._wait_for_task(instance['uuid'], rename_task)
+ LOG.debug(_("Renamed the VM from %s") % name_label,
+ instance=instance)
+ self.power_on(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."""
+ # 4. Start VM
+ self.power_on(instance)
+ self._update_instance_progress(context, instance,
+ step=4,
+ total_steps=RESIZE_TOTAL_STEPS)
+
+ def live_migration(self, context, instance_ref, dest,
+ post_method, recover_method, block_migration=False):
+ """Spawning live_migration operation for distributing high-load."""
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance_ref.name)
+ if vm_ref is None:
+ raise exception.InstanceNotFound(instance_id=instance_ref.name)
+ host_ref = self._get_host_ref_from_name(dest)
+ if host_ref is None:
+ raise exception.HostNotFound(host=dest)
+
+ LOG.debug(_("Migrating VM to host %s") % dest, instance=instance_ref)
+ try:
+ vm_migrate_task = self._session._call_method(
+ self._session._get_vim(),
+ "MigrateVM_Task", vm_ref,
+ host=host_ref,
+ priority="defaultPriority")
+ self._session._wait_for_task(instance_ref['uuid'], vm_migrate_task)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ recover_method(context, instance_ref, dest, block_migration)
+ post_method(context, instance_ref, dest, block_migration)
+ LOG.debug(_("Migrated VM to host %s") % dest, instance=instance_ref)
+
+ def poll_rebooting_instances(self, timeout, instances):
+ """Poll for rebooting instances."""
+ ctxt = nova_context.get_admin_context()
+
+ instances_info = dict(instance_count=len(instances),
+ timeout=timeout)
+
+ if instances_info["instance_count"] > 0:
+ LOG.info(_("Found %(instance_count)d hung reboots "
+ "older than %(timeout)d seconds") % instances_info)
+
+ for instance in instances:
+ LOG.info(_("Automatically hard rebooting %d") % instance['uuid'])
+ self.compute_api.reboot(ctxt, instance, "HARD")
+
def get_info(self, instance):
"""Return data about the VM instance."""
- vm_ref = self._get_vm_ref_from_the_name(instance['name'])
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
if vm_ref is None:
raise exception.InstanceNotFound(instance_id=instance['name'])
@@ -709,9 +1084,9 @@ class VMwareVMOps(object):
def get_console_output(self, instance):
"""Return snapshot of console."""
- vm_ref = self._get_vm_ref_from_the_name(instance.name)
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
if vm_ref is None:
- raise exception.InstanceNotFound(instance_id=instance.id)
+ raise exception.InstanceNotFound(instance_id=instance['uuid'])
param_list = {"id": str(vm_ref)}
base_url = "%s://%s/screen?%s" % (self._session._scheme,
self._session._host_ip,
@@ -728,14 +1103,32 @@ class VMwareVMOps(object):
else:
return ""
+ def get_vnc_console(self, instance):
+ """Return connection info for a vnc console."""
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
+ if vm_ref is None:
+ raise exception.InstanceNotFound(instance_id=instance['uuid'])
+
+ return {'host': CONF.vmwareapi_host_ip,
+ 'port': self._get_vnc_port(vm_ref),
+ 'internal_access_path': None}
+
+ @staticmethod
+ def _get_vnc_port(vm_ref):
+ """Return VNC port for an VM."""
+ vm_id = int(vm_ref.value.replace('vm-', ''))
+ port = CONF.vnc_port + vm_id % CONF.vnc_port_total
+
+ return port
+
def _set_machine_id(self, client_factory, instance, network_info):
"""
Set the machine id of the VM for guest tools to pick up and reconfigure
the network interfaces.
"""
- vm_ref = self._get_vm_ref_from_the_name(instance.name)
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
if vm_ref is None:
- raise exception.InstanceNotFound(instance_id=instance.id)
+ raise exception.InstanceNotFound(instance_id=instance['uuid'])
machine_id_str = ''
for (network, info) in network_info:
@@ -763,24 +1156,66 @@ class VMwareVMOps(object):
client_factory, machine_id_str)
LOG.debug(_("Reconfiguring VM instance to set the machine id "
- "with ip - %(ip_addr)s") %
- {'ip_addr': ip_v4['ip']},
+ "with ip - %(ip_addr)s") % {'ip_addr': ip_v4['ip']},
instance=instance)
reconfig_task = self._session._call_method(self._session._get_vim(),
"ReconfigVM_Task", vm_ref,
spec=machine_id_change_spec)
self._session._wait_for_task(instance['uuid'], reconfig_task)
LOG.debug(_("Reconfigured VM instance to set the machine id "
- "with ip - %(ip_addr)s") %
- {'ip_addr': ip_v4['ip']},
+ "with ip - %(ip_addr)s") % {'ip_addr': ip_v4['ip']},
instance=instance)
- def _get_datacenter_name_and_ref(self):
+ def _set_vnc_config(self, client_factory, instance, port, password):
+ """
+ Set the vnc configuration of the VM.
+ """
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance['name'])
+ if vm_ref is None:
+ raise exception.InstanceNotFound(instance_id=instance['uuid'])
+
+ vnc_config_spec = vm_util.get_vnc_config_spec(
+ client_factory, port, password)
+
+ LOG.debug(_("Reconfiguring VM instance to enable vnc on "
+ "port - %(port)s") % {'port': port},
+ instance=instance)
+ reconfig_task = self._session._call_method(self._session._get_vim(),
+ "ReconfigVM_Task", vm_ref,
+ spec=vnc_config_spec)
+ self._session._wait_for_task(instance['uuid'], reconfig_task)
+ LOG.debug(_("Reconfigured VM instance to enable vnc on "
+ "port - %(port)s") % {'port': port},
+ instance=instance)
+
+ def _get_datacenter_ref_and_name(self):
"""Get the datacenter name and the reference."""
dc_obj = self._session._call_method(vim_util, "get_objects",
"Datacenter", ["name"])
return dc_obj[0].obj, dc_obj[0].propSet[0].val
+ def _get_host_ref_from_name(self, host_name):
+ """Get reference to the host with the name specified."""
+ host_objs = self._session._call_method(vim_util, "get_objects",
+ "HostSystem", ["name"])
+ for host in host_objs:
+ if host.propSet[0].val == host_name:
+ return host.obj
+ return None
+
+ def _get_vmfolder_and_res_pool_refs(self):
+ """Get the Vm folder ref from the datacenter."""
+ dc_objs = self._session._call_method(vim_util, "get_objects",
+ "Datacenter", ["vmFolder"])
+ # There is only one default datacenter in a standalone ESX host
+ vm_folder_ref = dc_objs[0].propSet[0].val
+
+ # Get the resource pool. Taking the first resource pool coming our
+ # way. Assuming that is the default resource pool.
+ res_pool_ref = self._session._call_method(vim_util, "get_objects",
+ "ResourcePool")[0].obj
+ return vm_folder_ref, res_pool_ref
+
def _path_exists(self, ds_browser, ds_path):
"""Check if the path exists on the datastore."""
search_task = self._session._call_method(self._session._get_vim(),
@@ -801,6 +1236,32 @@ class VMwareVMOps(object):
return False
return True
+ def _path_file_exists(self, ds_browser, ds_path, file_name):
+ """Check if the path and file exists on the datastore."""
+ client_factory = self._session._get_vim().client.factory
+ search_spec = vm_util.search_datastore_spec(client_factory, file_name)
+ search_task = self._session._call_method(self._session._get_vim(),
+ "SearchDatastore_Task",
+ ds_browser,
+ datastorePath=ds_path,
+ searchSpec=search_spec)
+ # Wait till the state changes from queued or running.
+ # If an error state is returned, it means that the path doesn't exist.
+ while True:
+ task_info = self._session._call_method(vim_util,
+ "get_dynamic_property",
+ search_task, "Task", "info")
+ if task_info.state in ['queued', 'running']:
+ time.sleep(2)
+ continue
+ break
+ if task_info.state == "error":
+ return False, False
+
+ file_exists = (getattr(task_info.result, 'file', False) and
+ task_info.result.file[0].path == file_name)
+ return True, file_exists
+
def _mkdir(self, ds_path):
"""
Creates a directory at the path specified. If it is just "NAME",
@@ -813,14 +1274,30 @@ class VMwareVMOps(object):
name=ds_path, createParentDirectories=False)
LOG.debug(_("Created directory with path %s") % ds_path)
- def _get_vm_ref_from_the_name(self, vm_name):
- """Get reference to the VM with the name specified."""
- vms = self._session._call_method(vim_util, "get_objects",
- "VirtualMachine", ["name"])
- for vm in vms:
- if vm.propSet[0].val == vm_name:
- return vm.obj
- return None
+ def _check_if_folder_file_exists(self, ds_ref, ds_name,
+ folder_name, file_name):
+ ds_browser = vim_util.get_dynamic_property(
+ self._session._get_vim(),
+ ds_ref,
+ "Datastore",
+ "browser")
+ # Check if the folder exists or not. If not, create one
+ # Check if the file exists or not.
+ folder_path = vm_util.build_datastore_path(ds_name, folder_name)
+ folder_exists, file_exists = self._path_file_exists(ds_browser,
+ folder_path,
+ file_name)
+ if not folder_exists:
+ self._mkdir(vm_util.build_datastore_path(ds_name, folder_name))
+
+ return file_exists
+
+ def inject_network_info(self, instance, network_info):
+ """inject network info for specified instance."""
+ # Set the machine.id parameter of the instance to inject
+ # the NIC configuration inside the VM
+ client_factory = self._session._get_vim().client.factory
+ self._set_machine_id(client_factory, instance, network_info)
def plug_vifs(self, instance, network_info):
"""Plug VIFs into networks."""
@@ -829,3 +1306,46 @@ class VMwareVMOps(object):
def unplug_vifs(self, instance, network_info):
"""Unplug VIFs from networks."""
pass
+
+ def list_interfaces(self, instance_name):
+ """
+ Return the IDs of all the virtual network interfaces attached to the
+ specified instance, as a list. These IDs are opaque to the caller
+ (they are only useful for giving back to this layer as a parameter to
+ interface_stats). These IDs only need to be unique for a given
+ instance.
+ """
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance_name)
+ if vm_ref is None:
+ raise exception.InstanceNotFound(instance_id=instance_name)
+
+ interfaces = []
+ # Get the virtual network interfaces attached to the VM
+ hardware_devices = self._session._call_method(vim_util,
+ "get_dynamic_property", vm_ref,
+ "VirtualMachine", "config.hardware.device")
+
+ for device in hardware_devices:
+ if device.__class__.__name__ in ["VirtualE1000", "VirtualE1000e",
+ "VirtualPCNet32", "VirtualVmxnet"]:
+ interfaces.append(device.key)
+
+ return interfaces
+
+ @staticmethod
+ def _volume_in_mapping(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
diff --git a/nova/virt/vmwareapi/vmware_images.py b/nova/virt/vmwareapi/vmware_images.py
index 7c4480ea0..e8510b36e 100644
--- a/nova/virt/vmwareapi/vmware_images.py
+++ b/nova/virt/vmwareapi/vmware_images.py
@@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
+# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack LLC.
#
@@ -17,7 +18,6 @@
"""
Utility functions for Image transfer.
"""
-import StringIO
from nova import exception
from nova.image import glance
@@ -56,7 +56,7 @@ def start_transfer(context, read_file_handle, data_size,
write_thread = io_util.IOThread(thread_safe_pipe, write_file_handle)
# In case of VMware - Glance transfer, we relinquish VMware HTTP file read
# handle to Glance Client instance, but to be sure of the transfer we need
- # to be sure of the status of the image on glnace changing to active.
+ # to be sure of the status of the image on glance changing to active.
# The GlanceWriteThread handles the same for us.
elif image_service and image_id:
write_thread = io_util.GlanceWriteThread(context, thread_safe_pipe,
@@ -93,9 +93,8 @@ def fetch_image(context, image, instance, **kwargs):
(image_service, image_id) = glance.get_remote_image_service(context, image)
metadata = image_service.show(context, image_id)
file_size = int(metadata['size'])
- f = StringIO.StringIO()
- image_service.download(context, image_id, f)
- read_file_handle = read_write_util.GlanceFileRead(f)
+ read_iter = image_service.download(context, image_id)
+ read_file_handle = read_write_util.GlanceFileRead(read_iter)
write_file_handle = read_write_util.VMwareHTTPWriteFile(
kwargs.get("host"),
kwargs.get("data_center_name"),
@@ -122,10 +121,9 @@ def upload_image(context, image, instance, **kwargs):
file_size = read_file_handle.get_size()
(image_service, image_id) = glance.get_remote_image_service(context, image)
# The properties and other fields that we need to set for the image.
- image_metadata = {"is_public": True,
- "disk_format": "vmdk",
+ image_metadata = {"disk_format": "vmdk",
"container_format": "bare",
- "type": "vmdk",
+ "size": file_size,
"properties": {"vmware_adaptertype":
kwargs.get("adapter_type"),
"vmware_ostype": kwargs.get("os_type"),
diff --git a/nova/virt/vmwareapi/volume_util.py b/nova/virt/vmwareapi/volume_util.py
new file mode 100644
index 000000000..2af3381a4
--- /dev/null
+++ b/nova/virt/vmwareapi/volume_util.py
@@ -0,0 +1,177 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2012 VMware, Inc.
+#
+# 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 re
+import string
+
+from nova.openstack.common import log as logging
+from nova.virt.vmwareapi import vim_util
+
+LOG = logging.getLogger(__name__)
+
+
+class StorageError(Exception):
+ """To raise errors related to Volume commands."""
+
+ def __init__(self, message=None):
+ super(StorageError, self).__init__(message)
+
+
+def get_host_iqn(session):
+ """
+ Return the host iSCSI IQN.
+ """
+ host_mor = session._call_method(vim_util, "get_objects",
+ "HostSystem")[0].obj
+ hbas_ret = session._call_method(vim_util, "get_dynamic_property",
+ host_mor, "HostSystem",
+ "config.storageDevice.hostBusAdapter")
+
+ # Meaning there are no host bus adapters on the host
+ if not hbas_ret:
+ return
+ host_hbas = hbas_ret.HostHostBusAdapter
+ for hba in host_hbas:
+ if hba.__class__.__name__ == 'HostInternetScsiHba':
+ return hba.iScsiName
+
+
+def find_st(session, data):
+ """
+ Return the iSCSI Target given a volume info.
+ """
+ target_portal = data['target_portal']
+ target_iqn = data['target_iqn']
+ host_mor = session._call_method(vim_util, "get_objects",
+ "HostSystem")[0].obj
+
+ lst_properties = ["config.storageDevice.hostBusAdapter",
+ "config.storageDevice.scsiTopology",
+ "config.storageDevice.scsiLun"]
+ props = session._call_method(vim_util, "get_object_properties",
+ None, host_mor, "HostSystem",
+ lst_properties)
+ result = (None, None)
+ hbas_ret = None
+ scsi_topology = None
+ scsi_lun_ret = None
+ for elem in props:
+ for prop in elem.propSet:
+ if prop.name == "config.storageDevice.hostBusAdapter":
+ hbas_ret = prop.val
+ elif prop.name == "config.storageDevice.scsiTopology":
+ scsi_topology = prop.val
+ elif prop.name == "config.storageDevice.scsiLun":
+ scsi_lun_ret = prop.val
+
+ # Meaning there are no host bus adapters on the host
+ if hbas_ret is None:
+ return result
+ host_hbas = hbas_ret.HostHostBusAdapter
+ if not host_hbas:
+ return result
+ for hba in host_hbas:
+ if hba.__class__.__name__ == 'HostInternetScsiHba':
+ hba_key = hba.key
+ break
+ else:
+ return result
+
+ if scsi_topology is None:
+ return result
+ host_adapters = scsi_topology.adapter
+ if not host_adapters:
+ return result
+ scsi_lun_key = None
+ for adapter in host_adapters:
+ if adapter.adapter == hba_key:
+ if not getattr(adapter, 'target', None):
+ return result
+ for target in adapter.target:
+ if (getattr(target.transport, 'address', None) and
+ target.transport.address[0] == target_portal and
+ target.transport.iScsiName == target_iqn):
+ if not target.lun:
+ return result
+ for lun in target.lun:
+ if 'host.ScsiDisk' in lun.scsiLun:
+ scsi_lun_key = lun.scsiLun
+ break
+ break
+ break
+
+ if scsi_lun_key is None:
+ return result
+
+ if scsi_lun_ret is None:
+ return result
+ host_scsi_luns = scsi_lun_ret.ScsiLun
+ if not host_scsi_luns:
+ return result
+ for scsi_lun in host_scsi_luns:
+ if scsi_lun.key == scsi_lun_key:
+ return (scsi_lun.deviceName, scsi_lun.uuid)
+
+ return result
+
+
+def rescan_iscsi_hba(session):
+ """
+ Rescan the iSCSI HBA to discover iSCSI targets.
+ """
+ # There is only one default storage system in a standalone ESX host
+ storage_system_mor = session._call_method(vim_util, "get_objects",
+ "HostSystem", ["configManager.storageSystem"])[0].propSet[0].val
+ hbas_ret = session._call_method(vim_util,
+ "get_dynamic_property",
+ storage_system_mor,
+ "HostStorageSystem",
+ "storageDeviceInfo.hostBusAdapter")
+ # Meaning there are no host bus adapters on the host
+ if hbas_ret is None:
+ return
+ host_hbas = hbas_ret.HostHostBusAdapter
+ if not host_hbas:
+ return
+ for hba in host_hbas:
+ if hba.__class__.__name__ == 'HostInternetScsiHba':
+ hba_device = hba.device
+ break
+ else:
+ return
+
+ LOG.debug(_("Rescanning HBA %s") % hba_device)
+ session._call_method(session._get_vim(), "RescanHba", storage_system_mor,
+ hbaDevice=hba_device)
+ LOG.debug(_("Rescanned HBA %s ") % hba_device)
+
+
+def mountpoint_to_number(mountpoint):
+ """Translate a mountpoint like /dev/sdc into a numeric."""
+ if mountpoint.startswith('/dev/'):
+ mountpoint = mountpoint[5:]
+ if re.match('^[hsv]d[a-p]$', mountpoint):
+ return (ord(mountpoint[2:3]) - ord('a'))
+ elif re.match('^[0-9]+$', mountpoint):
+ return string.atoi(mountpoint, 10)
+ else:
+ LOG.warn(_("Mountpoint cannot be translated: %s") % mountpoint)
+ return -1
diff --git a/nova/virt/vmwareapi/volumeops.py b/nova/virt/vmwareapi/volumeops.py
new file mode 100644
index 000000000..922d2135b
--- /dev/null
+++ b/nova/virt/vmwareapi/volumeops.py
@@ -0,0 +1,183 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2012 VMware, Inc.
+#
+# 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).
+"""
+
+from nova import exception
+from nova.openstack.common import cfg
+from nova.openstack.common import log as logging
+from nova.virt.vmwareapi import vim_util
+from nova.virt.vmwareapi import vm_util
+from nova.virt.vmwareapi import volume_util
+
+
+CONF = cfg.CONF
+LOG = logging.getLogger(__name__)
+
+
+class VMwareVolumeOps(object):
+ """
+ Management class for Volume-related tasks
+ """
+
+ def __init__(self, session):
+ self._session = session
+
+ def attach_disk_to_vm(self, vm_ref, instance_name,
+ adapter_type, disk_type, vmdk_path=None,
+ disk_size=None, linked_clone=False,
+ controller_key=None, unit_number=None,
+ device_name=None):
+ """
+ Attach disk to VM by reconfiguration.
+ """
+ client_factory = self._session._get_vim().client.factory
+ vmdk_attach_config_spec = vm_util.get_vmdk_attach_config_spec(
+ client_factory, adapter_type, disk_type,
+ vmdk_path, disk_size, linked_clone,
+ controller_key, unit_number, device_name)
+
+ LOG.debug(_("Reconfiguring VM instance %(instance_name)s to attach "
+ "disk %(vmdk_path)s or device %(device_name)s with type "
+ "%(disk_type)s") % locals())
+ reconfig_task = self._session._call_method(
+ self._session._get_vim(),
+ "ReconfigVM_Task", vm_ref,
+ spec=vmdk_attach_config_spec)
+ self._session._wait_for_task(instance_name, reconfig_task)
+ LOG.debug(_("Reconfigured VM instance %(instance_name)s to attach "
+ "disk %(vmdk_path)s or device %(device_name)s with type "
+ "%(disk_type)s") % locals())
+
+ def detach_disk_from_vm(self, vm_ref, instance_name, device):
+ """
+ Detach disk from VM by reconfiguration.
+ """
+ client_factory = self._session._get_vim().client.factory
+ vmdk_detach_config_spec = vm_util.get_vmdk_detach_config_spec(
+ client_factory, device)
+ disk_key = device.key
+ LOG.debug(_("Reconfiguring VM instance %(instance_name)s to detach "
+ "disk %(disk_key)s") % locals())
+ reconfig_task = self._session._call_method(
+ self._session._get_vim(),
+ "ReconfigVM_Task", vm_ref,
+ spec=vmdk_detach_config_spec)
+ self._session._wait_for_task(instance_name, reconfig_task)
+ LOG.debug(_("Reconfigured VM instance %(instance_name)s to detach "
+ "disk %(disk_key)s") % locals())
+
+ def discover_st(self, data):
+ """Discover iSCSI targets."""
+ target_portal = data['target_portal']
+ target_iqn = data['target_iqn']
+ LOG.debug(_("Discovering iSCSI target %(target_iqn)s from "
+ "%(target_portal)s.") % locals())
+ device_name, uuid = volume_util.find_st(self._session, data)
+ if device_name:
+ LOG.debug(_("Storage target found. No need to discover"))
+ return (device_name, uuid)
+ # Rescan iSCSI HBA
+ volume_util.rescan_iscsi_hba(self._session)
+ # Find iSCSI Target again
+ device_name, uuid = volume_util.find_st(self._session, data)
+ if device_name:
+ LOG.debug(_("Discovered iSCSI target %(target_iqn)s from "
+ "%(target_portal)s.") % locals())
+ else:
+ LOG.debug(_("Unable to discovered iSCSI target %(target_iqn)s "
+ "from %(target_portal)s.") % locals())
+ return (device_name, uuid)
+
+ def get_volume_connector(self, instance):
+ """Return volume connector information."""
+ iqn = volume_util.get_host_iqn(self._session)
+ return {
+ 'ip': CONF.vmwareapi_host_ip,
+ 'initiator': iqn,
+ 'host': CONF.vmwareapi_host_ip
+ }
+
+ def attach_volume(self, connection_info, instance, mountpoint):
+ """Attach volume storage to VM instance."""
+ instance_name = instance['name']
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance_name)
+ if vm_ref is None:
+ raise exception.InstanceNotFound(instance_id=instance_name)
+ # Attach Volume to VM
+ LOG.debug(_("Attach_volume: %(connection_info)s, %(instance_name)s, "
+ "%(mountpoint)s") % locals())
+ driver_type = connection_info['driver_volume_type']
+ if driver_type not in ['iscsi']:
+ raise exception.VolumeDriverNotFound(driver_type=driver_type)
+ data = connection_info['data']
+ mount_unit = volume_util.mountpoint_to_number(mountpoint)
+
+ # Discover iSCSI Target
+ device_name, uuid = self.discover_st(data)
+ if device_name is None:
+ raise volume_util.StorageError(_("Unable to find iSCSI Target"))
+
+ # Get the vmdk file name that the VM is pointing to
+ hardware_devices = self._session._call_method(vim_util,
+ "get_dynamic_property", vm_ref,
+ "VirtualMachine", "config.hardware.device")
+ vmdk_file_path, controller_key, adapter_type, disk_type, unit_number \
+ = vm_util.get_vmdk_path_and_adapter_type(hardware_devices)
+ # Figure out the correct unit number
+ if unit_number < mount_unit:
+ unit_number = mount_unit
+ else:
+ unit_number = unit_number + 1
+ self.attach_disk_to_vm(vm_ref, instance_name,
+ adapter_type, disk_type="rdmp",
+ controller_key=controller_key,
+ unit_number=unit_number,
+ device_name=device_name)
+ LOG.info(_("Mountpoint %(mountpoint)s attached to "
+ "instance %(instance_name)s") % locals())
+
+ def detach_volume(self, connection_info, instance, mountpoint):
+ """Detach volume storage to VM instance."""
+ instance_name = instance['name']
+ vm_ref = vm_util.get_vm_ref_from_name(self._session, instance_name)
+ if vm_ref is None:
+ raise exception.InstanceNotFound(instance_id=instance_name)
+ # Detach Volume from VM
+ LOG.debug(_("Detach_volume: %(instance_name)s, %(mountpoint)s")
+ % locals())
+ driver_type = connection_info['driver_volume_type']
+ if driver_type not in ['iscsi']:
+ raise exception.VolumeDriverNotFound(driver_type=driver_type)
+ data = connection_info['data']
+
+ # Discover iSCSI Target
+ device_name, uuid = volume_util.find_st(self._session, data)
+ if device_name is None:
+ raise volume_util.StorageError(_("Unable to find iSCSI Target"))
+
+ # Get the vmdk file name that the VM is pointing to
+ hardware_devices = self._session._call_method(vim_util,
+ "get_dynamic_property", vm_ref,
+ "VirtualMachine", "config.hardware.device")
+ device = vm_util.get_rdm_disk(hardware_devices, uuid)
+ if device is None:
+ raise volume_util.StorageError(_("Unable to find volume"))
+ self.detach_disk_from_vm(vm_ref, instance_name, device)
+ LOG.info(_("Mountpoint %(mountpoint)s detached from "
+ "instance %(instance_name)s") % locals())
diff --git a/nova/virt/xenapi/__init__.py b/nova/virt/xenapi/__init__.py
index 6a56a918c..3853f09f2 100644
--- a/nova/virt/xenapi/__init__.py
+++ b/nova/virt/xenapi/__init__.py
@@ -18,4 +18,6 @@
:mod:`xenapi` -- Nova support for XenServer and XCP through XenAPI
==================================================================
"""
-from nova.virt.xenapi.driver import XenAPIDriver
+from nova.virt.xenapi import driver
+
+XenAPIDriver = driver.XenAPIDriver
diff --git a/nova/virt/xenapi/imageupload/__init__.py b/nova/virt/xenapi/imageupload/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/nova/virt/xenapi/imageupload/__init__.py
diff --git a/nova/virt/xenapi/imageupload/glance.py b/nova/virt/xenapi/imageupload/glance.py
new file mode 100644
index 000000000..adc06f65b
--- /dev/null
+++ b/nova/virt/xenapi/imageupload/glance.py
@@ -0,0 +1,54 @@
+# Copyright 2013 OpenStack, LLC
+# 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.
+
+from nova.image import glance
+from nova.openstack.common import cfg
+import nova.openstack.common.log as logging
+from nova.virt.xenapi import vm_utils
+
+LOG = logging.getLogger(__name__)
+
+CONF = cfg.CONF
+
+
+class GlanceStore(object):
+
+ def upload_image(self, context, session, instance, vdi_uuids, image_id):
+ """Requests that the Glance plugin bundle the specified VDIs and
+ push them into Glance using the specified human-friendly name.
+ """
+ # NOTE(sirp): Currently we only support uploading images as VHD, there
+ # is no RAW equivalent (yet)
+ LOG.debug(_("Asking xapi to upload to glance %(vdi_uuids)s as"
+ " ID %(image_id)s"), locals(), instance=instance)
+
+ glance_api_servers = glance.get_api_servers()
+ glance_host, glance_port, glance_use_ssl = glance_api_servers.next()
+
+ properties = {
+ 'auto_disk_config': instance['auto_disk_config'],
+ 'os_type': instance['os_type'] or CONF.default_os_type,
+ }
+
+ params = {'vdi_uuids': vdi_uuids,
+ 'image_id': image_id,
+ 'glance_host': glance_host,
+ 'glance_port': glance_port,
+ 'glance_use_ssl': glance_use_ssl,
+ 'sr_path': vm_utils.get_sr_path(session),
+ 'auth_token': getattr(context, 'auth_token', None),
+ 'properties': properties}
+
+ session.call_plugin_serialized('glance', 'upload_vhd', **params)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 52a5f37b2..fe4ce9409 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -196,13 +196,6 @@ class ImageType(object):
}.get(image_type_id)
-def _system_metadata_to_dict(system_metadata):
- result = {}
- for item in system_metadata:
- result[item['key']] = item['value']
- return result
-
-
def create_vm(session, instance, name_label, kernel, ramdisk,
use_pv_kernel=False):
"""Create a VM record. Returns new VM reference.
@@ -722,35 +715,6 @@ def _find_cached_image(session, image_id, sr_ref):
return cached_images.get(image_id)
-def upload_image(context, session, instance, vdi_uuids, image_id):
- """Requests that the Glance plugin bundle the specified VDIs and
- push them into Glance using the specified human-friendly name.
- """
- # NOTE(sirp): Currently we only support uploading images as VHD, there
- # is no RAW equivalent (yet)
- LOG.debug(_("Asking xapi to upload %(vdi_uuids)s as"
- " ID %(image_id)s"), locals(), instance=instance)
-
- glance_api_servers = glance.get_api_servers()
- glance_host, glance_port, glance_use_ssl = glance_api_servers.next()
-
- properties = {
- 'auto_disk_config': instance['auto_disk_config'],
- 'os_type': instance['os_type'] or CONF.default_os_type,
- }
-
- params = {'vdi_uuids': vdi_uuids,
- 'image_id': image_id,
- 'glance_host': glance_host,
- 'glance_port': glance_port,
- 'glance_use_ssl': glance_use_ssl,
- 'sr_path': get_sr_path(session),
- 'auth_token': getattr(context, 'auth_token', None),
- 'properties': properties}
-
- session.call_plugin_serialized('glance', 'upload_vhd', **params)
-
-
def resize_disk(session, instance, vdi_ref, instance_type):
# Copy VDI over to something we can resize
# NOTE(jerdfelt): Would be nice to just set vdi_ref to read/write
@@ -994,7 +958,7 @@ def _create_image(context, session, instance, name_label, image_id,
elif cache_images == 'all':
cache = True
elif cache_images == 'some':
- sys_meta = _system_metadata_to_dict(instance['system_metadata'])
+ sys_meta = utils.metadata_to_dict(instance['system_metadata'])
try:
cache = utils.bool_from_str(sys_meta['image_cache_in_nova'])
except KeyError:
@@ -1087,7 +1051,7 @@ def _image_uses_bittorrent(context, instance):
if xenapi_torrent_images == 'all':
bittorrent = True
elif xenapi_torrent_images == 'some':
- sys_meta = _system_metadata_to_dict(instance['system_metadata'])
+ sys_meta = utils.metadata_to_dict(instance['system_metadata'])
try:
bittorrent = utils.bool_from_str(sys_meta['image_bittorrent'])
except KeyError:
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 4a8372cda..8d3a3bed2 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -59,7 +59,10 @@ xenapi_vmops_opts = [
'to go to running state'),
cfg.StrOpt('xenapi_vif_driver',
default='nova.virt.xenapi.vif.XenAPIBridgeDriver',
- help='The XenAPI VIF driver using XenServer Network APIs.')
+ help='The XenAPI VIF driver using XenServer Network APIs.'),
+ cfg.StrOpt('xenapi_image_upload_handler',
+ default='nova.virt.xenapi.imageupload.glance.GlanceStore',
+ help='Object Store Driver used to handle image uploads.'),
]
CONF = cfg.CONF
@@ -161,6 +164,11 @@ class VMOps(object):
self.vif_driver = vif_impl(xenapi_session=self._session)
self.default_root_dev = '/dev/sda'
+ msg = _("Importing image upload handler: %s")
+ LOG.debug(msg % CONF.xenapi_image_upload_handler)
+ self.image_upload_handler = importutils.import_object(
+ CONF.xenapi_image_upload_handler)
+
@property
def agent_enabled(self):
return not CONF.xenapi_disable_agent
@@ -661,9 +669,11 @@ class VMOps(object):
coalesce together, so, we must wait for this coalescing to occur to
get a stable representation of the data on disk.
- 3. Push-to-glance: Once coalesced, we call a plugin on the XenServer
- that will bundle the VHDs together and then push the bundle into
- Glance.
+ 3. Push-to-data-store: Once coalesced, we call a plugin on the
+ XenServer that will bundle the VHDs together and then push the
+ bundle. Depending on the configured value of
+ 'xenapi_image_upload_handler', image data may be pushed to
+ Glance or the specified data store.
"""
vm_ref = self._get_vm_opaque_ref(instance)
@@ -674,8 +684,11 @@ class VMOps(object):
update_task_state) as vdi_uuids:
update_task_state(task_state=task_states.IMAGE_UPLOADING,
expected_state=task_states.IMAGE_PENDING_UPLOAD)
- vm_utils.upload_image(
- context, self._session, instance, vdi_uuids, image_id)
+ self.image_upload_handler.upload_image(context,
+ self._session,
+ instance,
+ vdi_uuids,
+ image_id)
LOG.debug(_("Finished snapshot and upload for VM"),
instance=instance)
diff --git a/nova/virt/xenapi/volumeops.py b/nova/virt/xenapi/volumeops.py
index 5f79b6c3a..c2d717cfd 100644
--- a/nova/virt/xenapi/volumeops.py
+++ b/nova/virt/xenapi/volumeops.py
@@ -125,6 +125,7 @@ class VolumeOps(object):
try:
vbd_ref = vm_utils.find_vbd_by_number(self._session, vm_ref,
device_number)
+ sr_ref = volume_utils.find_sr_from_vbd(self._session, vbd_ref)
except volume_utils.StorageError, exc:
LOG.exception(exc)
raise Exception(_('Unable to locate volume %s') % mountpoint)
@@ -143,7 +144,6 @@ class VolumeOps(object):
# Forget SR only if no other volumes on this host are using it
try:
- sr_ref = volume_utils.find_sr_from_vbd(self._session, vbd_ref)
volume_utils.purge_sr(self._session, sr_ref)
except volume_utils.StorageError, exc:
LOG.exception(exc)
diff --git a/nova/volume/cinder.py b/nova/volume/cinder.py
index 3e1ccc66b..05918f83d 100644
--- a/nova/volume/cinder.py
+++ b/nova/volume/cinder.py
@@ -20,7 +20,7 @@
Handles all requests relating to volumes + cinder.
"""
-from copy import deepcopy
+import copy
import sys
from cinderclient import exceptions as cinder_exception
@@ -63,8 +63,10 @@ def cinderclient(context):
# FIXME: the cinderclient ServiceCatalog object is mis-named.
# It actually contains the entire access blob.
+ # Only needed parts of the service catalog are passed in, see
+ # nova/context.py.
compat_catalog = {
- 'access': {'serviceCatalog': context.service_catalog or {}}
+ 'access': {'serviceCatalog': context.service_catalog or []}
}
sc = service_catalog.ServiceCatalog(compat_catalog)
if CONF.cinder_endpoint_template:
@@ -139,7 +141,7 @@ def _untranslate_volume_summary_view(context, vol):
d['volume_metadata'].append(item)
if hasattr(vol, 'volume_image_metadata'):
- d['volume_image_metadata'] = deepcopy(vol.volume_image_metadata)
+ d['volume_image_metadata'] = copy.deepcopy(vol.volume_image_metadata)
return d
diff --git a/nova/wsgi.py b/nova/wsgi.py
index 16851dba8..651dbc4f6 100644
--- a/nova/wsgi.py
+++ b/nova/wsgi.py
@@ -28,6 +28,7 @@ import eventlet.wsgi
import greenlet
from paste import deploy
import routes.middleware
+import ssl
import webob.dec
import webob.exc
@@ -45,7 +46,21 @@ wsgi_opts = [
help='A python format string that is used as the template to '
'generate log lines. The following values can be formatted '
'into it: client_ip, date_time, request_line, status_code, '
- 'body_length, wall_seconds.')
+ 'body_length, wall_seconds.'),
+ cfg.StrOpt('ssl_ca_file',
+ default=None,
+ help="CA certificate file to use to verify "
+ "connecting clients"),
+ cfg.StrOpt('ssl_cert_file',
+ default=None,
+ help="SSL certificate of API server"),
+ cfg.StrOpt('ssl_key_file',
+ default=None,
+ help="SSL private key of API server"),
+ cfg.IntOpt('tcp_keepidle',
+ default=600,
+ help="Sets the value of TCP_KEEPIDLE in seconds for each "
+ "server socket. Not supported on OS X.")
]
CONF = cfg.CONF
CONF.register_opts(wsgi_opts)
@@ -59,7 +74,8 @@ class Server(object):
default_pool_size = 1000
def __init__(self, name, app, host='0.0.0.0', port=0, pool_size=None,
- protocol=eventlet.wsgi.HttpProtocol, backlog=128):
+ protocol=eventlet.wsgi.HttpProtocol, backlog=128,
+ use_ssl=False, max_url_len=None):
"""Initialize, but do not start, a WSGI server.
:param name: Pretty name for logging.
@@ -68,6 +84,7 @@ class Server(object):
:param port: Port number to server the application.
:param pool_size: Maximum number of eventlets to spawn concurrently.
:param backlog: Maximum number of queued connections.
+ :param max_url_len: Maximum length of permitted URLs.
:returns: None
:raises: nova.exception.InvalidInput
"""
@@ -78,6 +95,8 @@ class Server(object):
self._pool = eventlet.GreenPool(pool_size or self.default_pool_size)
self._logger = logging.getLogger("nova.%s.wsgi.server" % self.name)
self._wsgi_logger = logging.WritableLogger(self._logger)
+ self._use_ssl = use_ssl
+ self._max_url_len = max_url_len
if backlog < 1:
raise exception.InvalidInput(
@@ -106,13 +125,74 @@ class Server(object):
:returns: None
"""
- self._server = eventlet.spawn(eventlet.wsgi.server,
- self._socket,
- self.app,
- protocol=self._protocol,
- custom_pool=self._pool,
- log=self._wsgi_logger,
- log_format=CONF.wsgi_log_format)
+ if self._use_ssl:
+ try:
+ ca_file = CONF.ssl_ca_file
+ cert_file = CONF.ssl_cert_file
+ key_file = CONF.ssl_key_file
+
+ if cert_file and not os.path.exists(cert_file):
+ raise RuntimeError(
+ _("Unable to find cert_file : %s") % cert_file)
+
+ if ca_file and not os.path.exists(ca_file):
+ raise RuntimeError(
+ _("Unable to find ca_file : %s") % ca_file)
+
+ if key_file and not os.path.exists(key_file):
+ raise RuntimeError(
+ _("Unable to find key_file : %s") % key_file)
+
+ if self._use_ssl and (not cert_file or not key_file):
+ raise RuntimeError(
+ _("When running server in SSL mode, you must "
+ "specify both a cert_file and key_file "
+ "option value in your configuration file"))
+ ssl_kwargs = {
+ 'server_side': True,
+ 'certfile': cert_file,
+ 'keyfile': key_file,
+ 'cert_reqs': ssl.CERT_NONE,
+ }
+
+ if CONF.ssl_ca_file:
+ ssl_kwargs['ca_certs'] = ca_file
+ ssl_kwargs['cert_reqs'] = ssl.CERT_REQUIRED
+
+ self._socket = eventlet.wrap_ssl(self._socket,
+ **ssl_kwargs)
+
+ self._socket.setsockopt(socket.SOL_SOCKET,
+ socket.SO_REUSEADDR, 1)
+ # sockets can hang around forever without keepalive
+ self._socket.setsockopt(socket.SOL_SOCKET,
+ socket.SO_KEEPALIVE, 1)
+
+ # This option isn't available in the OS X version of eventlet
+ if hasattr(socket, 'TCP_KEEPIDLE'):
+ self._socket.setsockopt(socket.IPPROTO_TCP,
+ socket.TCP_KEEPIDLE,
+ CONF.tcp_keepidle)
+
+ except Exception:
+ LOG.error(_("Failed to start %(name)s on %(host)s"
+ ":%(port)s with SSL support") % self.__dict__)
+ raise
+
+ wsgi_kwargs = {
+ 'func': eventlet.wsgi.server,
+ 'sock': self._socket,
+ 'site': self.app,
+ 'protocol': self._protocol,
+ 'custom_pool': self._pool,
+ 'log': self._wsgi_logger,
+ 'log_format': CONF.wsgi_log_format
+ }
+
+ if self._max_url_len:
+ wsgi_kwargs['url_length_limit'] = self._max_url_len
+
+ self._server = eventlet.spawn(**wsgi_kwargs)
def stop(self):
"""Stop this server.
diff --git a/run_tests.sh b/run_tests.sh
index 238f5e194..3bf996fa2 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -91,7 +91,7 @@ function run_tests {
# Just run the test suites in current environment
set +e
testrargs=`echo "$testrargs" | sed -e's/^\s*\(.*\)\s*$/\1/'`
- TESTRTESTS="$TESTRTESTS --testr-args='$testrargs'"
+ TESTRTESTS="$TESTRTESTS --testr-args='$testropts $testrargs'"
echo "Running \`${wrapper} $TESTRTESTS\`"
bash -c "${wrapper} $TESTRTESTS"
RESULT=$?
@@ -99,6 +99,13 @@ function run_tests {
copy_subunit_log
+ if [ $coverage -eq 1 ]; then
+ echo "Generating coverage report in covhtml/"
+ # Don't compute coverage for common code, which is tested elsewhere
+ ${wrapper} coverage combine
+ ${wrapper} coverage html --include='nova/*' --omit='nova/openstack/common/*' -d covhtml -i
+ fi
+
return $RESULT
}
@@ -118,7 +125,7 @@ function run_pep8 {
# NOTE(lzyeval): Avoid selecting *.pyc files to reduce pep8 check-up time
# when running on devstack.
srcfiles=`find nova -type f -name "*.py" ! -wholename "nova\/openstack*"`
- srcfiles+=" `find bin -type f ! -name "nova.conf*" ! -name "*api-paste.ini*"`"
+ srcfiles+=" `find bin -type f ! -name "nova.conf*" ! -name "*api-paste.ini*" ! -name "*~"`"
srcfiles+=" `find tools -type f -name "*.py"`"
srcfiles+=" `find plugins -type f -name "*.py"`"
srcfiles+=" `find smoketests -type f -name "*.py"`"
@@ -135,6 +142,7 @@ function run_pep8 {
echo "Running pep8"
${wrapper} python tools/hacking.py ${ignore} ${srcfiles}
+ ${wrapper} bash tools/unused_imports.sh
# NOTE(sdague): as of grizzly-2 these are passing however leaving the comment
# in here in case we need to break it out when we get more of our hacking working
# again.
@@ -147,7 +155,7 @@ function run_pep8 {
}
-TESTRTESTS="python setup.py testr $testropts"
+TESTRTESTS="python setup.py testr"
if [ $never_venv -eq 0 ]
then
@@ -201,10 +209,3 @@ if [ -z "$testrargs" ]; then
run_pep8
fi
fi
-
-if [ $coverage -eq 1 ]; then
- echo "Generating coverage report in covhtml/"
- # Don't compute coverage for common code, which is tested elsewhere
- ${wrapper} coverage combine
- ${wrapper} coverage html --include='nova/*' --omit='nova/openstack/common/*' -d covhtml -i
-fi
diff --git a/smoketests/base.py b/smoketests/base.py
index f6cec3168..c90da102c 100644
--- a/smoketests/base.py
+++ b/smoketests/base.py
@@ -17,7 +17,7 @@
# under the License.
import boto
-from boto.ec2.regioninfo import RegionInfo
+from boto.ec2 import regioninfo
import commands
import httplib
import os
@@ -123,7 +123,7 @@ class SmokeTestCase(unittest.TestCase):
return boto_v6.connect_ec2(aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
is_secure=parts['is_secure'],
- region=RegionInfo(None,
+ region=regioninfo.RegionInfo(None,
'nova',
parts['ip']),
port=parts['port'],
@@ -133,7 +133,7 @@ class SmokeTestCase(unittest.TestCase):
return boto.connect_ec2(aws_access_key_id=access_key,
aws_secret_access_key=secret_key,
is_secure=parts['is_secure'],
- region=RegionInfo(None,
+ region=regioninfo.RegionInfo(None,
'nova',
parts['ip']),
port=parts['port'],
@@ -169,7 +169,6 @@ class SmokeTestCase(unittest.TestCase):
cmd += ' --kernel true'
status, output = commands.getstatusoutput(cmd)
if status != 0:
- print '%s -> \n %s' % (cmd, output)
raise Exception(output)
return True
@@ -178,7 +177,6 @@ class SmokeTestCase(unittest.TestCase):
cmd += '%s -m %s/%s.manifest.xml' % (bucket_name, tempdir, image)
status, output = commands.getstatusoutput(cmd)
if status != 0:
- print '%s -> \n %s' % (cmd, output)
raise Exception(output)
return True
@@ -186,7 +184,6 @@ class SmokeTestCase(unittest.TestCase):
cmd = 'euca-delete-bundle --clear -b %s' % (bucket_name)
status, output = commands.getstatusoutput(cmd)
if status != 0:
- print '%s -> \n%s' % (cmd, output)
raise Exception(output)
return True
diff --git a/smoketests/public_network_smoketests.py b/smoketests/public_network_smoketests.py
index 4fb843e0f..f20b0923e 100644
--- a/smoketests/public_network_smoketests.py
+++ b/smoketests/public_network_smoketests.py
@@ -97,7 +97,6 @@ class InstanceTestsFromPublic(base.UserSmokeTestCase):
self.data['ip_v6'], TEST_KEY)
conn.close()
except Exception as ex:
- print ex
time.sleep(1)
else:
break
diff --git a/tools/flakes.py b/tools/flakes.py
index 4b93abc21..f805fd156 100644
--- a/tools/flakes.py
+++ b/tools/flakes.py
@@ -8,7 +8,7 @@ import __builtin__
import os
import sys
-from pyflakes.scripts.pyflakes import main
+from pyflakes.scripts import pyflakes
if __name__ == "__main__":
names = os.environ.get('PYFLAKES_BUILTINS', '_')
@@ -19,4 +19,4 @@ if __name__ == "__main__":
del names, os, __builtin__
- sys.exit(main())
+ sys.exit(pyflakes.main())
diff --git a/tools/hacking.py b/tools/hacking.py
index 56f6694bd..801a87899 100755
--- a/tools/hacking.py
+++ b/tools/hacking.py
@@ -18,7 +18,7 @@
"""nova HACKING file compliance testing
-built on top of pep8.py
+Built on top of pep8.py
"""
import inspect
@@ -49,6 +49,8 @@ START_DOCSTRING_TRIPLE = ['u"""', 'r"""', '"""', "u'''", "r'''", "'''"]
END_DOCSTRING_TRIPLE = ['"""', "'''"]
VERBOSE_MISSING_IMPORT = os.getenv('HACKING_VERBOSE_MISSING_IMPORT', 'False')
+_missingImport = set([])
+
# Monkey patch broken excluded filter in pep8
# See https://github.com/jcrocholl/pep8/pull/111
@@ -103,7 +105,7 @@ def import_normalize(line):
return line
-def nova_todo_format(physical_line):
+def nova_todo_format(physical_line, tokens):
"""Check for 'TODO()'.
nova HACKING guide recommendation for TODO:
@@ -111,14 +113,13 @@ def nova_todo_format(physical_line):
Okay: #TODO(sdague)
N101: #TODO fail
+ N101: #TODO (jogo) fail
"""
# TODO(sdague): TODO check shouldn't fail inside of space
pos = physical_line.find('TODO')
pos1 = physical_line.find('TODO(')
pos2 = physical_line.find('#') # make sure it's a comment
- # TODO(sdague): should be smarter on this test
- this_test = physical_line.find('N101: #TODO fail')
- if pos != pos1 and pos2 >= 0 and pos2 < pos and this_test == -1:
+ if (pos != pos1 and pos2 >= 0 and pos2 < pos and len(tokens) == 0):
return pos, "N101: Use TODO(NAME)"
@@ -165,8 +166,6 @@ def nova_one_import_per_line(logical_line):
not is_import_exception(parts[1])):
yield pos, "N301: one import per line"
-_missingImport = set([])
-
def nova_import_module_only(logical_line):
r"""Check for import module only.
@@ -175,20 +174,23 @@ def nova_import_module_only(logical_line):
Do not import objects, only modules
Okay: from os import path
- N302 from os.path import mkdir as mkdir2
- N303 import bubba
- N304 import blueblue
+ Okay: import os.path
+ N302: from os.path import dirname as dirname2
+ N303 from os.path import *
+ N304 import flakes
"""
# N302 import only modules
# N303 Invalid Import
# N304 Relative Import
# TODO(sdague) actually get these tests working
- def importModuleCheck(mod, parent=None, added=False):
- """Import Module helper function.
+ # TODO(jogo) simplify this code
+ def import_module_check(mod, parent=None, added=False):
+ """Checks for relative, modules and invalid imports.
If can't find module on first try, recursively check for relative
- imports
+ imports.
+ When parsing 'from x import y,' x is the parent.
"""
current_path = os.path.dirname(pep8.current_file)
try:
@@ -196,8 +198,6 @@ def nova_import_module_only(logical_line):
warnings.simplefilter('ignore', DeprecationWarning)
valid = True
if parent:
- if is_import_exception(parent):
- return
parent_mod = __import__(parent, globals(), locals(),
[mod], -1)
valid = inspect.ismodule(getattr(parent_mod, mod))
@@ -209,7 +209,7 @@ def nova_import_module_only(logical_line):
sys.path.pop()
added = False
return logical_line.find(mod), ("N304: No "
- "relative imports. '%s' is a relative import"
+ "relative imports. '%s' is a relative import"
% logical_line)
return logical_line.find(mod), ("N302: import only "
"modules. '%s' does not import a module"
@@ -219,7 +219,7 @@ def nova_import_module_only(logical_line):
if not added:
added = True
sys.path.append(current_path)
- return importModuleCheck(mod, parent, added)
+ return import_module_check(mod, parent, added)
else:
name = logical_line.split()[1]
if name not in _missingImport:
@@ -234,23 +234,27 @@ def nova_import_module_only(logical_line):
except AttributeError:
# Invalid import
+ if "import *" in logical_line:
+ # TODO(jogo): handle "from x import *, by checking all
+ # "objects in x"
+ return
return logical_line.find(mod), ("N303: Invalid import, "
- "AttributeError raised")
+ "%s" % mod)
- # convert "from x import y" to " import x.y"
- # convert "from x import y as z" to " import x.y"
- import_normalize(logical_line)
split_line = logical_line.split()
-
- if (logical_line.startswith("import ") and "," not in logical_line and
- (len(split_line) == 2 or
- (len(split_line) == 4 and split_line[2] == "as"))):
- mod = split_line[1]
- rval = importModuleCheck(mod)
+ if (", " not in logical_line and
+ split_line[0] in ('import', 'from') and
+ (len(split_line) in (2, 4, 6)) and
+ split_line[1] != "__future__"):
+ if is_import_exception(split_line[1]):
+ return
+ if "from" == split_line[0]:
+ rval = import_module_check(split_line[3], parent=split_line[1])
+ else:
+ rval = import_module_check(split_line[1])
if rval is not None:
yield rval
- # TODO(jogo) handle "from x import *"
#TODO(jogo): import template: N305
@@ -329,6 +333,8 @@ def nova_docstring_one_line(physical_line):
A one line docstring looks like this and ends in punctuation.
Okay: '''This is good.'''
+ Okay: '''This is good too!'''
+ Okay: '''How about this?'''
N402: '''This is not'''
N402: '''Bad punctuation,'''
"""
diff --git a/tools/pip-requires b/tools/pip-requires
index 231d5cfe5..126f0125c 100644
--- a/tools/pip-requires
+++ b/tools/pip-requires
@@ -20,7 +20,7 @@ Babel>=0.9.6
iso8601>=0.1.4
httplib2
setuptools_git>=0.4
-python-cinderclient
+python-cinderclient>=1.0.1
python-quantumclient>=2.1
python-glanceclient>=0.5.0,<2
python-keystoneclient>=0.2.0
diff --git a/tools/test-requires b/tools/test-requires
index bc279166e..fce1bc8f1 100644
--- a/tools/test-requires
+++ b/tools/test-requires
@@ -1,7 +1,7 @@
# Packages needed for dev testing
distribute>=0.6.24
-coverage
+coverage>=3.6
discover
feedparser
fixtures>=0.3.12
@@ -12,4 +12,4 @@ pylint==0.25.2
python-subunit
sphinx>=1.1.2
testrepository>=0.0.13
-testtools>=0.9.26
+testtools>=0.9.27
diff --git a/tools/unused_imports.sh b/tools/unused_imports.sh
new file mode 100755
index 000000000..0e0294517
--- /dev/null
+++ b/tools/unused_imports.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+#snakefood sfood-checker detects even more unused imports
+! pyflakes nova/ | grep "imported but unused"
diff --git a/tools/xenserver/cleanup_sm_locks.py b/tools/xenserver/cleanup_sm_locks.py
new file mode 100755
index 000000000..de455b076
--- /dev/null
+++ b/tools/xenserver/cleanup_sm_locks.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+
+# Copyright 2013 OpenStack, LLC
+#
+# 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.
+"""
+Script to cleanup old XenServer /var/lock/sm locks.
+
+XenServer 5.6 and 6.0 do not appear to always cleanup locks when using a
+FileSR. ext3 has a limit of 32K inode links, so when we have 32K-2 (31998)
+locks laying around, builds will begin to fail because we can't create any
+additional locks. This cleanup script is something we can run periodically as
+a stop-gap measure until this is fixed upstream.
+
+This script should be run on the dom0 of the affected machine.
+"""
+import errno
+import optparse
+import os
+import sys
+import time
+
+BASE = '/var/lock/sm'
+
+
+def _get_age_days(secs):
+ return float(time.time() - secs) / 86400
+
+
+def _parse_args():
+ parser = optparse.OptionParser()
+ parser.add_option("-d", "--dry-run",
+ action="store_true", dest="dry_run", default=False,
+ help="don't actually remove locks")
+ parser.add_option("-l", "--limit",
+ action="store", type='int', dest="limit",
+ default=sys.maxint,
+ help="max number of locks to delete (default: no limit)")
+ parser.add_option("-v", "--verbose",
+ action="store_true", dest="verbose", default=False,
+ help="don't print status messages to stdout")
+
+ options, args = parser.parse_args()
+
+ try:
+ days_old = int(args[0])
+ except (IndexError, ValueError):
+ parser.print_help()
+ sys.exit(1)
+
+ return options, days_old
+
+
+def main():
+ options, days_old = _parse_args()
+
+ if not os.path.exists(BASE):
+ print >> sys.stderr, "error: '%s' doesn't exist. Make sure you're"\
+ " running this on the dom0." % BASE
+ sys.exit(1)
+
+ lockpaths_removed = 0
+ nspaths_removed = 0
+
+ for nsname in os.listdir(BASE)[:options.limit]:
+ nspath = os.path.join(BASE, nsname)
+
+ if not os.path.isdir(nspath):
+ continue
+
+ # Remove old lockfiles
+ removed = 0
+ locknames = os.listdir(nspath)
+ for lockname in locknames:
+ lockpath = os.path.join(nspath, lockname)
+ lock_age_days = _get_age_days(os.path.getmtime(lockpath))
+ if lock_age_days > days_old:
+ lockpaths_removed += 1
+ removed += 1
+
+ if options.verbose:
+ print 'Removing old lock: %03d %s' % (lock_age_days,
+ lockpath)
+
+ if not options.dry_run:
+ os.unlink(lockpath)
+
+ # Remove empty namespace paths
+ if len(locknames) == removed:
+ nspaths_removed += 1
+
+ if options.verbose:
+ print 'Removing empty namespace: %s' % nspath
+
+ if not options.dry_run:
+ try:
+ os.rmdir(nspath)
+ except OSError, e:
+ if e.errno == errno.ENOTEMPTY:
+ print >> sys.stderr, "warning: directory '%s'"\
+ " not empty" % nspath
+ else:
+ raise
+
+ if options.dry_run:
+ print "** Dry Run **"
+
+ print "Total locks removed: ", lockpaths_removed
+ print "Total namespaces removed: ", nspaths_removed
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tox.ini b/tox.ini
index e98f30151..67fe1eea0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -15,13 +15,16 @@ sitepackages = True
downloadcache = ~/cache/pip
[testenv:pep8]
-deps=pep8==1.3.3
+deps=
+ pep8==1.3.3
+ pyflakes
commands =
python tools/hacking.py --doctest
python tools/hacking.py --ignore=E12,E711,E721,E712,N403,N404 --show-source \
--exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg .
python tools/hacking.py --ignore=E12,E711,E721,E712,N403,N404 --show-source \
--filename=nova* bin
+ bash tools/unused_imports.sh
[testenv:pylint]
setenv = VIRTUAL_ENV={envdir}