class TestEsxVmConfig(unittest.TestCase):
    @patch.object(VimClient, "acquire_credentials")
    @patch.object(VimClient, "update_cache")
    @patch("pysdk.connect.Connect")
    def setUp(self, connect, update, creds):
        creds.return_value = ["username", "password"]
        self.vim_client = VimClient(auto_sync=False)
        with patch("host.hypervisor.esx.vm_config.GetEnv"):
            self.vm_config = EsxVmConfig(self.vim_client)

    def tearDown(self):
        self.vim_client.disconnect(wait=True)

    def dummy_devices(self):
        return [
            vim.vm.device.VirtualFloppy(key=10),
            vim.vm.device.VirtualPCIController(key=100),
            DEFAULT_DISK_CONTROLLER_CLASS(key=1000),
            vim.vm.device.VirtualSoundCard(key=10000),
        ]

    def test_vm_create_spec(self):
        datastore = "ds1"
        vm_id = str(uuid.uuid4())
        metadata = {
            "configuration": {"guestOS": "otherLinuxGuest"},
            "parameters": [{"name": "key1"}, {"name": "key2"}]
        }
        env = {
            "key1": "value1",
            "keyUnexpected": "valueNotSet",
        }
        spec = self.vm_config.create_spec(vm_id, datastore, 512, 1, metadata,
                                          env)
        assert_that(spec.memoryMB, equal_to(512))
        assert_that(spec.numCPUs, equal_to(1))
        assert_that(spec.name, equal_to(vm_id))
        assert_that(spec.guestId, equal_to("otherLinuxGuest"))
        expected_metadata = {'guestOS': 'otherLinuxGuest', 'key1': 'value1'}
        assert_that(spec._metadata, equal_to(expected_metadata))

    def test_create_nic_spec(self):
        net_name = "VM_network"
        cspec = self.vm_config.update_spec()
        spec = self.vm_config.add_nic(cspec, net_name)
        backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo
        assert_that(spec.deviceChange[0].device.backing.__class__,
                    equal_to(backing))
        assert_that(spec.deviceChange[0].device.backing.deviceName,
                    equal_to(net_name))

    def test_find_disk_controller(self):
        devices = self.dummy_devices()
        device_type = DEFAULT_DISK_CONTROLLER_CLASS
        disk_controller = self.vm_config.find_device(devices, device_type)
        assert_that(disk_controller.key, equal_to(1000))

    def test_find_nic_controller(self):
        devices = self.dummy_devices()
        device_type = vim.vm.device.VirtualPCIController
        disk_controller = self.vm_config.find_device(devices, device_type)
        assert_that(disk_controller.key, equal_to(100))

    def test_find_virtual_disk(self):
        spec = vim.vm.ConfigSpec()
        vm_config = self.vm_config
        devices = self.dummy_devices()
        for device in devices:
            vm_config.add_device(spec, device)
        cfg_info = FakeConfigInfo()
        device_type = vim.vm.device.VirtualDisk
        datastore = "ds1"
        filename = "folder/foo"
        path = vmdk_path(datastore, filename)

        find_disk = vm_config.disk_matcher(datastore, filename)
        disk = vm_config.find_device(devices, device_type, matcher=find_disk)
        assert_that(disk, equal_to(None))

        vm_config.add_scsi_disk(cfg_info, spec, datastore, "nope")

        self.assertRaises(DeviceNotFoundException, vm_config.get_device,
                          devices, device_type, matcher=find_disk)

        vm_config.add_scsi_disk(cfg_info, spec, datastore,
                                filename)
        device_changes = spec.deviceChange
        device_list = []
        for device_change in device_changes:
            device_list.append(device_change.device)

        disk = vm_config.find_device(device_list, device_type,
                                     matcher=find_disk)
        assert_that(disk.backing.fileName, equal_to(path))

    def _create_spec_for_disk_test(self, datastore, vm_id):
        spec = vim.vm.ConfigSpec()
        devices = self.dummy_devices()
        for device in devices:
            self.vm_config.add_device(spec, device)
        vm_path_name = '[%s] %s/%s' % (datastore, vm_id[0:2], vm_id)
        spec.files = vim.vm.FileInfo(vmPathName=vm_path_name)
        spec.name = vm_id
        return spec

    def test_create_empty_disk(self):
        vm_id = str(uuid.uuid4())
        datastore = "ds1"
        spec = self._create_spec_for_disk_test(datastore, vm_id)

        size_mb = 100
        disk_id = str(uuid.uuid4())
        self.vm_config.create_empty_disk(spec, datastore, disk_id, size_mb)

        devs = [change.device for change in spec.deviceChange]
        device_type = vim.vm.device.VirtualDisk
        disks = self.vm_config.find_devices(devs, device_type)
        assert_that(len(disks), equal_to(1))
        # verify that uuid to be set on disk to be added matches the
        # of the disk (modulo some formatting differences)
        assert_that(disks[0].backing.uuid,
                    equal_to(uuid_to_vmdk_uuid(disk_id)))

    def test_create_child_disk(self):
        vm_id = str(uuid.uuid4())
        datastore = "ds1"
        spec = self._create_spec_for_disk_test(datastore, vm_id)

        disk_id = str(uuid.uuid4())
        parent_id = str(uuid.uuid4())
        self.vm_config.create_child_disk(spec, datastore, disk_id, parent_id)

        devs = [change.device for change in spec.deviceChange]
        device_type = vim.vm.device.VirtualDisk
        disks = self.vm_config.find_devices(devs, device_type)
        assert_that(len(disks), equal_to(1))
        # verify that disk to be added does not request a specifc uuid
        assert_that(disks[0].backing.uuid, equal_to(None))

    def _get_config_info_with_iso(self, iso_path):
        devices = self.dummy_devices()
        cfg_info = FakeConfigInfo()
        cfg_info.hardware.device = devices

        cdrom = vim.vm.device.VirtualCdrom()
        cdrom.key = 1234
        cdrom.controllerKey = 100
        cdrom.unitNumber = 1

        iso_backing = vim.vm.device.VirtualCdrom.IsoBackingInfo()
        iso_backing.fileName = iso_path
        cdrom.backing = iso_backing

        conInfo = vim.vm.device.VirtualDevice.ConnectInfo()
        conInfo.allowGuestControl = True
        conInfo.connected = True
        conInfo.startConnected = True
        cdrom.connectable = conInfo
        cfg_info.hardware.device.append(cdrom)
        return cfg_info

    def _get_config_info_without_connected(self, is_iso_backing):
        devices = self.dummy_devices()
        cfg_info = FakeConfigInfo()
        cfg_info.hardware.device = devices

        cdrom = vim.vm.device.VirtualCdrom()
        cdrom.key = 1234
        cdrom.controllerKey = 100
        cdrom.unitNumber = 1

        if is_iso_backing:
            iso_backing = vim.vm.device.VirtualCdrom.IsoBackingInfo()
            cdrom.backing = iso_backing

        conInfo = vim.vm.device.VirtualDevice.ConnectInfo()
        conInfo.allowGuestControl = True
        conInfo.connected = False
        conInfo.startConnected = True
        cdrom.connectable = conInfo
        cfg_info.hardware.device.append(cdrom)
        return cfg_info

    def test_add_iso_cdrom(self):
        virtual_ide_controller = vim.vm.device.VirtualIDEController()
        cfgOption = vim.vm.ConfigOption()
        cfgOption.defaultDevice.append(virtual_ide_controller)
        self.vm_config._cfg_opts = cfgOption
        # fake iso ds path
        fake_iso_ds_path = '[ds] vm_fake/fake.iso'

        # test if no virtual cdrom attached to the VM
        cfg_info = FakeConfigInfo()

        cspec = self.vm_config.update_spec()

        result = self.vm_config.add_iso_cdrom(
            cspec,
            fake_iso_ds_path,
            cfg_info)

        assert_that(result.__class__,
                    equal_to(bool))
        assert_that(result, equal_to(True))

        dev = cspec.deviceChange[0].device
        assert_that(len(cspec.deviceChange), equal_to(1))
        assert_that(dev.connectable.connected, equal_to(True))
        assert_that(dev.connectable.startConnected, equal_to(True))
        assert_that(dev.backing.__class__,
                    equal_to(vim.vm.device.VirtualCdrom.IsoBackingInfo))

        # test if virtual cdrom exist and ISO already attached to the VM
        cspec = self.vm_config.update_spec()

        cfg_info = self._get_config_info_with_iso(fake_iso_ds_path)

        result = self.vm_config.add_iso_cdrom(
            cspec,
            fake_iso_ds_path,
            cfg_info)

        assert_that(result.__class__,
                    equal_to(bool))
        assert_that(result, equal_to(False))

        # test if virtual cdrom exist and it's iso_backing
        # and ISO is not attached to the VM
        cspec = self.vm_config.update_spec()

        cfg_info = self._get_config_info_without_connected(is_iso_backing=True)

        result = self.vm_config.add_iso_cdrom(
            cspec,
            fake_iso_ds_path,
            cfg_info)

        assert_that(result.__class__,
                    equal_to(bool))
        assert_that(result, equal_to(True))

        dev = cspec.deviceChange[0].device
        assert_that(len(cspec.deviceChange), equal_to(1))
        assert_that(dev.connectable.connected, equal_to(True))
        assert_that(dev.connectable.startConnected, equal_to(True))
        assert_that(dev.backing.__class__,
                    equal_to(vim.vm.device.VirtualCdrom.IsoBackingInfo))

        # test if virtual cdrom exist and it's _not_ iso_backing
        # and ISO is not attached to the VM
        cspec = self.vm_config.update_spec()

        cfg_info = self._get_config_info_without_connected(
            is_iso_backing=False)

        self.assertRaises(TypeError,
                          self.vm_config.add_iso_cdrom,
                          cspec, fake_iso_ds_path, cfg_info)

    def test_disconnect_iso(self):
        # on vm config with no cdrom devices
        cfg_info = FakeConfigInfo()
        cspec = self.vm_config.update_spec()
        self.assertRaises(DeviceNotFoundException,
                          self.vm_config.disconnect_iso_cdrom,
                          cspec, cfg_info)
        assert_that(len(cspec.deviceChange), equal_to(0))

        # on vm config with no a fake cdrom device
        fake_iso_ds_path = '[ds] vm_fake/fake.iso'
        cspec = self.vm_config.update_spec()
        cfg_info = self._get_config_info_with_iso(fake_iso_ds_path)
        iso_path = self.vm_config.disconnect_iso_cdrom(cspec, cfg_info)

        assert_that(len(cspec.deviceChange), equal_to(1))
        dev = cspec.deviceChange[0].device
        assert_that(dev.backing.__class__,
                    equal_to(vim.vm.device.VirtualCdrom.IsoBackingInfo))
        assert_that(dev.backing.fileName,
                    equal_to(fake_iso_ds_path))

        assert_that(iso_path, equal_to(fake_iso_ds_path))
        assert_that(dev.connectable.connected, equal_to(False))
        assert_that(dev.connectable.startConnected, equal_to(False))

    def test_remove_iso_cdrom_device(self):
        fake_iso_ds_path = '[ds] vm_fake/fake.iso'
        cspec = self.vm_config.update_spec()
        cfg_info = self._get_config_info_with_iso(fake_iso_ds_path)
        self.vm_config.remove_iso_cdrom(cspec, cfg_info)

        assert_that(len(cspec.deviceChange), equal_to(1))
        assert_that(cspec.deviceChange[0].operation, equal_to('remove'))
        dev = cspec.deviceChange[0].device
        assert_that(dev.backing.__class__,
                    equal_to(vim.vm.device.VirtualCdrom.IsoBackingInfo))
        assert_that(dev.backing.fileName,
                    equal_to(fake_iso_ds_path))

    def test_update_spec(self):
        cfg_info = FakeConfigInfo()
        spec = self.vm_config.update_spec()
        assert_that(len(spec.deviceChange), equal_to(0))
        net_name = "VM_Network"
        self.vm_config.add_nic(spec, net_name)
        assert_that(len(spec.deviceChange), equal_to(1))
        self.vm_config.add_scsi_disk(cfg_info, spec, "ds1", "foo")
        # One for the controller and one for the disk itself.
        assert_that(len(spec.deviceChange), equal_to(3))

    def test_path_conversion_invalid(self):
        self.assertRaises(IndexError, datastore_to_os_path, "invalid_ds_path")

    @parameterized.expand([
        ('[foo] a/b/c.vmdk', '/vmfs/volumes/foo/a/b/c.vmdk'),
        ('[foo] c.vmdk', '/vmfs/volumes/foo/c.vmdk'),
        ('[foo]a', '/vmfs/volumes/foo/a'),
        ('/vmfs/volumes/foo/bar.vmdk', '/vmfs/volumes/foo/bar.vmdk'),
        ('[]/vmfs/volumes/foo/bar.vmdk', '/vmfs/volumes/foo/bar.vmdk'),
        ('[] /vmfs/volumes/foo/bar.vmdk', '/vmfs/volumes/foo/bar.vmdk')
    ])
    def test_path_conversion(self, ds_path, expected_os_path):
        path = datastore_to_os_path(ds_path)
        assert_that(path, equal_to(expected_os_path))

    @parameterized.expand([
        (['[foo] image_a_b/c.vmdk'], True, False, False),
        (['[foo] vm_a_b/c.vmdk'], False, True, False),
        (['[foo] image_a_b/c.vmdk', '[foo] vm/a.vmdk'], False, True, False),
        (['[foo] disk_a_b/c.vmdk'], False, False, True),
        (['[foo] image_a/c.vmdk', '[foo] disk/a.vmdk'], False, False, True),
        ([], False, False, False)
    ])
    def test_is_what_disk(self, disk_files, image, ephemeral, persistent):
        assert_that(is_image(disk_files), equal_to(image))
        assert_that(is_ephemeral_disk(disk_files), equal_to(ephemeral))
        assert_that(is_persistent_disk(disk_files), equal_to(persistent))

    def test_vmdk_uuid_conversion(self):
        for id in ['01234567-89ab-cedf-0123-456789abcdef',
                   '01 23456 789ABCEDF0123456789ABCDEF',
                   '01 23 45 67 89 ab ce df-01 23 45 67 89 ab cd ef',
                   '0123456789abcedf0123456789abcdef']:
            vmdk_uuid = uuid_to_vmdk_uuid(id)
            assert_that(
                vmdk_uuid,
                equal_to('01 23 45 67 89 ab ce df-01 23 45 67 89 ab cd ef'))
        for id in ['',
                   '01234567-89ab-cedf-0123-456789abcd',
                   '01 23456 789abcedf0123456789abcdefabcd']:
            self.assertRaises(ValueError, uuid_to_vmdk_uuid, id)
class TestVimClient(unittest.TestCase):
    def setUp(self):
        if "host_remote_test" not in config:
            raise SkipTest()

        self.host = config["host_remote_test"]["server"]
        self.pwd = config["host_remote_test"]["esx_pwd"]

        if self.host is None or self.pwd is None:
            raise SkipTest()

        self.vim_client = VimClient(self.host, "root", self.pwd,
                                    auto_sync=True)
        self.vm_config = EsxVmConfig(self.vim_client)
        self._logger = logging.getLogger(__name__)

    def tearDown(self):
        self.vim_client.disconnect(wait=True)

    def test_memory_usage(self):
        used_memory = self.vim_client.memory_usage_mb
        assert_that(used_memory > 0, is_(True))

    def test_total_memory(self):
        total_memory = self.vim_client.total_vmusable_memory_mb
        assert_that(total_memory > 0, is_(True))

    def test_total_cpus(self):
        num_cpus = self.vim_client.num_physical_cpus
        assert_that(num_cpus > 0, is_(True))

    def _create_test_vm(self, suffix="host-integ"):
        # Create VM
        vm_id = "vm_%s-%s-%s" % (
            time.strftime("%Y-%m-%d-%H%M%S", time.localtime()),
            str(random.randint(100000, 1000000)),
            suffix)

        datastore = self.vim_client.get_datastore().name
        disk_path = "[%s] %s/disk.vmdk" % (datastore, vm_id)
        create_spec = self.get_create_spec(datastore, vm_id, disk_path)
        folder = self.vim_client.vm_folder
        resource_pool = self.vim_client.root_resource_pool
        task = folder.CreateVm(create_spec, resource_pool, None)
        self.vim_client.wait_for_task(task)
        vm = self.vim_client.get_vm(vm_id)
        return (vm_id, vm, datastore, disk_path)

    def test_get_cached_vm(self):
        vm_id, vm, datastore, disk_path = self._create_test_vm("vm-cache-test")

        # Verify VM is in cache
        vms = self.vim_client.get_vms_in_cache()
        found_vms = [v for v in vms if v.name == vm_id]
        assert_that(len(found_vms), is_(1))
        assert_that(found_vms[0].name, is_(vm_id))
        assert_that(found_vms[0].power_state, is_(PowerState.poweredOff))
        assert_that(found_vms[0].memory_mb, is_(64))
        assert_that(found_vms[0].path, starts_with("[%s]" % datastore))
        assert_that(len(found_vms[0].disks), is_(1))
        assert_that(found_vms[0].disks[0], is_(disk_path))

        # Make sure get_vm_in_cache works
        vm_from_cache = self.vim_client.get_vm_in_cache(vm_id)
        assert_that(vm_from_cache.name, is_(vm_id))
        self.assertRaises(VmNotFoundException,
                          self.vim_client.get_vm_in_cache, "missing")

        # Add disk
        disk2_path = "[%s] %s/disk2.vmdk" % (datastore, vm_id)
        update_spec = self.get_update_spec(vm, disk2_path)
        task = vm.ReconfigVM_Task(update_spec)
        self.vim_client.wait_for_task(task)

        # For the ReconfigVM task to remove disk, the hostd could update
        # task status to success before updating VM status. Thus when
        # wait_for_task returns, the vm_cache is possible to be still in old
        # state, though eventually it converges to consistent state. It only
        # happens in this task AFAIK. It should be fine for this task, because
        # rarely there is other operation that depends on this task.
        self._wait_vm_has_disk(vm_id, 2)

        # Verify disk added
        vms = self.vim_client.get_vms_in_cache()
        found_vms = [v for v in vms if v.name == vm_id]
        assert_that(len(found_vms[0].disks), is_(2))
        assert_that(found_vms[0].disks,
                    contains_inanyorder(disk_path, disk2_path))

        # Remove disk
        vm = self.vim_client.get_vm(vm_id)
        remove_spec = self.get_remove_spec(vm, disk2_path)
        task = vm.ReconfigVM_Task(remove_spec)
        self.vim_client.wait_for_task(task)

        # Same as before when disk is added
        self._wait_vm_has_disk(vm_id, 1)

        # Verify disk removed
        vms = self.vim_client.get_vms_in_cache()
        found_vms = [v for v in vms if v.name == vm_id]
        assert_that(len(found_vms), is_(1))
        assert_that(len(found_vms[0].disks), is_(1), "disk2 in " +
                                                     str(found_vms[0].disks))
        assert_that(found_vms[0].disks,
                    contains_inanyorder(disk_path))

        # Power on vm
        task = vm.PowerOn()
        self.vim_client.wait_for_task(task)

        # Wait until it disappears from the cache
        self._wait_vm_power_status(vm_id, PowerState.poweredOn)

        # Verify VM state in cache is updated
        vms = self.vim_client.get_vms_in_cache()
        found_vms = [v for v in vms if v.name == vm_id]
        assert_that(len(found_vms), is_(1))
        assert_that(found_vms[0].power_state, is_(PowerState.poweredOn))
        assert_that(found_vms[0].name, is_(vm_id))
        assert_that(found_vms[0].memory_mb, is_(64))
        assert_that(found_vms[0].path, starts_with("[%s]" % datastore))
        assert_that(len(found_vms[0].disks), is_(1))
        assert_that(found_vms[0].disks[0], is_(disk_path))

        # Destroy VM
        task = vm.PowerOff()
        self.vim_client.wait_for_task(task)
        task = vm.Destroy()
        self.vim_client.wait_for_task(task)

        # Verify VM is deleted from cache
        vms = self.vim_client.get_vms_in_cache()
        found_vms = [v for v in vms if v.name == vm_id]
        assert_that(len(found_vms), is_(0))

    def test_no_datastore_update(self):
        """ Test datastore update is no longer triggered on VM creates/deletes
        """

        class UpdateListener(object):
            def __init__(self):
                self._ds_update_count = 0

            def datastores_updated(self):
                self._ds_update_count += 1

            def networks_updated(self):
                pass

            def virtual_machines_updated(self):
                pass

        listener = UpdateListener()
        self.vim_client.add_update_listener(listener)
        # listener always gets updated once on add
        assert_that(listener._ds_update_count, is_(1))

        mock_apply = MagicMock(wraps=self.vim_client._apply_ds_update)
        self.vim_client._apply_ds_update = mock_apply

        _, vm, _, _ = self._create_test_vm("ds-update-test")
        task = vm.Destroy()
        self.vim_client.wait_for_task(task)

        # expect to get a datastore property update (unfortunately) ...
        for _ in xrange(50):
            if mock_apply.call_count > 0:
                break
            time.sleep(0.1)
        # ... but that additional datastore updated notifications are sent out
        # as a result
        assert_that(listener._ds_update_count, is_(1))

    def get_create_spec(self, datastore, vm_id, disk_path):
        create_spec = vim.vm.ConfigSpec(
            name=vm_id,
            guestId="otherGuest",
            memoryMB=64,
            numCPUs=2,
            files=vim.vm.FileInfo(vmPathName="[%s] /" % datastore),
            deviceChange=[],
        )
        controller = vim.vm.device.VirtualLsiLogicController(
            key=1,
            sharedBus=vim.vm.device.VirtualSCSIController.Sharing.noSharing,
            busNumber=2,
            unitNumber=-1)
        self.vm_config.add_device(create_spec, controller)
        backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo(
            fileName=disk_path,
            diskMode=vim.vm.device.VirtualDiskOption.DiskMode.persistent
        )
        disk = vim.vm.device.VirtualDisk(
            controllerKey=1,
            key=-1,
            unitNumber=-1,
            backing=backing,
            capacityInKB=1024,
        )
        self.vm_config.create_device(create_spec, disk)
        return create_spec

    def get_update_spec(self, vm_info, disk_path):
        update_spec = vim.vm.ConfigSpec()
        backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo(
            fileName=disk_path,
            diskMode=vim.vm.device.VirtualDiskOption.DiskMode.persistent
        )
        controller = \
            self.vm_config._find_scsi_controller(update_spec,
                                                 vm_info.config)
        disk = vim.vm.device.VirtualDisk(
            controllerKey=controller.key,
            key=-1,
            unitNumber=-1,
            backing=backing,
            capacityInKB=1024,
        )
        self.vm_config.create_device(update_spec, disk)
        return update_spec

    def get_remove_spec(self, vm_info, disk_path):
        remove_spec = vim.vm.ConfigSpec()
        devices = self.vm_config.get_devices_from_config(vm_info.config)
        found_device = None
        for device in devices:
            if isinstance(device, vim.vm.device.VirtualDisk) and \
                    device.backing.fileName.endswith(disk_path):
                found_device = device
        self.vm_config.remove_device(remove_spec, found_device)
        return remove_spec

    def test_clone_ticket(self):
        ticket = self.vim_client.acquire_clone_ticket()
        vim_client2 = VimClient(host=self.host, ticket=ticket)
        vim_client2.host_system

    def test_http_ticket(self):
        datastore = self.vim_client.get_datastore().name
        filename = "%s.bin" % str(uuid.uuid4())
        quoted_dc_name = 'ha%252ddatacenter'
        url = 'https://%s/folder/%s?dcPath=%s&dsName=%s' % (
            self.host, filename, quoted_dc_name, datastore)

        ticket = self.vim_client.acquire_cgi_ticket(url, HttpOp.PUT)
        assert_that(ticket, is_not(equal_to(None)))

    def test_host_stats(self):
        """ Skip host stats test.
        This test does not agree with the contract exposed from
        the implementation.
        Until the vim_client code be refactor/cleanup, disable this test for
        now.
        """
        raise SkipTest()

        self.vim_client.initialize_host_counters()
        self.vim_client.update_hosts_stats()
        stats = self.vim_client.get_host_stats()
        assert_that(has_key('mem.consumed'))
        assert_that(stats['mem.consumed'], greater_than(0))
        assert_that(has_key('rescpu.actav1'))
        assert_that(stats['rescpu.actav1'], greater_than(0))

    def _wait_vm_has_disk(self, vm_id, disk_num):
        """Wait until the vm has disk number of the vm becomes disk_num
        """
        now = time.time()
        for _ in xrange(50):
            vm_in_cache = self.vim_client.get_vm_in_cache(vm_id)
            if len(vm_in_cache.disks) == disk_num:
                self._logger.info("VmCache disk number synced in %.2f second" %
                                  (time.time() - now))
                break
            time.sleep(0.1)

    def _wait_vm_power_status(self, vm_id, power_state):
        """Wait until the vm has power_state
        """
        now = time.time()
        for _ in xrange(50):
            vm_in_cache = self.vim_client.get_vm_in_cache(vm_id)
            if vm_in_cache.power_state == power_state:
                self._logger.info("VmCache power_state synced in %.2f second" %
                                  (time.time() - now))
                break
            time.sleep(0.1)
class TestEsxVmConfig(unittest.TestCase):
    @patch.object(VimClient, "acquire_credentials")
    @patch.object(VimClient, "update_cache")
    @patch("pysdk.connect.Connect")
    def setUp(self, connect, update, creds):
        creds.return_value = ["username", "password"]
        self.vim_client = VimClient(auto_sync=False)
        with patch("host.hypervisor.esx.vm_config.GetEnv"):
            self.vm_config = EsxVmConfig(self.vim_client)

    def tearDown(self):
        self.vim_client.disconnect(wait=True)

    def dummy_devices(self):
        return [
            vim.vm.device.VirtualFloppy(key=10),
            vim.vm.device.VirtualPCIController(key=100),
            DEFAULT_DISK_CONTROLLER_CLASS(key=1000),
            vim.vm.device.VirtualSoundCard(key=10000),
        ]

    def test_vm_create_spec(self):
        datastore = "ds1"
        vm_id = str(uuid.uuid4())
        metadata = {
            "configuration": {"guestOS": "otherLinuxGuest"},
            "parameters": [{"name": "key1"}, {"name": "key2"}]
        }
        env = {
            "key1": "value1",
            "keyUnexpected": "valueNotSet",
        }
        spec = self.vm_config.create_spec(vm_id, datastore, 512, 1, metadata,
                                          env)
        assert_that(spec.memoryMB, equal_to(512))
        assert_that(spec.numCPUs, equal_to(1))
        assert_that(spec.name, equal_to(vm_id))
        assert_that(spec.guestId, equal_to("otherLinuxGuest"))
        expected_metadata = {'guestOS': 'otherLinuxGuest', 'key1': 'value1'}
        assert_that(spec._metadata, equal_to(expected_metadata))

    def test_create_nic_spec(self):
        net_name = "VM_network"
        cspec = self.vm_config.update_spec()
        spec = self.vm_config.add_nic(cspec, net_name)
        backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo
        assert_that(spec.deviceChange[0].device.backing.__class__,
                    equal_to(backing))
        assert_that(spec.deviceChange[0].device.backing.deviceName,
                    equal_to(net_name))

    def test_find_disk_controller(self):
        devices = self.dummy_devices()
        device_type = DEFAULT_DISK_CONTROLLER_CLASS
        disk_controller = self.vm_config.find_device(devices, device_type)
        assert_that(disk_controller.key, equal_to(1000))

    def test_find_nic_controller(self):
        devices = self.dummy_devices()
        device_type = vim.vm.device.VirtualPCIController
        disk_controller = self.vm_config.find_device(devices, device_type)
        assert_that(disk_controller.key, equal_to(100))

    def test_find_virtual_disk(self):
        spec = vim.vm.ConfigSpec()
        vm_config = self.vm_config
        devices = self.dummy_devices()
        for device in devices:
            vm_config.add_device(spec, device)
        cfg_info = FakeConfigInfo()
        device_type = vim.vm.device.VirtualDisk
        datastore = "ds1"
        filename = "folder/foo"
        path = vmdk_path(datastore, filename)

        find_disk = vm_config.disk_matcher(datastore, filename)
        disk = vm_config.find_device(devices, device_type, matcher=find_disk)
        assert_that(disk, equal_to(None))

        vm_config.add_scsi_disk(cfg_info, spec, datastore, "nope")

        self.assertRaises(DeviceNotFoundException, vm_config.get_device,
                          devices, device_type, matcher=find_disk)

        vm_config.add_scsi_disk(cfg_info, spec, datastore,
                                filename)
        device_changes = spec.deviceChange
        device_list = []
        for device_change in device_changes:
            device_list.append(device_change.device)

        disk = vm_config.find_device(device_list, device_type,
                                     matcher=find_disk)
        assert_that(disk.backing.fileName, equal_to(path))

    def _create_spec_for_disk_test(self, datastore, vm_id):
        spec = vim.vm.ConfigSpec()
        devices = self.dummy_devices()
        for device in devices:
            self.vm_config.add_device(spec, device)
        vm_path_name = '[%s] %s/%s' % (datastore, vm_id[0:2], vm_id)
        spec.files = vim.vm.FileInfo(vmPathName=vm_path_name)
        spec.name = vm_id
        return spec

    def test_create_empty_disk(self):
        vm_id = str(uuid.uuid4())
        datastore = "ds1"
        spec = self._create_spec_for_disk_test(datastore, vm_id)

        size_mb = 100
        disk_id = str(uuid.uuid4())
        self.vm_config.create_empty_disk(spec, datastore, disk_id, size_mb)

        devs = [change.device for change in spec.deviceChange]
        device_type = vim.vm.device.VirtualDisk
        disks = self.vm_config.find_devices(devs, device_type)
        assert_that(len(disks), equal_to(1))
        # verify that uuid to be set on disk to be added matches the
        # of the disk (modulo some formatting differences)
        assert_that(disks[0].backing.uuid,
                    equal_to(uuid_to_vmdk_uuid(disk_id)))

    def test_create_child_disk(self):
        vm_id = str(uuid.uuid4())
        datastore = "ds1"
        spec = self._create_spec_for_disk_test(datastore, vm_id)

        disk_id = str(uuid.uuid4())
        parent_id = str(uuid.uuid4())
        self.vm_config.create_child_disk(spec, datastore, disk_id, parent_id)

        devs = [change.device for change in spec.deviceChange]
        device_type = vim.vm.device.VirtualDisk
        disks = self.vm_config.find_devices(devs, device_type)
        assert_that(len(disks), equal_to(1))
        # verify that disk to be added does not request a specifc uuid
        assert_that(disks[0].backing.uuid, equal_to(None))

    def _get_config_info_with_iso(self, iso_path):
        devices = self.dummy_devices()
        cfg_info = FakeConfigInfo()
        cfg_info.hardware.device = devices

        cdrom = vim.vm.device.VirtualCdrom()
        cdrom.key = 1234
        cdrom.controllerKey = 100
        cdrom.unitNumber = 1

        iso_backing = vim.vm.device.VirtualCdrom.IsoBackingInfo()
        iso_backing.fileName = iso_path
        cdrom.backing = iso_backing

        conInfo = vim.vm.device.VirtualDevice.ConnectInfo()
        conInfo.allowGuestControl = True
        conInfo.connected = True
        conInfo.startConnected = True
        cdrom.connectable = conInfo
        cfg_info.hardware.device.append(cdrom)
        return cfg_info

    def _get_config_info_without_connected(self, is_iso_backing):
        devices = self.dummy_devices()
        cfg_info = FakeConfigInfo()
        cfg_info.hardware.device = devices

        cdrom = vim.vm.device.VirtualCdrom()
        cdrom.key = 1234
        cdrom.controllerKey = 100
        cdrom.unitNumber = 1

        if is_iso_backing:
            iso_backing = vim.vm.device.VirtualCdrom.IsoBackingInfo()
            cdrom.backing = iso_backing

        conInfo = vim.vm.device.VirtualDevice.ConnectInfo()
        conInfo.allowGuestControl = True
        conInfo.connected = False
        conInfo.startConnected = True
        cdrom.connectable = conInfo
        cfg_info.hardware.device.append(cdrom)
        return cfg_info

    def test_add_iso_cdrom(self):
        virtual_ide_controller = vim.vm.device.VirtualIDEController()
        cfgOption = vim.vm.ConfigOption()
        cfgOption.defaultDevice.append(virtual_ide_controller)
        self.vm_config._cfg_opts = cfgOption
        # fake iso ds path
        fake_iso_ds_path = '[ds] vms/fa/fake/fake.iso'

        # test if no virtual cdrom attached to the VM
        cfg_info = FakeConfigInfo()

        cspec = self.vm_config.update_spec()

        result = self.vm_config.add_iso_cdrom(
            cspec,
            fake_iso_ds_path,
            cfg_info)

        assert_that(result.__class__,
                    equal_to(bool))
        assert_that(result, equal_to(True))

        dev = cspec.deviceChange[0].device
        assert_that(len(cspec.deviceChange), equal_to(1))
        assert_that(dev.connectable.connected, equal_to(True))
        assert_that(dev.connectable.startConnected, equal_to(True))
        assert_that(dev.backing.__class__,
                    equal_to(vim.vm.device.VirtualCdrom.IsoBackingInfo))

        # test if virtual cdrom exist and ISO already attached to the VM
        cspec = self.vm_config.update_spec()

        cfg_info = self._get_config_info_with_iso(fake_iso_ds_path)

        result = self.vm_config.add_iso_cdrom(
            cspec,
            fake_iso_ds_path,
            cfg_info)

        assert_that(result.__class__,
                    equal_to(bool))
        assert_that(result, equal_to(False))

        # test if virtual cdrom exist and it's iso_backing
        # and ISO is not attached to the VM
        cspec = self.vm_config.update_spec()

        cfg_info = self._get_config_info_without_connected(is_iso_backing=True)

        result = self.vm_config.add_iso_cdrom(
            cspec,
            fake_iso_ds_path,
            cfg_info)

        assert_that(result.__class__,
                    equal_to(bool))
        assert_that(result, equal_to(True))

        dev = cspec.deviceChange[0].device
        assert_that(len(cspec.deviceChange), equal_to(1))
        assert_that(dev.connectable.connected, equal_to(True))
        assert_that(dev.connectable.startConnected, equal_to(True))
        assert_that(dev.backing.__class__,
                    equal_to(vim.vm.device.VirtualCdrom.IsoBackingInfo))

        # test if virtual cdrom exist and it's _not_ iso_backing
        # and ISO is not attached to the VM
        cspec = self.vm_config.update_spec()

        cfg_info = self._get_config_info_without_connected(
            is_iso_backing=False)

        self.assertRaises(TypeError,
                          self.vm_config.add_iso_cdrom,
                          cspec, fake_iso_ds_path, cfg_info)

    def test_disconnect_iso(self):
        # on vm config with no cdrom devices
        cfg_info = FakeConfigInfo()
        cspec = self.vm_config.update_spec()
        self.assertRaises(DeviceNotFoundException,
                          self.vm_config.disconnect_iso_cdrom,
                          cspec, cfg_info)
        assert_that(len(cspec.deviceChange), equal_to(0))

        # on vm config with no a fake cdrom device
        fake_iso_ds_path = '[ds] vms/fa/fake/fake.iso'
        cspec = self.vm_config.update_spec()
        cfg_info = self._get_config_info_with_iso(fake_iso_ds_path)
        iso_path = self.vm_config.disconnect_iso_cdrom(cspec, cfg_info)

        assert_that(len(cspec.deviceChange), equal_to(1))
        dev = cspec.deviceChange[0].device
        assert_that(dev.backing.__class__,
                    equal_to(vim.vm.device.VirtualCdrom.IsoBackingInfo))
        assert_that(dev.backing.fileName,
                    equal_to(fake_iso_ds_path))

        assert_that(iso_path, equal_to(fake_iso_ds_path))
        assert_that(dev.connectable.connected, equal_to(False))
        assert_that(dev.connectable.startConnected, equal_to(False))

    def test_remove_iso_cdrom_device(self):
        fake_iso_ds_path = '[ds] vms/fa/fake/fake.iso'
        cspec = self.vm_config.update_spec()
        cfg_info = self._get_config_info_with_iso(fake_iso_ds_path)
        self.vm_config.remove_iso_cdrom(cspec, cfg_info)

        assert_that(len(cspec.deviceChange), equal_to(1))
        assert_that(cspec.deviceChange[0].operation, equal_to('remove'))
        dev = cspec.deviceChange[0].device
        assert_that(dev.backing.__class__,
                    equal_to(vim.vm.device.VirtualCdrom.IsoBackingInfo))
        assert_that(dev.backing.fileName,
                    equal_to(fake_iso_ds_path))

    def test_update_spec(self):
        cfg_info = FakeConfigInfo()
        spec = self.vm_config.update_spec()
        assert_that(len(spec.deviceChange), equal_to(0))
        net_name = "VM_Network"
        self.vm_config.add_nic(spec, net_name)
        assert_that(len(spec.deviceChange), equal_to(1))
        self.vm_config.add_scsi_disk(cfg_info, spec, "ds1", "foo")
        # One for the controller and one for the disk itself.
        assert_that(len(spec.deviceChange), equal_to(3))

    def test_path_conversion_invalid(self):
        self.assertRaises(IndexError, datastore_to_os_path, "invalid_ds_path")

    @parameterized.expand([
        ('[foo] a/b/c.vmdk', '/vmfs/volumes/foo/a/b/c.vmdk'),
        ('[foo] c.vmdk', '/vmfs/volumes/foo/c.vmdk'),
        ('[foo]a', '/vmfs/volumes/foo/a'),
        ('/vmfs/volumes/foo/bar.vmdk', '/vmfs/volumes/foo/bar.vmdk'),
        ('[]/vmfs/volumes/foo/bar.vmdk', '/vmfs/volumes/foo/bar.vmdk'),
        ('[] /vmfs/volumes/foo/bar.vmdk', '/vmfs/volumes/foo/bar.vmdk')
    ])
    def test_path_conversion(self, ds_path, expected_os_path):
        path = datastore_to_os_path(ds_path)
        assert_that(path, equal_to(expected_os_path))

    @parameterized.expand([
        (['[foo] images/a/b/c.vmdk'], True, False, False),
        (['[foo] vms/a/b/c.vmdk'], False, True, False),
        (['[foo] images/a/b/c.vmdk', '[foo] vms/a.vmdk'], False, True, False),
        (['[foo] disks/a/b/c.vmdk'], False, False, True),
        (['[foo] images/a/c.vmdk', '[foo] disks/a.vmdk'], False, False, True),
        ([], False, False, False)
    ])
    def test_is_what_disk(self, disk_files, image, ephemeral, persistent):
        assert_that(is_image(disk_files), equal_to(image))
        assert_that(is_ephemeral_disk(disk_files), equal_to(ephemeral))
        assert_that(is_persistent_disk(disk_files), equal_to(persistent))

    @parameterized.expand([
        ("ds1", "image_id",
         "/vmfs/volumes/ds1/images/im/image_id/image_id.manifest"),
        ("123 456", "image_id",
         "/vmfs/volumes/123 456/images/im/image_id/image_id.manifest"),
    ])
    def test_os_image_manifest_path(self, datastore, image_id, expected):
        assert_that(os_image_manifest_path(datastore, image_id),
                    equal_to(expected))

    def test_vmdk_uuid_conversion(self):
        for id in ['01234567-89ab-cedf-0123-456789abcdef',
                   '01 23456 789ABCEDF0123456789ABCDEF',
                   '01 23 45 67 89 ab ce df-01 23 45 67 89 ab cd ef',
                   '0123456789abcedf0123456789abcdef']:
            vmdk_uuid = uuid_to_vmdk_uuid(id)
            assert_that(
                vmdk_uuid,
                equal_to('01 23 45 67 89 ab ce df-01 23 45 67 89 ab cd ef'))
        for id in ['',
                   '01234567-89ab-cedf-0123-456789abcd',
                   '01 23456 789abcedf0123456789abcdefabcd']:
            self.assertRaises(ValueError, uuid_to_vmdk_uuid, id)
Пример #4
0
class TestVimClient(unittest.TestCase):
    def setUp(self):
        if "host_remote_test" not in config:
            raise SkipTest()

        self.host = config["host_remote_test"]["server"]
        self.pwd = config["host_remote_test"]["esx_pwd"]

        if self.host is None or self.pwd is None:
            raise SkipTest()

        self.vim_client = VimClient(self.host, "root", self.pwd,
                                    auto_sync=True)
        self.vm_config = EsxVmConfig(self.vim_client)
        self._logger = logging.getLogger(__name__)

    def tearDown(self):
        self.vim_client.disconnect(wait=True)

    def test_memory_usage(self):
        used_memory = self.vim_client.memory_usage_mb
        assert_that(used_memory > 0, is_(True))

    def test_total_memory(self):
        total_memory = self.vim_client.total_vmusable_memory_mb
        assert_that(total_memory > 0, is_(True))

    def test_total_cpus(self):
        num_cpus = self.vim_client.num_physical_cpus
        assert_that(num_cpus > 0, is_(True))

    def _create_test_vm(self, suffix="host-integ"):
        # Create VM
        vm_id = "%s-%s-%s" % (
            time.strftime("%Y-%m-%d-%H%M%S", time.localtime()),
            str(random.randint(100000, 1000000)),
            suffix)

        datastore = self.vim_client.get_datastore().name
        disk_path = "[%s] %s/disk.vmdk" % (datastore, vm_id)
        create_spec = self.get_create_spec(datastore, vm_id, disk_path)
        folder = self.vim_client.vm_folder
        resource_pool = self.vim_client.root_resource_pool
        task = folder.CreateVm(create_spec, resource_pool, None)
        self.vim_client.wait_for_task(task)
        vm = self.vim_client.get_vm(vm_id)
        return (vm_id, vm, datastore, disk_path)

    def test_get_cached_vm(self):
        vm_id, vm, datastore, disk_path = self._create_test_vm("vm-cache-test")

        # Verify VM is in cache
        vms = self.vim_client.get_vms_in_cache()
        found_vms = [v for v in vms if v.name == vm_id]
        assert_that(len(found_vms), is_(1))
        assert_that(found_vms[0].name, is_(vm_id))
        assert_that(found_vms[0].power_state, is_(PowerState.poweredOff))
        assert_that(found_vms[0].memory_mb, is_(64))
        assert_that(found_vms[0].path, starts_with("[%s]" % datastore))
        assert_that(len(found_vms[0].disks), is_(1))
        assert_that(found_vms[0].disks[0], is_(disk_path))

        # Make sure get_vm_in_cache works
        vm_from_cache = self.vim_client.get_vm_in_cache(vm_id)
        assert_that(vm_from_cache.name, is_(vm_id))
        self.assertRaises(VmNotFoundException,
                          self.vim_client.get_vm_in_cache, "missing")

        # Add disk
        disk2_path = "[%s] %s/disk2.vmdk" % (datastore, vm_id)
        update_spec = self.get_update_spec(vm, disk2_path)
        task = vm.ReconfigVM_Task(update_spec)
        self.vim_client.wait_for_task(task)

        # For the ReconfigVM task to remove disk, the hostd could update
        # task status to success before updating VM status. Thus when
        # wait_for_task returns, the vm_cache is possible to be still in old
        # state, though eventually it converges to consistent state. It only
        # happens in this task AFAIK. It should be fine for this task, because
        # rarely there is other operation that depends on this task.
        self._wait_vm_has_disk(vm_id, 2)

        # Verify disk added
        vms = self.vim_client.get_vms_in_cache()
        found_vms = [v for v in vms if v.name == vm_id]
        assert_that(len(found_vms[0].disks), is_(2))
        assert_that(found_vms[0].disks,
                    contains_inanyorder(disk_path, disk2_path))

        # Remove disk
        vm = self.vim_client.get_vm(vm_id)
        remove_spec = self.get_remove_spec(vm, disk2_path)
        task = vm.ReconfigVM_Task(remove_spec)
        self.vim_client.wait_for_task(task)

        # Same as before when disk is added
        self._wait_vm_has_disk(vm_id, 1)

        # Verify disk removed
        vms = self.vim_client.get_vms_in_cache()
        found_vms = [v for v in vms if v.name == vm_id]
        assert_that(len(found_vms), is_(1))
        assert_that(len(found_vms[0].disks), is_(1), "disk2 in " +
                                                     str(found_vms[0].disks))
        assert_that(found_vms[0].disks,
                    contains_inanyorder(disk_path))

        # Power on vm
        task = vm.PowerOn()
        self.vim_client.wait_for_task(task)

        # Wait until it disappears from the cache
        self._wait_vm_power_status(vm_id, PowerState.poweredOn)

        # Verify VM state in cache is updated
        vms = self.vim_client.get_vms_in_cache()
        found_vms = [v for v in vms if v.name == vm_id]
        assert_that(len(found_vms), is_(1))
        assert_that(found_vms[0].power_state, is_(PowerState.poweredOn))
        assert_that(found_vms[0].name, is_(vm_id))
        assert_that(found_vms[0].memory_mb, is_(64))
        assert_that(found_vms[0].path, starts_with("[%s]" % datastore))
        assert_that(len(found_vms[0].disks), is_(1))
        assert_that(found_vms[0].disks[0], is_(disk_path))

        # Destroy VM
        task = vm.PowerOff()
        self.vim_client.wait_for_task(task)
        task = vm.Destroy()
        self.vim_client.wait_for_task(task)

        # Verify VM is deleted from cache
        vms = self.vim_client.get_vms_in_cache()
        found_vms = [v for v in vms if v.name == vm_id]
        assert_that(len(found_vms), is_(0))

    def test_no_datastore_update(self):
        """ Test datastore update is no longer triggered on VM creates/deletes
        """

        class UpdateListener(object):
            def __init__(self):
                self._ds_update_count = 0

            def datastores_updated(self):
                self._ds_update_count += 1

            def networks_updated(self):
                pass

            def virtual_machines_updated(self):
                pass

        listener = UpdateListener()
        self.vim_client.add_update_listener(listener)
        # listener always gets updated once on add
        assert_that(listener._ds_update_count, is_(1))

        mock_apply = MagicMock(wraps=self.vim_client._apply_ds_update)
        self.vim_client._apply_ds_update = mock_apply

        _, vm, _, _ = self._create_test_vm("ds-update-test")
        task = vm.Destroy()
        self.vim_client.wait_for_task(task)

        # expect to get a datastore property update (unfortunately) ...
        for _ in xrange(50):
            if mock_apply.call_count > 0:
                break
            time.sleep(0.1)
        # ... but that additional datastore updated notifications are sent out
        # as a result
        assert_that(listener._ds_update_count, is_(1))

    def get_create_spec(self, datastore, vm_id, disk_path):
        create_spec = vim.vm.ConfigSpec(
            name=vm_id,
            guestId="otherGuest",
            memoryMB=64,
            numCPUs=2,
            files=vim.vm.FileInfo(vmPathName="[%s] /" % datastore),
            deviceChange=[],
        )
        controller = vim.vm.device.VirtualLsiLogicController(
            key=1,
            sharedBus=vim.vm.device.VirtualSCSIController.Sharing.noSharing,
            busNumber=2,
            unitNumber=-1)
        self.vm_config.add_device(create_spec, controller)
        backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo(
            fileName=disk_path,
            diskMode=vim.vm.device.VirtualDiskOption.DiskMode.persistent
        )
        disk = vim.vm.device.VirtualDisk(
            controllerKey=1,
            key=-1,
            unitNumber=-1,
            backing=backing,
            capacityInKB=1024,
        )
        self.vm_config.create_device(create_spec, disk)
        return create_spec

    def get_update_spec(self, vm_info, disk_path):
        update_spec = vim.vm.ConfigSpec()
        backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo(
            fileName=disk_path,
            diskMode=vim.vm.device.VirtualDiskOption.DiskMode.persistent
        )
        controller = \
            self.vm_config._find_scsi_controller(update_spec,
                                                 vm_info.config)
        disk = vim.vm.device.VirtualDisk(
            controllerKey=controller.key,
            key=-1,
            unitNumber=-1,
            backing=backing,
            capacityInKB=1024,
        )
        self.vm_config.create_device(update_spec, disk)
        return update_spec

    def get_remove_spec(self, vm_info, disk_path):
        remove_spec = vim.vm.ConfigSpec()
        devices = self.vm_config.get_devices_from_config(vm_info.config)
        found_device = None
        for device in devices:
            if isinstance(device, vim.vm.device.VirtualDisk) and \
                    device.backing.fileName.endswith(disk_path):
                found_device = device
        self.vm_config.remove_device(remove_spec, found_device)
        return remove_spec

    def test_clone_ticket(self):
        ticket = self.vim_client.acquire_clone_ticket()
        vim_client2 = VimClient(host=self.host, ticket=ticket)
        vim_client2.host_system

    def test_http_ticket(self):
        datastore = self.vim_client.get_datastore().name
        filename = "%s.bin" % str(uuid.uuid4())
        quoted_dc_name = 'ha%252ddatacenter'
        url = 'https://%s/folder/%s?dcPath=%s&dsName=%s' % (
            self.host, filename, quoted_dc_name, datastore)

        ticket = self.vim_client.acquire_cgi_ticket(url, HttpOp.PUT)
        assert_that(ticket, is_not(equal_to(None)))

    def test_host_stats(self):
        """ Skip host stats test.
        This test does not agree with the contract exposed from
        the implementation.
        Until the vim_client code be refactor/cleanup, disable this test for
        now.
        """
        raise SkipTest()

        self.vim_client.initialize_host_counters()
        self.vim_client.update_hosts_stats()
        stats = self.vim_client.get_host_stats()
        assert_that(has_key('mem.consumed'))
        assert_that(stats['mem.consumed'], greater_than(0))
        assert_that(has_key('rescpu.actav1'))
        assert_that(stats['rescpu.actav1'], greater_than(0))

    def _wait_vm_has_disk(self, vm_id, disk_num):
        """Wait until the vm has disk number of the vm becomes disk_num
        """
        now = time.time()
        for _ in xrange(50):
            vm_in_cache = self.vim_client.get_vm_in_cache(vm_id)
            if len(vm_in_cache.disks) == disk_num:
                self._logger.info("VmCache disk number synced in %.2f second" %
                                  (time.time() - now))
                break
            time.sleep(0.1)

    def _wait_vm_power_status(self, vm_id, power_state):
        """Wait until the vm has power_state
        """
        now = time.time()
        for _ in xrange(50):
            vm_in_cache = self.vim_client.get_vm_in_cache(vm_id)
            if vm_in_cache.power_state == power_state:
                self._logger.info("VmCache power_state synced in %.2f second" %
                                  (time.time() - now))
                break
            time.sleep(0.1)