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"))
 def test_update_fail_without_looping(self, connect_mock, update_mock):
     client = VimClient("esx.local", "root", "password", auto_sync=True,
                        min_interval=1)
     update_mock.side_effect = vim.fault.HostConnectFault
     time.sleep(0.5)
     client.disconnect(wait=True)
     assert_that(update_mock.call_count, less_than(4))  # no crazy loop
    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")
Beispiel #4
0
    def __init__(self, agent_config):
        self.logger = logging.getLogger(__name__)

        # If VimClient's housekeeping thread failed to update its own cache,
        # call errback to commit suicide. Watchdog will bring up the agent
        # again.
        self.vim_client = VimClient(wait_timeout=agent_config.wait_timeout,
                                    errback=lambda: suicide())
        atexit.register(lambda client: client.disconnect(), self.vim_client)

        self._uuid = self.vim_client.host_uuid
        self.set_memory_overcommit(agent_config.memory_overcommit)

        self.datastore_manager = EsxDatastoreManager(
            self, agent_config.datastores, agent_config.image_datastores)
        # datastore manager needs to update the cache when there is a change.
        self.vim_client.add_update_listener(self.datastore_manager)
        self.vm_manager = EsxVmManager(self.vim_client, self.datastore_manager)
        self.disk_manager = EsxDiskManager(self.vim_client,
                                           self.datastore_manager)
        self.image_manager = EsxImageManager(self.vim_client,
                                             self.datastore_manager)
        self.network_manager = EsxNetworkManager(self.vim_client,
                                                 agent_config.networks)
        self.system = EsxSystem(self.vim_client)
        self.image_manager.monitor_for_cleanup()
        self.image_transferer = HttpNfcTransferer(
            self.vim_client, self.datastore_manager.image_datastores())
        atexit.register(self.image_manager.cleanup)
 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")
    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"]
        self.agent_port = config["host_remote_test"].get("agent_port", 8835)
        if self.host is None or self.pwd is None:
            raise SkipTest()

        self.image_datastore = config["host_remote_test"].get(
            "image_datastore", "datastore1")

        self._logger = logging.getLogger(__name__)
        self.vim_client = VimClient(self.host, "root", self.pwd)
        self.http_transferer = HttpNfcTransferer(self.vim_client,
                                                 [self.image_datastore],
                                                 self.host)

        with tempfile.NamedTemporaryFile(delete=False) as source_file:
            with open(source_file.name, 'wb') as f:
                f.write(os.urandom(1024 * 100))
        self.random_file = source_file.name

        self.remote_files_to_delete = []
    def test_update_fail_will_suicide(self, sleep_mock, connect_mock,
                                      update_mock, update_hosts_mock):
        killed = threading.Event()

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

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

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

        killed.wait(1)
        client.disconnect(wait=True)

        # update_cache will be called 5 times before it kill itself
        assert_that(update_cache.call_count, is_(5))
        assert_that(killed.is_set(), is_(True))
Beispiel #8
0
 def setUp(self, connect, update, creds):
     creds.return_value = ["username", "password"]
     self.vim_client = VimClient(auto_sync=False)
     self.vim_client.wait_for_task = MagicMock()
     self.patcher = patch("host.hypervisor.esx.vm_config.GetEnv")
     self.patcher.start()
     self.vm_manager = EsxVmManager(self.vim_client, MagicMock())
 def setUp(self, connect, update, creds):
     creds.return_value = ["username", "password"]
     self.vim_client = VimClient(auto_sync=False)
     self.vim_client.wait_for_task = MagicMock()
     self.disk_manager = EsxDiskManager(self.vim_client, [])
     self.disk_manager._vmdk_mkdir = MagicMock()
     self.disk_manager._vmdk_rmdir = MagicMock()
    def test_update_host_cache_in_thread(self, disconnect_mock, connect_mock,
                                         spec_mock, update_mock,
                                         update_host_mock, query_spec_mock,
                                         perf_manager_mock,
                                         prop_collector_mock):

        # Test Values.
        counter = MagicMock()
        counter.groupInfo.key = "mem"
        counter.nameInfo.key = "consumed"
        counter.key = 65613

        n = 5
        statValues = ','.join([str(x) for x in range(1, n + 1)])
        statAverage = sum(range(1, n + 1)) / len(range(1, n + 1))
        stat = MagicMock()
        stat.value = [MagicMock()]
        stat.value[0].id.counterId = 65613
        stat.value[0].value = statValues

        # Mock the Vim APIs.
        pc_return_mock = MagicMock({'WaitForUpdatesEx.return_value': {}})
        summarize_stats = {'QueryPerf.return_value': [stat]}
        pm_return_mock = MagicMock(perfCounter=[counter], **summarize_stats)

        # Tie the mocked APIs with VimClient.
        prop_collector_mock.return_value = pc_return_mock
        perf_manager_mock.return_value = pm_return_mock

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

        # 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(wait=True)
        assert_that(disconnect_mock.called, is_(True))

        # Verify that update_host_mock is called atleast once and is called
        # less number of times than update_mock.
        assert_that(update_host_mock.call_count, is_not(0),
                    "VimClient.update_host_mock is not called repeatedly")
        assert_that(update_host_mock.call_count,
                    less_than(update_mock.call_count))

        host_stats = update_host_mock.call_args_list
        for host in host_stats:
            assert_that(host[0][0]['mem.consumed'], equal_to(statAverage))
 def test_check_prefix_len_to_netmask_conversion(self):
     """Check the conversion from prefix length to netmask"""
     self.assertEqual(VimClient._prefix_len_to_mask(32), "255.255.255.255")
     self.assertEqual(VimClient._prefix_len_to_mask(0), "0.0.0.0")
     self.assertRaises(ValueError,
                       VimClient._prefix_len_to_mask, 33)
     self.assertEqual(VimClient._prefix_len_to_mask(23), "255.255.254.0")
     self.assertEqual(VimClient._prefix_len_to_mask(6), "252.0.0.0")
     self.assertEqual(VimClient._prefix_len_to_mask(32), "255.255.255.255")
    def test_update_host_cache_in_thread(self, disconnect_mock, connect_mock,
                                         spec_mock, update_mock,
                                         update_host_mock, query_spec_mock,
                                         perf_manager_mock,
                                         prop_collector_mock):

        # Test Values.
        counter = MagicMock()
        counter.groupInfo.key = "mem"
        counter.nameInfo.key = "consumed"
        counter.key = 65613

        n = 5
        statValues = ','.join([str(x) for x in range(1, n+1)])
        statAverage = sum(range(1, n+1)) / len(range(1, n+1))
        stat = MagicMock()
        stat.value = [MagicMock()]
        stat.value[0].id.counterId = 65613
        stat.value[0].value = statValues

        # Mock the Vim APIs.
        pc_return_mock = MagicMock({'WaitForUpdatesEx.return_value': {}})
        summarize_stats = {'QueryPerf.return_value': [stat]}
        pm_return_mock = MagicMock(perfCounter=[counter], **summarize_stats)

        # Tie the mocked APIs with VimClient.
        prop_collector_mock.return_value = pc_return_mock
        perf_manager_mock.return_value = pm_return_mock

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

        # 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(wait=True)
        assert_that(disconnect_mock.called, is_(True))

        # Verify that update_host_mock is called atleast once and is called
        # less number of times than update_mock.
        assert_that(update_host_mock.call_count, is_not(0),
                    "VimClient.update_host_mock is not called repeatedly")
        assert_that(update_host_mock.call_count,
                    less_than(update_mock.call_count))

        host_stats = update_host_mock.call_args_list
        for host in host_stats:
            assert_that(host[0][0]['mem.consumed'], equal_to(statAverage))
 def test_update_fail_without_looping(self, connect_mock, update_mock):
     client = VimClient("esx.local",
                        "root",
                        "password",
                        auto_sync=True,
                        min_interval=1)
     update_mock.side_effect = vim.fault.HostConnectFault
     time.sleep(0.5)
     client.disconnect(wait=True)
     assert_that(update_mock.call_count, less_than(4))  # no crazy loop
Beispiel #14
0
 def setUp(self, connect, creds):
     self.shadow_vm_id = SHADOW_VM_NAME_PREFIX + str(uuid.uuid4())
     self.image_datastores = ["image_ds", "alt_image_ds"]
     creds.return_value = ["username", "password"]
     self.vim_client = VimClient(auto_sync=False)
     self.patcher = patch("host.hypervisor.esx.vm_config.GetEnv")
     self.patcher.start()
     services.register(ServiceName.AGENT_CONFIG, MagicMock())
     self.http_transferer = HttpNfcTransferer(self.vim_client,
                                              self.image_datastores)
    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))
Beispiel #16
0
    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(auto_sync=True)
        self.vim_client.connect_userpwd(self.host, "root", self.pwd)
        self._logger = logging.getLogger(__name__)
 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 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._logger = logging.getLogger(__name__)
        self.vim_client = VimClient(self.host, "root", self.pwd)
        self.vm_manager = EsxVmManager(self.vim_client, [])
        for vm in self.vim_client.get_vms():
            vm.Destroy()
 def setUp(self):
     self.vim_client = VimClient(auto_sync=False)
     self.vim_client._content = MagicMock()
     self.vim_client.wait_for_task = MagicMock()
     self.disk_manager = DiskManager(self.vim_client, [])
     self.disk_manager._vmdk_mkdir = MagicMock()
     self.disk_manager._vmdk_rmdir = MagicMock()
 def setUp(self, connect, update, creds):
     creds.return_value = ["username", "password"]
     self.vim_client = VimClient(auto_sync=False)
     self.vim_client.wait_for_task = MagicMock()
     self.disk_manager = EsxDiskManager(self.vim_client, [])
     self.disk_manager._vmdk_mkdir = MagicMock()
     self.disk_manager._vmdk_rmdir = MagicMock()
    def __init__(self, agent_config):
        self.logger = logging.getLogger(__name__)

        # If VimClient's housekeeping thread failed to update its own cache,
        # call errback to commit suicide. Watchdog will bring up the agent
        # again.
        errback = lambda: suicide()
        self.vim_client = VimClient(wait_timeout=agent_config.wait_timeout,
                                    errback=errback)
        atexit.register(lambda client: client.disconnect(), self.vim_client)

        self._uuid = self.vim_client.host_uuid

        # Enable/Disable large page support. If this host is removed
        # from the deployment, large page support will need to be
        # explicitly updated by the user.
        disable_large_pages = agent_config.memory_overcommit > 1.0
        self.vim_client.set_large_page_support(disable=disable_large_pages)

        image_datastores = [ds["name"] for ds in agent_config.image_datastores]
        self.datastore_manager = EsxDatastoreManager(
            self, agent_config.datastores, image_datastores)
        # datastore manager needs to update the cache when there is a change.
        self.vim_client.add_update_listener(self.datastore_manager)
        self.vm_manager = EsxVmManager(self.vim_client, self.datastore_manager)
        self.disk_manager = EsxDiskManager(self.vim_client,
                                           self.datastore_manager)
        self.image_manager = EsxImageManager(self.vim_client,
                                             self.datastore_manager)
        self.network_manager = EsxNetworkManager(self.vim_client,
                                                 agent_config.networks)
        self.system = EsxSystem(self.vim_client)
        self.image_manager.monitor_for_cleanup()
        atexit.register(self.image_manager.cleanup)
 def setUp(self, connect, update, creds):
     creds.return_value = ["username", "password"]
     self.vim_client = VimClient(auto_sync=False)
     self.vim_client.wait_for_task = MagicMock()
     self.patcher = patch("host.hypervisor.esx.vm_config.GetEnv")
     self.patcher.start()
     self.vm_manager = EsxVmManager(self.vim_client, MagicMock())
    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"]
        self.agent_port = config["host_remote_test"].get("agent_port", 8835)
        if self.host is None or self.pwd is None:
            raise SkipTest()

        self.image_datastore = config["host_remote_test"].get(
            "image_datastore", "datastore1")

        self._logger = logging.getLogger(__name__)
        self.vim_client = VimClient(self.host, "root", self.pwd)
        self.http_transferer = HttpNfcTransferer(self.vim_client,
                                                 [self.image_datastore],
                                                 self.host)

        with tempfile.NamedTemporaryFile(delete=False) as source_file:
            with open(source_file.name, 'wb') as f:
                f.write(os.urandom(1024 * 100))
        self.random_file = source_file.name

        self.remote_files_to_delete = []
Beispiel #24
0
    def __init__(self, agent_config):
        self.logger = logging.getLogger(__name__)

        # If VimClient's housekeeping thread failed to update its own cache,
        # call errback to commit suicide. Watchdog will bring up the agent
        # again.
        errback = lambda: suicide()
        self.vim_client = VimClient(wait_timeout=agent_config.wait_timeout,
                                    errback=errback)
        atexit.register(lambda client: client.disconnect(), self.vim_client)

        self._uuid = self.vim_client.host_uuid
        self.set_memory_overcommit(agent_config.memory_overcommit)

        image_datastores = [ds["name"] for ds in agent_config.image_datastores]
        self.datastore_manager = EsxDatastoreManager(
            self, agent_config.datastores, agent_config.image_datastores)
        # datastore manager needs to update the cache when there is a change.
        self.vim_client.add_update_listener(self.datastore_manager)
        self.vm_manager = EsxVmManager(self.vim_client, self.datastore_manager)
        self.disk_manager = EsxDiskManager(self.vim_client,
                                           self.datastore_manager)
        self.image_manager = EsxImageManager(self.vim_client,
                                             self.datastore_manager)
        self.network_manager = EsxNetworkManager(self.vim_client,
                                                 agent_config.networks)
        self.system = EsxSystem(self.vim_client)
        self.image_manager.monitor_for_cleanup()
        self.image_transferer = HttpNfcTransferer(self.vim_client,
                                                  image_datastores)
        atexit.register(self.image_manager.cleanup)
class TestEsxDiskManager(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)
        self.vim_client.wait_for_task = MagicMock()
        self.disk_manager = EsxDiskManager(self.vim_client, [])
        self.disk_manager._vmdk_mkdir = MagicMock()
        self.disk_manager._vmdk_rmdir = MagicMock()

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

    def test_create_spec(self):
        """Test that we create a valid disk spec."""

        capacity = 2
        spec = self.disk_manager._create_spec(capacity)
        assert_that(spec.capacityKb, equal_to(capacity * (1024 ** 2)))
        assert_that(spec.adapterType, equal_to(DEFAULT_DISK_ADAPTER_TYPE))

    def test_invalid_datastore_path(self):
        """Test that we propagate InvalidDatastorePath."""

        self.vim_client.wait_for_task.side_effect = \
            vim.fault.InvalidDatastorePath
        self.assertRaises(DiskPathException,
                          self.disk_manager.create_disk, "ds1", "foo", 101)

    def test_disk_not_found(self):
        """Test that we propagate FileNotFound."""

        self.vim_client.wait_for_task.side_effect = vim.fault.FileNotFound
        self.assertRaises(DiskFileException,
                          self.disk_manager.delete_disk, "ds1", "bar")

    def test_general_fault(self):
        """Test general Exception propagation."""

        self.vim_client.wait_for_task.side_effect = vim.fault.TaskInProgress

        self.assertRaises(vim.fault.TaskInProgress,
                          self.disk_manager.move_disk,
                          "ds1", "biz", "ds1", "baz")
class TestEsxDiskManager(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)
        self.vim_client.wait_for_task = MagicMock()
        self.disk_manager = EsxDiskManager(self.vim_client, [])
        self.disk_manager._vmdk_mkdir = MagicMock()
        self.disk_manager._vmdk_rmdir = MagicMock()

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

    def test_create_spec(self):
        """Test that we create a valid disk spec."""

        capacity = 2
        spec = self.disk_manager._create_spec(capacity)
        assert_that(spec.capacityKb, equal_to(capacity * (1024**2)))
        assert_that(spec.adapterType, equal_to(DEFAULT_DISK_ADAPTER_TYPE))

    def test_invalid_datastore_path(self):
        """Test that we propagate InvalidDatastorePath."""

        self.vim_client.wait_for_task.side_effect = \
            vim.fault.InvalidDatastorePath
        self.assertRaises(DiskPathException, self.disk_manager.create_disk,
                          "ds1", "foo", 101)

    def test_disk_not_found(self):
        """Test that we propagate FileNotFound."""

        self.vim_client.wait_for_task.side_effect = vim.fault.FileNotFound
        self.assertRaises(DiskFileException, self.disk_manager.delete_disk,
                          "ds1", "bar")

    def test_general_fault(self):
        """Test general Exception propagation."""

        self.vim_client.wait_for_task.side_effect = vim.fault.TaskInProgress

        self.assertRaises(vim.fault.TaskInProgress,
                          self.disk_manager.move_disk, "ds1", "biz", "ds1",
                          "baz")
Beispiel #27
0
 def create_host_client(auto_sync=True, errback=None):
     try:
         # check whether attache is installed. If not, find_module will throw ImportError.
         from host.hypervisor.esx.attache_client import AttacheClient
         return AttacheClient(auto_sync, errback)
     except ImportError:
         from host.hypervisor.esx.vim_client import VimClient
         return VimClient(auto_sync, errback)
 def vim_delete_vm(self, vm_id):
     """ Delete a VM using the vim client """
     try:
         vim_client = VimClient()
         vim_client.connect_ticket(self.server, self._get_vim_ticket())
         vim_vm = vim_client.get_vm(vm_id)
         if vim_vm.runtime.powerState != 'poweredOff':
             try:
                 vim_task = vim_vm.PowerOff()
                 vim_client.wait_for_task(vim_task)
             except:
                 logger.info("Cannot power off vm", exc_info=True)
         vim_task = vim_vm.Destroy()
         vim_client.wait_for_task(vim_task)
     finally:
         if vim_client:
             vim_client.disconnect()
 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)
    def test_update_cache_in_thread(self, disconnect_mock, connect_mock,
                                    spec_mock, update_mock, update_host_mock):
        vim_client = VimClient("esx.local",
                               "root",
                               "password",
                               min_interval=0,
                               auto_sync=True)
        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.update_cache is not "
                    "called repeatedly")
        vim_client.disconnect(wait=True)
        assert_that(disconnect_mock.called, is_(True))
    def setUp(self, connect, update, creds):
        # Create VM manager
        creds.return_value = ["username", "password"]
        self.vim_client = VimClient(auto_sync=False)
        self.vim_client.wait_for_task = MagicMock()
        self.patcher = patch("host.hypervisor.esx.vm_config.GetEnv")
        self.patcher.start()
        self.vm_manager = EsxVmManager(self.vim_client, MagicMock())
        services.register(ServiceName.AGENT_CONFIG, MagicMock())

        # Set up test files
        self.base_dir = os.path.dirname(__file__)
        self.test_dir = os.path.join(self.base_dir, "../../test_files")
        self.image_manager = EsxImageManager(MagicMock(), MagicMock())
        self.image_scanner = DatastoreImageScanner(self.image_manager,
                                                   self.vm_manager,
                                                   self.DATASTORE_ID)
        self.write_count = 0
    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))
 def setUp(self, connect, creds):
     self.shadow_vm_id = SHADOW_VM_NAME_PREFIX + str(uuid.uuid1())
     self.image_datastores = ["image_ds", "alt_image_ds"]
     creds.return_value = ["username", "password"]
     self.vim_client = VimClient(auto_sync=False)
     self.patcher = patch("host.hypervisor.esx.vm_config.GetEnv")
     self.patcher.start()
     services.register(ServiceName.AGENT_CONFIG, MagicMock())
     self.http_transferer = HttpNfcTransferer(self.vim_client,
                                              self.image_datastores)
 def setUp(self, connect, creds):
     self.host_uuid = str(uuid.uuid4())
     VimClient.host_uuid = self.host_uuid
     self.image_ds = "image_ds"
     creds.return_value = ["username", "password"]
     self.vim_client = VimClient(auto_sync=False)
     self.patcher = patch("host.hypervisor.esx.vm_config.GetEnv")
     self.patcher.start()
     services.register(ServiceName.AGENT_CONFIG, MagicMock())
     self.http_transferer = HttpNfcTransferer(self.vim_client,
                                              self.image_ds)
    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))
Beispiel #36
0
 def _create_remote_vim_client(self, agent_client, host):
     request = ServiceTicketRequest(service_type=ServiceType.VIM)
     response = agent_client.get_service_ticket(request)
     if response.result != ServiceTicketResultCode.OK:
         self._logger.info("Get service ticket failed. Response = %s" %
                           str(response))
         raise ValueError("No ticket")
     vim_client = VimClient(host=host,
                            ticket=response.vim_ticket,
                            auto_sync=False)
     return vim_client
 def _get_remote_connections(self, host, port):
     agent_client = DirectClient("Host", Host.Client, host, port)
     agent_client.connect()
     request = ServiceTicketRequest(service_type=ServiceType.VIM)
     response = agent_client.get_service_ticket(request)
     if response.result != ServiceTicketResultCode.OK:
         self._logger.info("Get service ticket failed. Response = %s" %
                           str(response))
         raise ValueError("No ticket")
     vim_client = VimClient(
         host=host, ticket=response.vim_ticket, auto_sync=False)
     return agent_client, vim_client
 def test_vim_client_with_param(self, connect_mock, update_mock):
     vim_client = VimClient("esx.local",
                            "root",
                            "password",
                            auto_sync=False)
     assert_that(vim_client.host, is_("esx.local"))
     assert_that(vim_client.username, is_("root"))
     assert_that(vim_client.password, is_("password"))
     connect_mock.assert_called_once_with(host="esx.local",
                                          user="******",
                                          pwd="password",
                                          version="vim.version.version9")
    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 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(auto_sync=True)
        self.vim_client.connect_userpwd(self.host, "root", self.pwd)
        self._logger = logging.getLogger(__name__)
 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 setUp(self):
        # Create VM manager
        self.vim_client = VimClient(auto_sync=False)
        self.vim_client._content = MagicMock()
        self.vim_client.wait_for_task = MagicMock()
        self.vm_manager = VmManager(self.vim_client, MagicMock())
        services.register(ServiceName.AGENT_CONFIG, MagicMock())

        # Set up test files
        self.base_dir = os.path.dirname(__file__)
        self.test_dir = os.path.join(self.base_dir, "../test_files")
        self.image_manager = ImageManager(MagicMock(), MagicMock())
        self.image_scanner = DatastoreImageScanner(self.image_manager, self.vm_manager, self.DATASTORE_ID)
        self.write_count = 0
    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"))
Beispiel #44
0
    def __init__(self, agent_config):
        self.logger = logging.getLogger(__name__)

        # If VimClient's housekeeping thread failed to update its own cache,
        # call errback to commit suicide. Watchdog will bring up the agent
        # again.
        errback = lambda: suicide()
        self.vim_client = VimClient(wait_timeout=agent_config.wait_timeout,
                                    errback=errback)
        atexit.register(lambda client: client.disconnect(), self.vim_client)

        self._uuid = self.vim_client.host_uuid

        # Enable/Disable large page support. If this host is removed
        # from the deployment, large page support will need to be
        # explicitly updated by the user.
        disable_large_pages = agent_config.memory_overcommit > 1.0
        self.vim_client.set_large_page_support(disable=disable_large_pages)

        image_datastores = [ds["name"] for ds in agent_config.image_datastores]
        self.datastore_manager = EsxDatastoreManager(self,
                                                     agent_config.datastores,
                                                     image_datastores)
        # datastore manager needs to update the cache when there is a change.
        self.vim_client.add_update_listener(self.datastore_manager)
        self.vm_manager = EsxVmManager(self.vim_client, self.datastore_manager)
        self.disk_manager = EsxDiskManager(self.vim_client,
                                           self.datastore_manager)
        self.image_manager = EsxImageManager(self.vim_client,
                                             self.datastore_manager)
        self.network_manager = EsxNetworkManager(self.vim_client,
                                                 agent_config.networks)
        self.system = EsxSystem(self.vim_client)
        self.image_manager.monitor_for_cleanup()
        self.image_transferer = HttpNfcTransferer(self.vim_client,
                                                  image_datastores)
        atexit.register(self.image_manager.cleanup)
    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._logger = logging.getLogger(__name__)
        self.vim_client = VimClient(self.host, "root", self.pwd)
        self.vm_manager = EsxVmManager(self.vim_client, [])
        for vm in self.vim_client.get_vms():
            vm.Destroy()
    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_acquire_credentials(self, password_mock, si_method_mock):
        """The mockery of acquiring local hostd credentials"""
        local_ticket_mock = MagicMock(name="local_ticket")
        user_name_property = PropertyMock(return_value="local-root")
        type(local_ticket_mock).userName = user_name_property

        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

        (user, password) = VimClient.acquire_credentials()
        assert_that(user, equal_to("local-root"))
    def setUp(self, connect, update, creds):
        # Create VM manager
        creds.return_value = ["username", "password"]
        self.vim_client = VimClient(auto_sync=False)
        self.vim_client.wait_for_task = MagicMock()
        self.patcher = patch("host.hypervisor.esx.vm_config.GetEnv")
        self.patcher.start()
        self.vm_manager = EsxVmManager(self.vim_client, MagicMock())

        # Set up test files
        self.base_dir = os.path.dirname(__file__)
        self.test_dir = os.path.join(self.base_dir, "../../test_files")
        self.image_manager = EsxImageManager(MagicMock(), MagicMock())
        self.image_scanner = DatastoreImageScanner(self.image_manager,
                                                   self.vm_manager,
                                                   self.DATASTORE_ID)
        self.image_scanner._task_runner = MagicMock()
        self.image_scanner._task_runner.is_stopped.return_value = False
        self.write_count = 0
    def setUp(self):
        from testconfig import config
        if "agent_remote_test" not in config:
            raise SkipTest()

        # Set the default datastore name
        self._datastores = None

        if "datastores" in config["agent_remote_test"]:
            datastores = config["agent_remote_test"]["datastores"]
            self._datastores = [d.strip() for d in datastores.split(",")]
        else:
            self.fail("datastores not provided for test setUp")

        # Optionally update the specification of a remote iso file. The file
        # needs to exist on the remote esx server for this test to succeed.
        self._remote_iso_file = None
        self._second_remote_iso_file = None
        if ("iso_file" in config["agent_remote_test"]):
            self._remote_iso_file = config["agent_remote_test"]["iso_file"]

        if ("second_iso_file" in config["agent_remote_test"]):
            self._second_remote_iso_file = config["agent_remote_test"]["second_iso_file"]

        server = config["agent_remote_test"]["server"]
        self.server = server

        self.generation = int(time.time())

        # Connect to server and configure vim_client
        self.client_connections()
        self.vim_client = VimClient()
        self.vim_client.connect_ticket(self.server, self._get_vim_ticket())
        connect.SetSi(self.vim_client._si)

        # Set host mode to normal
        self.set_host_mode(HostMode.NORMAL)

        # The first time setup is called the agent will restart.
        self.provision_hosts()
        # Reconnect to account for the restart
        self.client_connections()
        self.clear()
    def test_acquire_credentials(self, password_mock, si_method_mock):
        """The mockery of acquiring local hostd credentials"""
        local_ticket_mock = MagicMock(name="local_ticket")
        user_name_property = PropertyMock(return_value="local-root")
        type(local_ticket_mock).userName = user_name_property

        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

        (user, password) = VimClient.acquire_credentials()
        assert_that(user, equal_to("local-root"))
    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)
class VsiTestCase(unittest.TestCase):
    """
    Check hostd rescpu.actav1 value equals to vsi's cpuLoadHistory1MinInPct
    """
    def setUp(self):
        self.vim_client = VimClient()

    def tearDown(self):
        pass

    def test_hostd_rescpu_actav1_match_vsi_value(self):
        try:
            vsi = VsiWrapper()
        except:
            return

        cpu_load_history = vsi.get(
            "/sched/groups/0/stats"
            "/cpuStatsDir/cpuLoadHistory"
            "/cpuLoadHistory1MinInPct")
        vsi_cpu_load = cpu_load_history["avgActive"]

        # get average cpu load percentage in past 20 seconds
        # since hostd takes a sample in every 20 seconds
        # we use the min 20secs here to get the latest
        # CPU active average over 1 minute
        host_stats = copy.copy(self.vim_client.get_perf_manager_stats(20))
        rescpu_cpu_load = host_stats['rescpu.actav1'] / 100
        check_value = False

        # vsi gets the current cpu active average over 1 minute.
        # hostd gets the cpu active average over 1 minute 20 seconds ago.
        # Thus if there's a CPU active average boost during the
        # past 20 seconds, the value from hostd's CPU active average
        # value will be 20 seconds late which will have a large deviation.
        if (1 if vsi_cpu_load - 7 < 1 else vsi_cpu_load - 7) \
                <= rescpu_cpu_load \
                <= vsi_cpu_load + 7:
            check_value = True

        self.assertEqual(check_value, True)
class TestVmManager(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._logger = logging.getLogger(__name__)
        self.vim_client = VimClient()
        self.vim_client.connect_userpwd(self.host, "root", self.pwd)
        self.vm_manager = VmManager(self.vim_client, None)
        for vm in self.vim_client._get_vms():
            vm.Destroy()

    def tearDown(self):
        self.vim_client.disconnect()

    @patch('os.path.exists', return_value=True)
    def test_mks_ticket(self, _exists):
        vm_id = self._vm_id()
        flavor = Flavor("vm", [
            QuotaLineItem("vm.cpu", 1, Unit.COUNT),
            QuotaLineItem("vm.memory", 8, Unit.MB)
        ])
        datastore = self.vim_client.get_all_datastores()[0].name
        spec = self.vm_manager.create_vm_spec(vm_id, datastore, flavor)
        try:
            self.vm_manager.create_vm(vm_id, spec)
            self.vm_manager.power_on_vm(vm_id)
            ticket = self.vm_manager.get_mks_ticket(vm_id)
            assert_that(ticket.cfg_file, not_none())
            assert_that(ticket.ticket, not_none())
        finally:
            self.vm_manager.power_off_vm(vm_id)
            self.vm_manager.delete_vm(vm_id)

    def _vm_id(self):
        vm_id = strftime("%Y-%m-%d-%H%M%S-", localtime())
        vm_id += str(random.randint(1, 10000))

        return vm_id

    def _test_port(self):
        return 5907
Beispiel #54
0
class VsiTestCase(unittest.TestCase):
    """
    Check hostd rescpu.actav1 value equals to vsi's cpuLoadHistory1MinInPct
    """
    def setUp(self):
        self.vim_client = VimClient()

    def tearDown(self):
        pass

    def test_hostd_rescpu_actav1_match_vsi_value(self):
        try:
            vsi = VsiWrapper()
        except:
            return

        cpu_load_history = vsi.get("/sched/groups/0/stats"
                                   "/cpuStatsDir/cpuLoadHistory"
                                   "/cpuLoadHistory1MinInPct")
        vsi_cpu_load = cpu_load_history["avgActive"]

        # get average cpu load percentage in past 20 seconds
        # since hostd takes a sample in every 20 seconds
        # we use the min 20secs here to get the latest
        # CPU active average over 1 minute
        host_stats = copy.copy(self.vim_client.get_perf_manager_stats(20))
        rescpu_cpu_load = host_stats['rescpu.actav1'] / 100
        check_value = False

        # vsi gets the current cpu active average over 1 minute.
        # hostd gets the cpu active average over 1 minute 20 seconds ago.
        # Thus if there's a CPU active average boost during the
        # past 20 seconds, the value from hostd's CPU active average
        # value will be 20 seconds late which will have a large deviation.
        if (1 if vsi_cpu_load - 7 < 1 else vsi_cpu_load - 7) \
                <= rescpu_cpu_load \
                <= vsi_cpu_load + 7:
            check_value = True

        self.assertEqual(check_value, True)
class TestVmManager(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._logger = logging.getLogger(__name__)
        self.vim_client = VimClient()
        self.vim_client.connect_userpwd(self.host, "root", self.pwd)
        self.vm_manager = VmManager(self.vim_client, None)
        for vm in self.vim_client._get_vms():
            vm.Destroy()

    def tearDown(self):
        self.vim_client.disconnect()

    @patch('os.path.exists', return_value=True)
    def test_mks_ticket(self, _exists):
        vm_id = self._vm_id()
        flavor = Flavor("vm", [QuotaLineItem("vm.cpu", 1, Unit.COUNT),
                               QuotaLineItem("vm.memory", 8, Unit.MB)])
        datastore = self.vim_client.get_all_datastores()[0].name
        spec = self.vm_manager.create_vm_spec(vm_id, datastore, flavor)
        try:
            self.vm_manager.create_vm(vm_id, spec)
            self.vm_manager.power_on_vm(vm_id)
            ticket = self.vm_manager.get_mks_ticket(vm_id)
            assert_that(ticket.cfg_file, not_none())
            assert_that(ticket.ticket, not_none())
        finally:
            self.vm_manager.power_off_vm(vm_id)
            self.vm_manager.delete_vm(vm_id)

    def _vm_id(self):
        vm_id = strftime("%Y-%m-%d-%H%M%S-", localtime())
        vm_id += str(random.randint(1, 10000))

        return vm_id

    def _test_port(self):
        return 5907
    def test_vim_client_errback(self, connect_mock, host_mock):
        callback = MagicMock()
        vim_client = VimClient("esx.local",
                               "root",
                               "password",
                               auto_sync=False,
                               errback=callback)
        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))
 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)
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)
 def setUp(self, connect, update, creds):
     creds.return_value = ["username", "password"]
     self.vim_client = VimClient(auto_sync=False)
     self.ds_manager = MagicMock()
     services.register(ServiceName.AGENT_CONFIG, MagicMock())
     self.image_manager = EsxImageManager(self.vim_client, self.ds_manager)
class TestEsxImageManager(unittest.TestCase):
    """Image Manager tests."""

    # We can use even more unit test coverage of the image manager here

    @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)
        self.ds_manager = MagicMock()
        services.register(ServiceName.AGENT_CONFIG, MagicMock())
        self.image_manager = EsxImageManager(self.vim_client, self.ds_manager)

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

    @patch("os.path.isdir", return_value=False)
    @patch("os.makedirs", side_effect=OSError)
    def test_make_image_dir(self, _makedirs, _isdir):
        self.assertRaises(
            OSError, self.image_manager._make_image_dir, "ds", "fake_iid")
        _isdir.assert_called_once_with("/vmfs/volumes/ds/images/fa/fake_iid")
        self.assertEqual(
            _makedirs.call_count, EsxImageManager.NUM_MAKEDIRS_ATTEMPTS)
        for i in range(0, EsxImageManager.NUM_MAKEDIRS_ATTEMPTS):
            self.assertEqual(_makedirs.call_args_list[i][0],
                             ("/vmfs/volumes/ds/images/fa/fake_iid",))

    @patch(
        "host.hypervisor.esx.image_manager.EsxImageManager.reap_tmp_images")
    def test_periodic_reaper(self, mock_reap):
        """ Test that the we invoke the image reaper periodically """
        image_manager = EsxImageManager(self.vim_client, self.ds_manager)
        image_manager.monitor_for_cleanup(reap_interval=0.1)

        self.assertFalse(image_manager._image_reaper is None)

        retry = 0
        while mock_reap.call_count < 2 and retry < 10:
            time.sleep(0.1)
            retry += 1
        image_manager.cleanup()
        assert_that(mock_reap.call_count, greater_than(1))
        assert_that(retry, is_not(10), "reaper cleanup not called repeatedly")

    @patch("uuid.uuid4", return_value="fake_id")
    @patch("host.hypervisor.esx.vm_config.os_datastore_path")
    def test_reap_tmp_images(self, _os_datastore_path, _uuid):
        """ Test that stray images are found and deleted by the reaper """

        def _fake_ds_folder(datastore, folder):
            return "%s__%s" % (datastore, folder)

        ds = MagicMock()
        ds.id = "dsid"
        ds.type = DatastoreType.EXT3

        # In a random transient directory, set up a directory to act as the
        # tmp images folder and to contain a stray image folder with a file.
        tmpdir = file_util.mkdtemp(delete=True)
        tmp_images_folder = _fake_ds_folder(ds.id, TMP_IMAGE_FOLDER_NAME)
        tmp_images_dir = os.path.join(tmpdir, tmp_images_folder)
        tmp_image_dir = os.path.join(tmp_images_dir, "stray_image")
        os.mkdir(tmp_images_dir)
        os.mkdir(tmp_image_dir)
        (fd, path) = tempfile.mkstemp(prefix='strayimage_', dir=tmp_image_dir)

        self.assertTrue(os.path.exists(path))

        def _fake_os_datastore_path(datastore, folder):
            return os.path.join(tmpdir, _fake_ds_folder(datastore, folder))

        _os_datastore_path.side_effect = _fake_os_datastore_path

        ds_manager = MagicMock()
        ds_manager.get_datastores.return_value = [ds]
        image_manager = EsxImageManager(self.vim_client, ds_manager)
        image_manager.reap_tmp_images()

        # verify stray image is deleted
        self.assertFalse(os.path.exists(path))

    @patch("os.path.isdir")
    @patch("os.makedirs")
    def test_vmdk_mkdir_eexist(self, _makedirs, _isdir):
        eexist = OSError()
        eexist.errno = errno.EEXIST
        _makedirs.side_effect = eexist
        _isdir.side_effect = (False,  # dest image dir missing
                              True)   # dest image dir is created

        self.image_manager._make_image_dir("ds", "fake_iid")
        _isdir.assert_called("/vmfs/volumes/ds/images/fa/fake_iid")

    @patch("pysdk.task.WaitForTask")
    @patch("uuid.uuid4", return_value="fake_id")
    @patch("os.path.exists")
    @patch("os.makedirs")
    @patch("shutil.copy")
    @patch("shutil.rmtree")
    @patch("shutil.move")
    @patch.object(EsxImageManager, "_manage_disk")
    @patch.object(EsxImageManager, "_get_datastore_type",
                  return_value=DatastoreType.EXT3)
    @patch.object(EsxImageManager, "_check_image_repair", return_value=False)
    @patch.object(EsxImageManager,
                  "check_and_validate_image", return_value=False)
    @patch.object(EsxImageManager, "_create_image_timestamp_file")
    @patch("host.hypervisor.esx.image_manager.FileBackedLock")
    def test_copy_image(self, _flock, _create_image_timestamp,
                        check_image, _check_image_repair,
                        _get_ds_type, _manage_disk,
                        _mv_dir, _rmtree, _copy, _makedirs, _exists,
                        _uuid, _wait_for_task):
        _exists.side_effect = (True,  # dest image vmdk missing
                               True)   # source meta file present

        self.image_manager.copy_image("ds1", "foo", "ds2", "bar")

        os_path_prefix1 = '/vmfs/volumes/ds1/images'
        os_path_prefix2 = '/vmfs/volumes/ds2/images'
        os_tmp_path_prefix = '/vmfs/volumes/ds2/tmp_images'

        _copy.assert_called_once_with(
            '%s/fo/foo/foo.%s' % (os_path_prefix1, METADATA_FILE_EXT),
            '/vmfs/volumes/ds2/tmp_images/fake_id')

        ds_path_prefix1 = '[] ' + os_path_prefix1
        ds_tmp_path_prefix = '[] ' + os_tmp_path_prefix

        expected_tmp_disk_ds_path = '%s/fake_id/%s.vmdk' % (ds_tmp_path_prefix,
                                                            'bar')

        _vd_spec = _manage_disk.call_args_list[0][1]['destSpec']

        self.assertEqual("thin", _vd_spec.diskType)
        self.assertEqual("lsiLogic", _vd_spec.adapterType)

        copy_call = call(vim.VirtualDiskManager.CopyVirtualDisk_Task,
                         sourceName='%s/fo/foo/foo.vmdk' % ds_path_prefix1,
                         destName=expected_tmp_disk_ds_path,
                         destSpec=_vd_spec)
        expected_vim_calls = [copy_call]
        self.assertEqual(expected_vim_calls, _manage_disk.call_args_list)
        _mv_dir.assert_called_once_with('/vmfs/volumes/ds2/tmp_images/fake_id',
                                        '%s/ba/bar' % os_path_prefix2)
        _create_image_timestamp.assert_called_once_with(
            "/vmfs/volumes/ds2/tmp_images/fake_id")

    @patch("pysdk.task.WaitForTask")
    @patch("uuid.uuid4", return_value="fake_id")
    @patch("os.path.exists")
    @patch("os.makedirs")
    @patch("shutil.copy")
    @patch.object(EsxImageManager, "_manage_disk")
    @patch.object(EsxImageManager, "_get_datastore_type",
                  return_value=DatastoreType.EXT3)
    @patch.object(EsxImageManager, "check_image", return_value=False)
    @patch.object(EsxImageManager, "_create_image_timestamp_file")
    @patch("host.hypervisor.esx.image_manager.FileBackedLock")
    def test_create_tmp_image(self, _flock, _create_image_timestamp,
                              check_image, _get_ds_type,
                              _manage_disk, _copy, _makedirs, _exists,
                              _uuid, _wait_for_task):

        # Common case is the same as the one covered by test_copy_image.

        # Check that things work when the src metadata file doesn't exist.
        _exists.side_effect = (False, True)
        ds_path_prefix1 = '[] /vmfs/volumes/ds1/images'
        expected_tmp_disk_ds_path = \
            "[] /vmfs/volumes/ds2/tmp_images/fake_id/bar.vmdk"
        self.image_manager._create_tmp_image("ds1", "foo", "ds2", "bar")
        _flock.assert_called_once_with("/vmfs/volumes/ds2/tmp_images/fake_id",
                                       DatastoreType.EXT3)
        # Verify that we don't copy the metadata file.
        self.assertFalse(_copy.called)

        # Verify that we copy the disk correctly
        _vd_spec = _manage_disk.call_args_list[0][1]['destSpec']

        self.assertEqual("thin", _vd_spec.diskType)
        self.assertEqual("lsiLogic", _vd_spec.adapterType)
        copy_call = call(vim.VirtualDiskManager.CopyVirtualDisk_Task,
                         sourceName='%s/fo/foo/foo.vmdk' % ds_path_prefix1,
                         destName=expected_tmp_disk_ds_path,
                         destSpec=_vd_spec)
        expected_vim_calls = [copy_call]
        self.assertEqual(expected_vim_calls, _manage_disk.call_args_list)

        # check that we return an IO error if the copy of metadata fails.
        _copy.side_effect = IOError
        _exists.side_effect = (True, True)
        _manage_disk.reset_mock()
        _flock.reset_mock()
        self.assertRaises(IOError, self.image_manager._create_tmp_image,
                          "ds1", "foo", "ds2", "bar")
        self.assertFalse(_manage_disk.called)
        _flock.assert_called_once_with("/vmfs/volumes/ds2/tmp_images/fake_id",
                                       DatastoreType.EXT3)
        _create_image_timestamp.assert_called_once_with(
            "/vmfs/volumes/ds2/tmp_images/fake_id")

    @patch("os.makedirs")
    @patch("shutil.rmtree")
    @patch("shutil.move")
    @patch.object(EsxImageManager, "_get_datastore_type",
                  return_value=DatastoreType.EXT3)
    @patch.object(EsxImageManager, "_check_image_repair", return_value=True)
    @patch("host.hypervisor.esx.image_manager.FileBackedLock")
    @raises(DiskAlreadyExistException)
    def test_move_image(self, _flock, check_image, _get_ds_type, _mv_dir,
                        _rmtree, _makedirs):
        # Common case is covered in test_copy_image.

        # check that if destination image directory exists we don't call move
        # and just bail after removing the tmp dir
        _rmtree.reset_mock()
        _mv_dir.reset_mock()
        expected_tmp_disk_folder = '/vmfs/volumes/ds2/tmp_images/bar'
        expected_rm_calls = [call(expected_tmp_disk_folder)]
        self.image_manager._move_image("foo", "ds1", expected_tmp_disk_folder)
        self.assertEqual(expected_rm_calls, _rmtree.call_args_list)
        _makedirs.assert_called_once_with('/vmfs/volumes/ds1/images/fo')
        _flock.assert_called_once_with('/vmfs/volumes/ds1/images/fo/foo',
                                       DatastoreType.EXT3, 3)

    @parameterized.expand([
        (True, ),
        (False, )
    ])
    @patch("os.path.exists")
    @patch.object(EsxImageManager, "_get_datastore_type",
                  return_value=DatastoreType.EXT3)
    @patch.object(EsxImageManager, "_create_image_timestamp_file")
    @patch.object(EsxImageManager, "_delete_renamed_image_timestamp_file")
    @patch("host.hypervisor.esx.image_manager.FileBackedLock")
    def test_validate_existing_image(self,
                                     create,
                                     _flock,
                                     _delete_renamed_timestamp_file,
                                     _create_timestamp_file,
                                     _get_ds_type,
                                     _path_exists):
        self._create_image_timestamp_file = create
        _path_exists.side_effect = self._local_os_path_exists
        _disk_folder = '/vmfs/volumes/ds1/images/fo/foo'
        self.image_manager._check_image_repair("foo", "ds1")

        if create:
            _create_timestamp_file.assert_called_once_with(_disk_folder)
            _delete_renamed_timestamp_file.assert_called_once()
        else:
            assert not _create_timestamp_file.called
            assert not _delete_renamed_timestamp_file.called

    def _local_os_path_exists(self, pathname):
        if not self._create_image_timestamp_file:
            return True
        if pathname.endswith(EsxImageManager.IMAGE_TIMESTAMP_FILE_NAME):
            return False
        else:
            return True

    @patch.object(EsxImageManager, "_clean_gc_dir")
    @patch.object(EsxImageManager, "_gc_image_dir")
    @patch.object(EsxImageManager, "_lock_data_disk")
    @patch.object(EsxImageManager, "create_image_tombstone")
    @patch.object(EsxImageManager, "check_image_dir")
    def test_delete(self, check_image_dir, create_image_tombstone,
                    lock_data_disk, gc_image_dir, clean_gc_dir):

        # Test successful delete
        check_image_dir.return_value = True
        self.image_manager.delete_image("ds1", "foo", 0, False)
        check_image_dir.assert_called_with("foo", "ds1")
        create_image_tombstone.assert_called_with("ds1", "foo")

        # Test successful delete with force option
        self.image_manager.delete_image("ds1", "foo", 0, True)
        check_image_dir.assert_called_with("foo", "ds1")
        create_image_tombstone.assert_called_with("ds1", "foo")
        lock_data_disk.assert_called_with("ds1", "foo")
        gc_image_dir.assert_called_with("ds1", "foo")
        clean_gc_dir.assert_called()

        # Test image not found
        check_image_dir.return_value = False
        self.assertRaises(ImageNotFoundException,
                          self.image_manager.delete_image,
                          "ds1", "foo", 0, False)

    @patch("host.hypervisor.esx.image_manager.os_vmdk_path")
    @patch("host.hypervisor.esx.image_manager.os_datastore_path")
    def test_gc_image_dir(self, dst_path, src_path):
        """ Test that we move the directory correctly to the GC location """
        src_dir = file_util.mkdtemp(delete=True)
        dst_dir = file_util.mkdtemp(delete=True)
        src_path.return_value = os.path.join(src_dir, "test.vmdk")
        dst_path.return_value = dst_dir

        self.image_manager._gc_image_dir("ds1", "foo")
        uuid_dir = os.path.join(dst_dir, os.listdir(dst_dir)[0])

        # Verify the src directory has been moved into the garbage dir.
        self.assertEqual(os.listdir(uuid_dir), [os.path.basename(src_dir)])

        src_path.assert_called_once_with("ds1", "foo", IMAGE_FOLDER_NAME)
        dst_path.assert_called_once_with("ds1", GC_IMAGE_FOLDER)

    def test_image_path(self):
        image_path = "/vmfs/volumes/ds/images/tt/ttylinux/ttylinux.vmdk"
        ds = self.image_manager.get_datastore_id_from_path(image_path)
        image = self.image_manager.get_image_id_from_path(image_path)
        self.assertEqual(ds, "ds")
        self.assertEqual(image, "ttylinux")

    @patch("host.hypervisor.esx.image_manager.os_vmdk_flat_path")
    @patch("host.hypervisor.esx.image_manager.os.remove")
    def test_lock_data_disk(self, mock_rm, vmdk_flat_path):
        """ Test acquisition of the lock on the flat file. """
        vmdk_flat_path.return_value = "fake_f_name"
        self.assertTrue(self.image_manager._lock_data_disk("ds1", "foo"))
        vmdk_flat_path.assert_called_once_with("ds1", "foo")
        mock_rm.side_effect = OSError
        self.assertFalse(self.image_manager._lock_data_disk("ds1", "foo"))

    @parameterized.expand([
        ("CLOUD", "EAGER", ImageType.CLOUD, ImageReplication.EAGER),
        ("MANAGEMENT", "EAGER", ImageType.MANAGEMENT, ImageReplication.EAGER),
        ("CLOUD", "ON_DEMAND", ImageType.CLOUD, ImageReplication.ON_DEMAND),
        ("MANAGEMENT", "ON_DEMAND", ImageType.MANAGEMENT,
         ImageReplication.ON_DEMAND),
    ])
    def test_image_type(self, type, replication, expected_type,
                        expected_replication):

        self.ds_manager.image_datastores.return_value = "ds1"
        with patch("host.hypervisor.esx.image_manager.os_image_manifest_path"
                   "") as manifest_path:
            tmpdir = file_util.mkdtemp(delete=True)
            tmpfile = os.path.join(tmpdir, "ds1.manifest")
            manifest_path.return_value = tmpfile

            with open(tmpfile, 'w+') as f:
                f.write('{"imageType":"%s","imageReplication":"%s"}' % (
                    type, replication))

            type, replication = self.image_manager.get_image_manifest(
                "image_id")
            self.assertEqual(type, expected_type)
            self.assertEqual(replication, expected_replication)

    @patch.object(EsxImageManager, "_move_image")
    @patch.object(EsxImageManager, "check_image_dir", return_value=False)
    @patch.object(EsxImageManager, "_create_image_timestamp_file_from_ids")
    @patch("os.path.exists")
    def test_create_image(self, _exists, _create_timestamp,
                          check_image_dir, move_image):

        # Happy path verify move is called with the right args.
        _exists.side_effect = ([True])
        self.image_manager.create_image("ds1", "foo", "img_1")
        check_image_dir.assert_called_once_with("img_1", "ds1")
        move_image.assert_called_once_with('img_1', 'ds1',
                                           '/vmfs/volumes/ds1/foo')
        _create_timestamp.assert_called_once_with("ds1", "img_1")

        # Verify error if tmp image doesn't exist
        _exists.side_effect = ([False])
        move_image.reset_mock()
        self.assertRaises(ImageNotFoundException,
                          self.image_manager.create_image,
                          "ds1", "foo", "img_1")
        self.assertFalse(move_image.called)

        # Verify error if destination image already exists.
        _exists.side_effect = ([True])
        move_image.reset_mock()
        check_image_dir.return_value = True
        self.assertRaises(DiskAlreadyExistException,
                          self.image_manager.create_image,
                          "ds1", "foo", "img_1")
        self.assertFalse(move_image.called)

    @patch.object(EsxImageManager, "create_image")
    @patch.object(EsxImageManager, "_manage_disk")
    @patch("os.path.exists", return_value=True)
    def test_create_image_with_vm_disk(self, _exists, _manage_disk,
                                       _create_image):
        vm_disk_path = "/vmfs/volumes/dsname/vms/ab/cd.vmdk"
        self.image_manager.create_image_with_vm_disk(
            "ds1", "foo", "img_1", vm_disk_path)

        # Verify that we copy the disk correctly
        expected_tmp_disk_ds_path = \
            "[] /vmfs/volumes/ds1/foo/img_1.vmdk"
        _vd_spec = _manage_disk.call_args_list[0][1]['destSpec']
        self.assertEqual("thin", _vd_spec.diskType)
        self.assertEqual("lsiLogic", _vd_spec.adapterType)
        copy_call = call(vim.VirtualDiskManager.CopyVirtualDisk_Task,
                         sourceName='[] %s' % vm_disk_path,
                         destName=expected_tmp_disk_ds_path,
                         destSpec=_vd_spec)
        expected_vim_calls = [copy_call]
        self.assertEqual(expected_vim_calls, _manage_disk.call_args_list)

        _create_image.assert_called_once_with("ds1", "foo", "img_1")

    @patch("shutil.rmtree")
    @patch("os.path.exists")
    def test_delete_tmp_dir(self, _exists, _rmtree):
        self.image_manager.delete_tmp_dir("ds1", "foo")
        _exists.assert_called_once("/vmfs/volumes/ds1/foo")
        _rmtree.assert_called_once("/vmfs/volumes/ds1/foo")

        _exists.reset_mock()
        _exists.return_value = False
        _rmtree.reset_mock()
        self.assertRaises(DirectoryNotFound,
                          self.image_manager.delete_tmp_dir,
                          "ds1", "foo")
        _exists.assert_called_once("/vmfs/volumes/ds1/foo")
        self.assertFalse(_rmtree.called)