class TestVimClient(unittest.TestCase):

    def setUp(self):
        self.vim_client = VimClient(auto_sync=False)
        self.vim_client._content = MagicMock()

    def test_import_pyvmomi(self):
        from pyVmomi import VmomiSupport
        assert_that(getattr(VmomiSupport, "BASE_VERSION", None), not_none())

    @patch("pysdk.connect.Connect")
    def test_hostd_connect_failure(self, connect_mock):
        connect_mock.side_effect = vim.fault.HostConnectFault
        vim_client = VimClient(auto_sync=False)
        self.assertRaises(HostdConnectionFailure, vim_client.connect_userpwd, "localhost", "username", "password")

    @patch("pysdk.connect.Connect")
    def test_vim_client_with_param(self, connect_mock):
        vim_client = VimClient(auto_sync=False)
        vim_client.connect_userpwd("esx.local", "root", "password")
        connect_mock.assert_called_once_with(host="esx.local", user="******", pwd="password",
                                             version="vim.version.version9")

    @patch.object(VimCache, "poll_updates")
    @patch("pysdk.connect.Connect")
    def test_update_fail_without_looping(self, connect_mock, update_mock):
        client = VimClient(auto_sync=True, min_interval=1)
        client.connect_userpwd("esx.local", "root", "password")
        update_mock.side_effect = vim.fault.HostConnectFault
        time.sleep(0.5)
        client.disconnect()
        assert_that(update_mock.call_count, less_than(5))  # no crazy loop

    @patch.object(VimCache, "poll_updates")
    @patch("pysdk.connect.Connect")
    @patch("time.sleep")
    def test_update_fail_will_suicide(self, sleep_mock, connect_mock, update_mock):
        killed = threading.Event()

        def suicide():
            killed.set()
            threading.current_thread().stop()

        poll_updates = MagicMock()
        poll_updates.side_effect = vim.fault.HostConnectFault

        client = VimClient(auto_sync=True, min_interval=1, errback=lambda: suicide())
        client.connect_userpwd("esx.local", "root", "password")
        client._vim_cache.poll_updates = poll_updates

        killed.wait(1)
        client.disconnect()

        # poll_updates will be called 5 times before it kill itself
        assert_that(poll_updates.call_count, is_(5))
        assert_that(killed.is_set(), is_(True))

    @patch.object(VimCache, "_build_filter_spec")
    @patch("pysdk.connect.Connect")
    def test_update_cache(self, connect_mock, spec_mock):
        vim_client = VimClient(auto_sync=False)
        vim_client.connect_userpwd("esx.local", "root", "password")
        vim_client._property_collector.WaitForUpdatesEx.return_value = {}
        vim_client._vim_cache = VimCache()

        # Test enter
        update = vmodl.query.PropertyCollector.UpdateSet(version="1")
        filter = vmodl.query.PropertyCollector.FilterUpdate()
        update.filterSet.append(filter)
        object_update = vmodl.query.PropertyCollector.ObjectUpdate(
            kind="enter",
            obj=vim.VirtualMachine("vim.VirtualMachine:9"),
        )
        filter.objectSet.append(object_update)
        object_update.changeSet.append(
                vmodl.query.PropertyCollector.Change(name="name", op="assign", val="agent4"))
        object_update.changeSet.append(
                vmodl.query.PropertyCollector.Change(name="runtime.powerState", op="assign", val="poweredOff"))
        object_update.changeSet.append(
            vmodl.query.PropertyCollector.Change(name="config", op="assign", val=vim.vm.ConfigInfo(
                files=vim.vm.FileInfo(vmPathName="[datastore2] agent4/agent4.vmx"),
                hardware=vim.vm.VirtualHardware(memoryMB=4096),
                locationId="location1"),
            ))
        disk_list = vim.vm.FileLayout.DiskLayout.Array()
        disk_list.append(vim.vm.FileLayout.DiskLayout(diskFile=["disk1"]))
        disk_list.append(vim.vm.FileLayout.DiskLayout(diskFile=["disk2"]))
        object_update.changeSet.append(
                vmodl.query.PropertyCollector.Change(name="layout.disk", op="assign", val=disk_list))

        vim_client._property_collector.WaitForUpdatesEx.return_value = update
        assert_that(len(vim_client.get_vms_in_cache()), is_(0))
        vim_client._vim_cache.poll_updates(vim_client)
        vim_client._property_collector.WaitForUpdatesEx.assert_called()

        vms = vim_client.get_vms_in_cache()
        assert_that(vim_client._vim_cache._current_version, is_("1"))
        assert_that(len(vms), 1)
        assert_that(vms[0].memory_mb, is_(4096))
        assert_that(vms[0].path, is_("[datastore2] agent4/agent4.vmx"))
        assert_that(vms[0].name, is_("agent4"))
        assert_that(vms[0].power_state, is_(VmPowerState.STOPPED))
        assert_that(len(vms[0].disks), is_(2))
        assert_that(vms[0].disks, contains_inanyorder("disk1", "disk2"))
        assert_that(vms[0].location_id, is_("location1"))

        # Test Modify
        update.version = "2"
        object_update = vmodl.query.PropertyCollector.ObjectUpdate(
            kind="modify",
            obj=vim.VirtualMachine("vim.VirtualMachine:9"),
        )
        filter.objectSet[0] = object_update
        object_update.changeSet.append(
                vmodl.query.PropertyCollector.Change(name="runtime.powerState", op="assign", val="poweredOn"))
        object_update.changeSet.append(
                vmodl.query.PropertyCollector.Change(name="runtime.powerState", op="assign", val="poweredOn"))
        disk_list = vim.vm.FileLayout.DiskLayout.Array()
        disk_list.append(vim.vm.FileLayout.DiskLayout(diskFile=["disk3", "disk4"]))
        object_update.changeSet.append(
                vmodl.query.PropertyCollector.Change(name="layout.disk", op="assign", val=disk_list))

        vim_client._vim_cache.poll_updates(vim_client)
        vms = vim_client.get_vms_in_cache()
        assert_that(vim_client._vim_cache._current_version, is_("2"))
        assert_that(len(vms), is_(1))
        assert_that(vms[0].memory_mb, is_(4096))
        assert_that(vms[0].path, is_("[datastore2] agent4/agent4.vmx"))
        assert_that(vms[0].name, is_("agent4"))
        assert_that(vms[0].power_state, is_(VmPowerState.STARTED))
        assert_that(len(vms[0].disks), is_(2))
        assert_that(vms[0].disks, contains_inanyorder("disk3", "disk4"))

        # Test leave
        update.version = "3"
        object_update = vmodl.query.PropertyCollector.ObjectUpdate(
            kind="leave",
            obj=vim.VirtualMachine("vim.VirtualMachine:9"),
        )
        filter.objectSet[0] = object_update
        vim_client._vim_cache.poll_updates(vim_client)
        vms = vim_client.get_vms_in_cache()
        assert_that(vim_client._vim_cache._current_version, is_("3"))
        assert_that(len(vms), is_(0))

    @patch.object(VimCache, "poll_updates")
    @patch.object(VimCache, "_build_filter_spec")
    @patch("pysdk.connect.Connect")
    @patch("pysdk.connect.Disconnect")
    def test_poll_update_in_thread(self, disconnect_mock, connect_mock, spec_mock, update_mock):
        vim_client = VimClient(min_interval=0, auto_sync=True)
        vim_client.connect_userpwd("esx.local", "root", "password")
        vim_client._property_collector.WaitForUpdatesEx.return_value = {}

        assert_that(update_mock.called, is_(True))
        retry = 0
        while update_mock.call_count < 5 and retry < 10:
            time.sleep(0.2)
            retry += 1
        assert_that(retry, is_not(10), "VimClient._poll_updates is not called repeatedly")
        vim_client.disconnect()
        assert_that(disconnect_mock.called, is_(True))

    @patch("pysdk.host.GetHostSystem")
    @patch("pysdk.connect.Connect")
    def test_vim_client_errback(self, connect_mock, host_mock):
        callback = MagicMock()
        vim_client = VimClient(auto_sync=False, errback=callback)
        vim_client.connect_userpwd("esx.local", "root", "password")
        host_mock.side_effect = vim.fault.NotAuthenticated
        vim_client.host_system
        callback.assert_called_once()

        host_mock.side_effect = vim.fault.HostConnectFault
        vim_client.host_system
        assert_that(callback.call_count, is_(2))

        host_mock.side_effect = vim.fault.InvalidLogin
        vim_client.host_system
        assert_that(callback.call_count, is_(3))

        host_mock.side_effect = AcquireCredentialsException
        vim_client.host_system
        assert_that(callback.call_count, is_(4))

    @patch("pysdk.connect.Connect")
    def test_get_nfc_ticket(self, connect_mock):
        vim_client = VimClient(auto_sync=False)
        vim_client._find_by_inventory_path = MagicMock(return_value=None)
        self.assertRaises(DatastoreNotFound, vim_client.get_nfc_ticket_by_ds_name, "no_exist")

        ds_mock = MagicMock()
        vim_client._find_by_inventory_path = MagicMock(return_value=ds_mock)
        nfc_service = MagicMock()
        type(vim).NfcService = MagicMock()
        type(vim).NfcService.return_value = nfc_service

        vim_client._si = MagicMock()
        vim_client.get_nfc_ticket_by_ds_name("existing_ds")

        nfc_service.FileManagement.assert_called_once_with(ds_mock)

    def test_inventory_path(self):
        """Check that convert to inventory path correctly."""
        tests = [
            {"path": (), "val": "ha-datacenter"},
            {"path": ("network", None), "val": "ha-datacenter/network"},
            {"path": ("vm",), "val": "ha-datacenter/vm"},
            {"path": ("vm", "Cent/OS"), "val": "ha-datacenter/vm/Cent%2fOS"},
        ]
        for test in tests:
            self.vim_client._find_by_inventory_path(*test["path"])
            self.vim_client._content.searchIndex.FindByInventoryPath.assert_called_once_with(test["val"])
            self.vim_client._content.searchIndex.FindByInventoryPath.reset_mock()

    def test_get_vm_not_found(self):
        """Test that if the vm isn't found we throw an exception."""

        self.vim_client._find_by_inventory_path = MagicMock(return_value=None)
        # assertRaisesRegexp is only supported in 2.7
        self.assertRaises(Exception, self.vim_client.get_vm, "vm_id")

    @patch.object(VimClient, "_create_local_service_instance")
    def test_acquire_credentials(self, si_method_mock):
        """The mockery of acquiring local hostd credentials"""
        local_ticket_mock = MagicMock(name="local_ticket")
        type(local_ticket_mock).userName = PropertyMock(return_value="local-root")
        pwd_fd, pwd_path = tempfile.mkstemp()
        os.write(pwd_fd, "local-root-password")
        os.close(pwd_fd)
        type(local_ticket_mock).passwordFilePath = PropertyMock(return_value=pwd_path)

        session_manager_mock = MagicMock(name="session-manager")
        session_manager_mock.AcquireLocalTicket.return_value = local_ticket_mock

        si = MagicMock(name="si")
        session_manager_property = PropertyMock(return_value=session_manager_mock)
        type(si.content).sessionManager = session_manager_property

        si_method_mock.return_value = si

        vim_client = VimClient(auto_sync=False)
        (user, password) = vim_client._acquire_local_credentials()
        os.remove(pwd_path)
        assert_that(user, equal_to("local-root"))
        assert_that(password, equal_to("local-root-password"))

    @patch.object(VimClient, "_create_local_service_instance")
    def test_acquire_credentials_connection_failure(self, si_method_mock):
        si = MagicMock(name="si")
        session_manager_property = PropertyMock(side_effect=HTTPException("hubba"))
        type(si.content).sessionManager = session_manager_property
        si_method_mock.return_value = si
        vim_client = VimClient(auto_sync=False)
        self.assertRaises(AcquireCredentialsException, vim_client._acquire_local_credentials)

    @patch('host.hypervisor.esx.vim_client.VimClient._property_collector', new_callable=PropertyMock)
    @patch.object(VimCache, "poll_updates")
    @patch.object(VimCache, "_vm_filter_spec")
    @patch("pysdk.connect.Connect")
    @patch("pysdk.connect.Disconnect")
    def test_update_host_cache_in_thread(self, disconnect_mock, connect_mock, spec_mock,
                                         update_mock, prop_collector_mock):
        vm = vim.VirtualMachine("moid", None)
        vm.kind = "enter"
        vm.changeSet = {}
        update = MagicMock()
        update.filterSet = [MagicMock()]
        update.filterSet[0].objectSet = [MagicMock()]
        update.filterSet[0].objectSet[0] = vm

        # Mock the Vim APIs.
        prop_collector_mock.WaitForUpdatesEx = MagicMock()
        prop_collector_mock.WaitForUpdatesEx.return_value = update

        # Create VimClient.
        vim_client = VimClient(min_interval=0.1, auto_sync=True)
        vim_client.connect_userpwd("esx.local", "root", "password")

        # Verify that the update mock is called a few times.
        retry = 0
        while update_mock.call_count < 5 and retry < 10:
            time.sleep(0.2)
            retry += 1
        assert_that(retry, is_not(10), "VimClient.update_mock is not called repeatedly")

        # Disconnect the client and stop the thread.
        vim_client.disconnect()
        assert_that(disconnect_mock.called, is_(True))

        assert_that(update_mock.call_count, is_not(0), "VimClient.update_mock is not called")

    def test_query_stats(self):
        metric_names = ["A.C", "B.E", "D.E"]
        self.vim_client._content.perfManager = MagicMock()
        self.vim_client._content.perfManager.perfCounter = create_fake_counters()

        self.vim_client._content.perfManager.QueryPerf.return_value = [
            vim.PerformanceManager.EntityMetricCSV(
                entity=vim.HostSystem('ha-host'),
                sampleInfoCSV='20,1970-01-01T00:00:10Z',
                value=[
                    vim.PerformanceManager.MetricSeriesCSV(
                        id=vim.PerformanceManager.MetricId(counterId=6567, instance=''),
                        value='200')]
            ),
            vim.PerformanceManager.EntityMetricCSV(
                entity=vim.HostSystem('ha-host'),
                sampleInfoCSV='20,1970-01-01T00:00:10Z',
                value=[
                    vim.PerformanceManager.MetricSeriesCSV(
                        id=vim.PerformanceManager.MetricId(counterId=6669, instance=''),
                        value='200')]
            ),
        ]

        host = MagicMock(spec=vim.ManagedObject, key=vim.HostSystem("ha-host"))
        since = datetime.now() - timedelta(seconds=20)

        results = self.vim_client.query_stats(host, metric_names, 20, since, None)
        assert_that(len(results), equal_to(2))
class TestVimClient(unittest.TestCase):
    def setUp(self):
        self.vim_client = VimClient(auto_sync=False)
        self.vim_client._content = MagicMock()
        self.host = MagicMock(spec=vim.ManagedObject,
                              key=vim.HostSystem("ha-host"))
        self.host.summary = MagicMock()
        self.host.summary.quickStats = MagicMock()
        self.host.summary.hardware = MagicMock()
        self.host.summary.quickStats.overallCpuUsage = 1024
        self.host.summary.hardware.cpuMhz = 1024
        self.host.summary.hardware.numCpuCores = 2
        self.host.summary.quickStats.overallMemoryUsage = 2  # 2GB
        self.host.summary.hardware.memorySize = 4 * 1024 * 1024  # 4GB
        self.vim_client.host_system = MagicMock(return_value=self.host)

    def test_import_pyvmomi(self):
        from pyVmomi import VmomiSupport
        assert_that(getattr(VmomiSupport, "BASE_VERSION", None), not_none())

    @patch("pysdk.connect.Connect")
    def test_hostd_connect_failure(self, connect_mock):
        connect_mock.side_effect = vim.fault.HostConnectFault
        vim_client = VimClient(auto_sync=False)
        self.assertRaises(HostdConnectionFailure, vim_client.connect_userpwd,
                          "localhost", "username", "password")

    @patch("pysdk.connect.Connect")
    def test_vim_client_with_param(self, connect_mock):
        vim_client = VimClient(auto_sync=False)
        vim_client.connect_userpwd("esx.local", "root", "password")
        connect_mock.assert_called_once_with(host="esx.local",
                                             user="******",
                                             pwd="password",
                                             version="vim.version.version9")

    @patch.object(VimCache, "poll_updates")
    @patch("pysdk.connect.Connect")
    def test_update_fail_without_looping(self, connect_mock, update_mock):
        client = VimClient(auto_sync=True, min_interval=1)
        client.connect_userpwd("esx.local", "root", "password")
        update_mock.side_effect = vim.fault.HostConnectFault
        time.sleep(0.5)
        client.disconnect()
        assert_that(update_mock.call_count, less_than(5))  # no crazy loop

    @patch.object(VimCache, "poll_updates")
    @patch("pysdk.connect.Connect")
    @patch("time.sleep")
    def test_update_fail_will_suicide(self, sleep_mock, connect_mock,
                                      update_mock):
        killed = threading.Event()

        def suicide():
            killed.set()
            threading.current_thread().stop()

        poll_updates = MagicMock()
        poll_updates.side_effect = vim.fault.HostConnectFault

        client = VimClient(auto_sync=True,
                           min_interval=1,
                           errback=lambda: suicide())
        client.connect_userpwd("esx.local", "root", "password")
        client._vim_cache.poll_updates = poll_updates

        killed.wait(1)
        client.disconnect()

        # poll_updates will be called 5 times before it kill itself
        assert_that(poll_updates.call_count, is_(5))
        assert_that(killed.is_set(), is_(True))

    @patch.object(VimCache, "_build_filter_spec")
    @patch("pysdk.connect.Connect")
    def test_update_cache(self, connect_mock, spec_mock):
        vim_client = VimClient(auto_sync=False)
        vim_client.connect_userpwd("esx.local", "root", "password")
        vim_client._property_collector.WaitForUpdatesEx.return_value = {}
        vim_client._vim_cache = VimCache()

        # Test enter
        update = vmodl.query.PropertyCollector.UpdateSet(version="1")
        filter = vmodl.query.PropertyCollector.FilterUpdate()
        update.filterSet.append(filter)
        object_update = vmodl.query.PropertyCollector.ObjectUpdate(
            kind="enter",
            obj=vim.VirtualMachine("vim.VirtualMachine:9"),
        )
        filter.objectSet.append(object_update)
        object_update.changeSet.append(
            vmodl.query.PropertyCollector.Change(name="name",
                                                 op="assign",
                                                 val="agent4"))
        object_update.changeSet.append(
            vmodl.query.PropertyCollector.Change(name="runtime.powerState",
                                                 op="assign",
                                                 val="poweredOff"))
        object_update.changeSet.append(
            vmodl.query.PropertyCollector.Change(
                name="config",
                op="assign",
                val=vim.vm.ConfigInfo(
                    files=vim.vm.FileInfo(
                        vmPathName="[datastore2] agent4/agent4.vmx"),
                    hardware=vim.vm.VirtualHardware(memoryMB=4096),
                    locationId="location1"),
            ))
        disk_list = vim.vm.FileLayout.DiskLayout.Array()
        disk_list.append(vim.vm.FileLayout.DiskLayout(diskFile=["disk1"]))
        disk_list.append(vim.vm.FileLayout.DiskLayout(diskFile=["disk2"]))
        object_update.changeSet.append(
            vmodl.query.PropertyCollector.Change(name="layout.disk",
                                                 op="assign",
                                                 val=disk_list))

        vim_client._property_collector.WaitForUpdatesEx.return_value = update
        assert_that(len(vim_client.get_vms_in_cache()), is_(0))
        vim_client._vim_cache.poll_updates(vim_client)
        vim_client._property_collector.WaitForUpdatesEx.assert_called()

        vms = vim_client.get_vms_in_cache()
        assert_that(vim_client._vim_cache._current_version, is_("1"))
        assert_that(len(vms), 1)
        assert_that(vms[0].memory_mb, is_(4096))
        assert_that(vms[0].path, is_("[datastore2] agent4/agent4.vmx"))
        assert_that(vms[0].name, is_("agent4"))
        assert_that(vms[0].power_state, is_(VmPowerState.STOPPED))
        assert_that(len(vms[0].disks), is_(2))
        assert_that(vms[0].disks, contains_inanyorder("disk1", "disk2"))
        assert_that(vms[0].location_id, is_("location1"))

        # Test Modify
        update.version = "2"
        object_update = vmodl.query.PropertyCollector.ObjectUpdate(
            kind="modify",
            obj=vim.VirtualMachine("vim.VirtualMachine:9"),
        )
        filter.objectSet[0] = object_update
        object_update.changeSet.append(
            vmodl.query.PropertyCollector.Change(name="runtime.powerState",
                                                 op="assign",
                                                 val="poweredOn"))
        object_update.changeSet.append(
            vmodl.query.PropertyCollector.Change(name="runtime.powerState",
                                                 op="assign",
                                                 val="poweredOn"))
        disk_list = vim.vm.FileLayout.DiskLayout.Array()
        disk_list.append(
            vim.vm.FileLayout.DiskLayout(diskFile=["disk3", "disk4"]))
        object_update.changeSet.append(
            vmodl.query.PropertyCollector.Change(name="layout.disk",
                                                 op="assign",
                                                 val=disk_list))

        vim_client._vim_cache.poll_updates(vim_client)
        vms = vim_client.get_vms_in_cache()
        assert_that(vim_client._vim_cache._current_version, is_("2"))
        assert_that(len(vms), is_(1))
        assert_that(vms[0].memory_mb, is_(4096))
        assert_that(vms[0].path, is_("[datastore2] agent4/agent4.vmx"))
        assert_that(vms[0].name, is_("agent4"))
        assert_that(vms[0].power_state, is_(VmPowerState.STARTED))
        assert_that(len(vms[0].disks), is_(2))
        assert_that(vms[0].disks, contains_inanyorder("disk3", "disk4"))

        # Test leave
        update.version = "3"
        object_update = vmodl.query.PropertyCollector.ObjectUpdate(
            kind="leave",
            obj=vim.VirtualMachine("vim.VirtualMachine:9"),
        )
        filter.objectSet[0] = object_update
        vim_client._vim_cache.poll_updates(vim_client)
        vms = vim_client.get_vms_in_cache()
        assert_that(vim_client._vim_cache._current_version, is_("3"))
        assert_that(len(vms), is_(0))

    @patch.object(VimCache, "poll_updates")
    @patch.object(VimCache, "_build_filter_spec")
    @patch("pysdk.connect.Connect")
    @patch("pysdk.connect.Disconnect")
    def test_poll_update_in_thread(self, disconnect_mock, connect_mock,
                                   spec_mock, update_mock):
        vim_client = VimClient(min_interval=0, auto_sync=True)
        vim_client.connect_userpwd("esx.local", "root", "password")
        vim_client._property_collector.WaitForUpdatesEx.return_value = {}

        assert_that(update_mock.called, is_(True))
        retry = 0
        while update_mock.call_count < 5 and retry < 10:
            time.sleep(0.2)
            retry += 1
        assert_that(retry, is_not(10),
                    "VimClient._poll_updates is not called repeatedly")
        vim_client.disconnect()
        assert_that(disconnect_mock.called, is_(True))

    @patch("pysdk.host.GetHostSystem")
    @patch("pysdk.connect.Connect")
    def test_vim_client_errback(self, connect_mock, host_mock):
        callback = MagicMock()
        vim_client = VimClient(auto_sync=False, errback=callback)
        vim_client.connect_userpwd("esx.local", "root", "password")
        host_mock.side_effect = vim.fault.NotAuthenticated
        vim_client.host_system()
        callback.assert_called_once()

        host_mock.side_effect = vim.fault.HostConnectFault
        vim_client.host_system()
        assert_that(callback.call_count, is_(2))

        host_mock.side_effect = vim.fault.InvalidLogin
        vim_client.host_system()
        assert_that(callback.call_count, is_(3))

        host_mock.side_effect = AcquireCredentialsException
        vim_client.host_system()
        assert_that(callback.call_count, is_(4))

    @patch("pysdk.connect.Connect")
    def test_get_nfc_ticket(self, connect_mock):
        vim_client = VimClient(auto_sync=False)
        vim_client._find_by_inventory_path = MagicMock(return_value=None)
        self.assertRaises(DatastoreNotFound,
                          vim_client.get_nfc_ticket_by_ds_name, "no_exist")

        ds_mock = MagicMock()
        vim_client._find_by_inventory_path = MagicMock(return_value=ds_mock)
        nfc_service = MagicMock()
        type(vim).NfcService = MagicMock()
        type(vim).NfcService.return_value = nfc_service

        vim_client._si = MagicMock()
        vim_client.get_nfc_ticket_by_ds_name("existing_ds")

        nfc_service.FileManagement.assert_called_once_with(ds_mock)

    def test_inventory_path(self):
        """Check that convert to inventory path correctly."""
        tests = [
            {
                "path": (),
                "val": "ha-datacenter"
            },
            {
                "path": ("network", None),
                "val": "ha-datacenter/network"
            },
            {
                "path": ("vm", ),
                "val": "ha-datacenter/vm"
            },
            {
                "path": ("vm", "Cent/OS"),
                "val": "ha-datacenter/vm/Cent%2fOS"
            },
        ]
        for test in tests:
            self.vim_client._find_by_inventory_path(*test["path"])
            self.vim_client._content.searchIndex.FindByInventoryPath.assert_called_once_with(
                test["val"])
            self.vim_client._content.searchIndex.FindByInventoryPath.reset_mock(
            )

    def test_get_vm_not_found(self):
        """Test that if the vm isn't found we throw an exception."""

        self.vim_client._find_by_inventory_path = MagicMock(return_value=None)
        # assertRaisesRegexp is only supported in 2.7
        self.assertRaises(Exception, self.vim_client.get_vm, "vm_id")

    @patch.object(VimClient, "_create_local_service_instance")
    def test_acquire_credentials(self, si_method_mock):
        """The mockery of acquiring local hostd credentials"""
        local_ticket_mock = MagicMock(name="local_ticket")
        type(local_ticket_mock).userName = PropertyMock(
            return_value="local-root")
        pwd_fd, pwd_path = tempfile.mkstemp()
        os.write(pwd_fd, "local-root-password")
        os.close(pwd_fd)
        type(local_ticket_mock).passwordFilePath = PropertyMock(
            return_value=pwd_path)

        session_manager_mock = MagicMock(name="session-manager")
        session_manager_mock.AcquireLocalTicket.return_value = local_ticket_mock

        si = MagicMock(name="si")
        session_manager_property = PropertyMock(
            return_value=session_manager_mock)
        type(si.content).sessionManager = session_manager_property

        si_method_mock.return_value = si

        vim_client = VimClient(auto_sync=False)
        (user, password) = vim_client._acquire_local_credentials()
        os.remove(pwd_path)
        assert_that(user, equal_to("local-root"))
        assert_that(password, equal_to("local-root-password"))

    @patch.object(VimClient, "_create_local_service_instance")
    def test_acquire_credentials_connection_failure(self, si_method_mock):
        si = MagicMock(name="si")
        session_manager_property = PropertyMock(
            side_effect=HTTPException("hubba"))
        type(si.content).sessionManager = session_manager_property
        si_method_mock.return_value = si
        vim_client = VimClient(auto_sync=False)
        self.assertRaises(AcquireCredentialsException,
                          vim_client._acquire_local_credentials)

    @patch('host.hypervisor.esx.vim_client.VimClient._property_collector',
           new_callable=PropertyMock)
    @patch.object(VimCache, "poll_updates")
    @patch.object(VimCache, "_vm_filter_spec")
    @patch("pysdk.connect.Connect")
    @patch("pysdk.connect.Disconnect")
    def test_update_host_cache_in_thread(self, disconnect_mock, connect_mock,
                                         spec_mock, update_mock,
                                         prop_collector_mock):
        vm = vim.VirtualMachine("moid", None)
        vm.kind = "enter"
        vm.changeSet = {}
        update = MagicMock()
        update.filterSet = [MagicMock()]
        update.filterSet[0].objectSet = [MagicMock()]
        update.filterSet[0].objectSet[0] = vm

        # Mock the Vim APIs.
        prop_collector_mock.WaitForUpdatesEx = MagicMock()
        prop_collector_mock.WaitForUpdatesEx.return_value = update

        # Create VimClient.
        vim_client = VimClient(min_interval=0.1, auto_sync=True)
        vim_client.connect_userpwd("esx.local", "root", "password")

        # Verify that the update mock is called a few times.
        retry = 0
        while update_mock.call_count < 5 and retry < 10:
            time.sleep(0.2)
            retry += 1
        assert_that(retry, is_not(10),
                    "VimClient.update_mock is not called repeatedly")

        # Disconnect the client and stop the thread.
        vim_client.disconnect()
        assert_that(disconnect_mock.called, is_(True))

        assert_that(update_mock.call_count, is_not(0),
                    "VimClient.update_mock is not called")

    def test_query_stats(self):
        self.vim_client._content.perfManager = MagicMock()
        counter_ids = [101, 102, 103]
        self.vim_client._content.perfManager.perfCounter = create_fake_counters(
            counter_ids)

        self.vim_client._content.perfManager.QueryPerf.return_value = [
            vim.PerformanceManager.EntityMetricCSV(
                entity=vim.HostSystem('ha-host'),
                sampleInfoCSV='20,1970-01-01T00:00:10Z',
                value=[
                    vim.PerformanceManager.MetricSeriesCSV(
                        id=vim.PerformanceManager.MetricId(
                            counterId=counter_ids[0], instance=''),
                        value='200')
                ]),
            vim.PerformanceManager.EntityMetricCSV(
                entity=vim.HostSystem('ha-host'),
                sampleInfoCSV='20,1970-01-01T00:00:10Z',
                value=[
                    vim.PerformanceManager.MetricSeriesCSV(
                        id=vim.PerformanceManager.MetricId(
                            counterId=counter_ids[1], instance=''),
                        value='200')
                ]),
        ]
        since = datetime.now() - timedelta(seconds=20)

        results = self.vim_client.query_stats(since, None)
        # 2 through QueryPerf + 2 through Pyvmomi Host object
        assert_that(len(results), equal_to(4))

    def test_query_stats_with_no_query_perf_results(self):
        self.vim_client._content.perfManager = MagicMock()
        self.vim_client._content.perfManager.perfCounter = []
        self.vim_client._content.perfManager.QueryPerf.return_value = []

        now = datetime.now()
        now_timestamp = int(time.mktime(now.timetuple()))
        since = now - timedelta(seconds=20)

        results = self.vim_client.query_stats(since, None)

        # Verify counters collected are only through Pyvmomi Host object
        assert_that(len(results), equal_to(2))

        # Verify counters collected through Pyvmomi Host object
        assert_that(
            results,
            has_entries('cpu.cpuUsagePercentage', [(now_timestamp, 50.0)]))
        assert_that(
            results,
            has_entries('mem.memoryUsagePercentage', [(now_timestamp, 50.0)]))

    def test_query_stats_with_bad_pyvmomi_results(self):
        self.vim_client._content.perfManager = MagicMock()
        self.vim_client._content.perfManager.perfCounter = []
        self.vim_client._content.perfManager.QueryPerf.return_value = []
        self.host.summary.hardware.numCpuCores = 0
        self.host.summary.hardware.memorySize = 0

        now = datetime.now()
        now_timestamp = int(time.mktime(now.timetuple()))
        since = now - timedelta(seconds=20)
        results = self.vim_client.query_stats(since, None)

        # Verify no counter
        assert_that(
            results,
            has_entries('cpu.cpuUsagePercentage', [(now_timestamp, 0.0)]))
        assert_that(
            results,
            has_entries('mem.memoryUsagePercentage', [(now_timestamp, 0.0)]))