def _start_syncing_cache(self):
     self._logger.info("Start vim client sync vm cache thread")
     self._vim_cache = VimCache()
     self._vim_cache.poll_updates(self)
     self._sync_thread = SyncVimCacheThread(self, self._vim_cache,
                                            self._wait_timeout,
                                            self._min_interval,
                                            self._errback)
     self._sync_thread.start()
Example #2
0
 def _start_syncing_cache(self):
     self._logger.info("Start vim client sync vm cache thread")
     self._vim_cache = VimCache()
     self._vim_cache.poll_updates(self)
     self._sync_thread = SyncVimCacheThread(self, self._vim_cache, self._wait_timeout,
                                            self._min_interval, self._errback)
     self._sync_thread.start()
class VimClient():
    """Wrapper class around VIM API calls using Service Instance connection"""
    def __init__(self,
                 auto_sync=True,
                 errback=None,
                 wait_timeout=10,
                 min_interval=1):
        self._logger = logging.getLogger(__name__)
        self._logger.info("VimClient init")
        self._sync_thread = None
        self._auto_sync = auto_sync
        self._errback = errback
        self._wait_timeout = wait_timeout
        self._min_interval = min_interval
        self._update_listeners = set()
        self._lock = threading.Lock()
        self._task_counter = 0
        self._vim_cache = None

    def connect_ticket(self, host, ticket):
        if ticket:
            try:
                stub = connect.SoapStubAdapter(host, HOSTD_PORT, VIM_NAMESPACE)
                self._si = vim.ServiceInstance("ServiceInstance", stub)
                self._si.RetrieveContent().sessionManager.CloneSession(ticket)
                self._post_connect()
            except httplib.HTTPException as http_exception:
                self._logger.info("Failed to login hostd with ticket: %s" %
                                  http_exception)
                raise AcquireCredentialsException(http_exception)

    def connect_userpwd(self, host, user, pwd):
        try:
            self._si = connect.Connect(host=host,
                                       user=user,
                                       pwd=pwd,
                                       version=VIM_VERSION)
            self._post_connect()
        except vim.fault.HostConnectFault as connection_exception:
            self._logger.info("Failed to connect to hostd: %s" %
                              connection_exception)
            raise HostdConnectionFailure(connection_exception)

    def connect_local(self):
        username, password = self._acquire_local_credentials()
        self.connect_userpwd("localhost", username, password)

    def _create_local_service_instance(self):
        stub = connect.SoapStubAdapter("localhost", HOSTD_PORT)
        return vim.ServiceInstance("ServiceInstance", stub)

    def _acquire_local_credentials(self):
        si = self._create_local_service_instance()
        try:
            session_manager = si.content.sessionManager
        except httplib.HTTPException as http_exception:
            self._logger.info("Failed to retrieve credentials from hostd: %s" %
                              http_exception)
            raise AcquireCredentialsException(http_exception)

        local_ticket = session_manager.AcquireLocalTicket(userName="******")
        username = local_ticket.userName
        password = file(local_ticket.passwordFilePath).read()
        return username, password

    def _post_connect(self):
        self._content = self._si.RetrieveContent()
        if self._auto_sync:
            # Start syncing vm cache periodically
            self._start_syncing_cache()

    def disconnect(self):
        """ Disconnect vim client
        :param wait: If wait is true, it waits until the sync thread exit.
        """
        self._logger.info("vimclient disconnect")
        self._stop_syncing_cache()
        try:
            connect.Disconnect(self._si)
        except:
            self._logger.warning("Failed to disconnect vim_client: %s" %
                                 sys.exc_info()[1])

    def _start_syncing_cache(self):
        self._logger.info("Start vim client sync vm cache thread")
        self._vim_cache = VimCache()
        self._vim_cache.poll_updates(self)
        self._sync_thread = SyncVimCacheThread(self, self._vim_cache,
                                               self._wait_timeout,
                                               self._min_interval,
                                               self._errback)
        self._sync_thread.start()

    def _stop_syncing_cache(self):
        if self._sync_thread:
            self._sync_thread.stop()
            self._sync_thread.join()

    @lock_with("_lock")
    def add_update_listener(self, listener):
        # Notify the listener immediately since there might have already been some updates.
        listener.datastores_updated()
        self._update_listeners.add(listener)

    @lock_with("_lock")
    def remove_update_listener(self, listener):
        self._update_listeners.discard(listener)

    @property
    @lock_with("_lock")
    def update_listeners(self):
        return self._update_listeners

    def query_config(self):
        env_browser = invt.GetEnv(si=self._si)
        return env_browser.QueryConfigOption("vmx-10", None)

    @property
    def _property_collector(self):
        return self._content.propertyCollector

    def _root_resource_pool(self):
        """Get the root resource pool for this host.
        :rtype: vim.ResourcePool
        """
        return host.GetRootResourcePool(self._si)

    def _vm_folder(self):
        """Get the default vm folder for this host.
        :rtype: vim.Folder
        """
        return invt.GetVmFolder(si=self._si)

    def host_system(self):
        return host.GetHostSystem(self._si)

    def _find_by_inventory_path(self, *path):
        """
        Finds a managed entity based on its location in the inventory.

        :param path: Inventory path
        :type path: tuple
        :rtype: vim.ManagedEntity
        """
        dc = (HA_DATACENTER_ID, )
        # Convert a tuple of strings to a path for use with `find_by_inventory_path`.
        p = "/".join(p.replace("/", "%2f") for p in dc + path if p)
        return self._content.searchIndex.FindByInventoryPath(p)

    def get_vm(self, vm_id):
        """Get the vm reference on a host.
        :param vm_id: The name of the vm.
        :rtype A vim vm reference.
        """
        vm = self._find_by_inventory_path(VM_FOLDER_NAME, vm_id)
        if not vm:
            raise VmNotFoundException("VM '%s' not found on host." % vm_id)

        return vm

    def get_all_datastores(self):
        """Get all datastores for this host.
        :rtype: list of vim.Datastore
        """
        datastores = []
        for ds in self._find_by_inventory_path(
                DATASTORE_FOLDER_NAME).childEntity:
            datastores.append(VimDatastore(ds))
        return datastores

    def _get_vms(self):
        """ Get VirtualMachine from hostd. Use get_vms_in_cache to have a
        better performance unless you want Vim Object.

        :return: list of vim.VirtualMachine
        """
        PC = vmodl.query.PropertyCollector
        traversal_spec = PC.TraversalSpec(name="folderTraversalSpec",
                                          type=vim.Folder,
                                          path="childEntity",
                                          skip=False)
        property_spec = PC.PropertySpec(
            type=vim.VirtualMachine,
            pathSet=["name", "runtime.powerState", "layout.disk", "config"])
        object_spec = PC.ObjectSpec(obj=self._vm_folder(),
                                    selectSet=[traversal_spec])
        filter_spec = PC.FilterSpec(propSet=[property_spec],
                                    objectSet=[object_spec])

        objects = self._property_collector.RetrieveContents([filter_spec])
        return [object.obj for object in objects]

    def get_vms_in_cache(self):
        """ Get information of all VMs from cache.

        :return: list of VmCache
        """
        return self._vim_cache.get_vms_in_cache()

    def get_vm_in_cache(self, vm_id):
        """ Get information of a VM from cache. The vm state is not
        guaranteed to be up-to-date. Also only name and power_state is
        guaranteed to be not None.

        :return: VmCache for the vm that is found
        :raise VmNotFoundException when vm is not found
        """
        return self._vim_cache.get_vm_in_cache(vm_id)

    def create_vm_spec(self, vm_id, datastore, memoryMB, cpus, metadata, env):
        spec = EsxVmConfigSpec(self.query_config())
        spec.init_for_create(vm_id, datastore, memoryMB, cpus, metadata, env)
        return spec

    def create_vm(self, vm_id, create_spec):
        """Create a new Virtual Maching given a VM create spec.

        :param vm_id: The Vm id
        :type vm_id: string
        :param create_spec: EsxVmConfigSpec
        :type ConfigSpec
        :raise: VmAlreadyExistException
        """
        # sanity check since VIM does not prevent this
        try:
            if self.get_vm_in_cache(vm_id):
                raise VmAlreadyExistException("VM already exists")
        except VmNotFoundException:
            pass

        folder = self._vm_folder()
        resource_pool = self._root_resource_pool()
        spec = create_spec.get_spec()
        # The scenario of the vm creation at ESX where intermediate directory
        # has to be created has not been well exercised and is known to be
        # racy and not informative on failures. So be defensive and proactively
        # create the intermediate directory ("/vmfs/volumes/<dsid>/vm_xy").
        try:
            self.make_directory(spec.files.vmPathName)
        except vim.fault.FileAlreadyExists:
            self._logger.debug(
                "VM directory %s exists, will create VM using it" %
                spec.files.vmPathName)

        task = folder.CreateVm(spec, resource_pool, None)
        self.wait_for_task(task)
        self.wait_for_vm_create(vm_id)

    def get_networks(self):
        return [
            network.name for network in self._find_by_inventory_path(
                NETWORK_FOLDER_NAME).childEntity
        ]

    def create_disk(self, path, size):
        spec = vim.VirtualDiskManager.FileBackedVirtualDiskSpec()
        spec.capacityKb = size * (1024**2)
        spec.diskType = vim.VirtualDiskManager.VirtualDiskType.thin
        spec.adapterType = DEFAULT_DISK_ADAPTER_TYPE

        try:
            disk_mgr = self._content.virtualDiskManager
            vim_task = disk_mgr.CreateVirtualDisk(
                name=os_to_datastore_path(path), spec=spec)
            self.wait_for_task(vim_task)
        except vim.fault.FileAlreadyExists, e:
            raise DiskAlreadyExistException(e.msg)
        except vim.fault.FileFault, e:
            raise DiskFileException(e.msg)
Example #4
0
class VimClient():
    """Wrapper class around VIM API calls using Service Instance connection"""

    def __init__(self, auto_sync=True, errback=None, wait_timeout=10, min_interval=1):
        self._logger = logging.getLogger(__name__)
        self._logger.info("VimClient init")
        self._sync_thread = None
        self._auto_sync = auto_sync
        self._errback = errback
        self._wait_timeout = wait_timeout
        self._min_interval = min_interval
        self._update_listeners = set()
        self._lock = threading.Lock()
        self._task_counter = 0
        self._vim_cache = None

    def connect_ticket(self, host, ticket):
        if ticket:
            try:
                stub = connect.SoapStubAdapter(host, HOSTD_PORT, VIM_NAMESPACE)
                self._si = vim.ServiceInstance("ServiceInstance", stub)
                self._si.RetrieveContent().sessionManager.CloneSession(ticket)
                self._post_connect()
            except httplib.HTTPException as http_exception:
                self._logger.info("Failed to login hostd with ticket: %s" % http_exception)
                raise AcquireCredentialsException(http_exception)

    def connect_userpwd(self, host, user, pwd):
        try:
            self._si = connect.Connect(host=host, user=user, pwd=pwd, version=VIM_VERSION)
            self._post_connect()
        except vim.fault.HostConnectFault as connection_exception:
            self._logger.info("Failed to connect to hostd: %s" % connection_exception)
            raise HostdConnectionFailure(connection_exception)

    def connect_local(self):
        username, password = self._acquire_local_credentials()
        self.connect_userpwd("localhost", username, password)

    def _create_local_service_instance(self):
        stub = connect.SoapStubAdapter("localhost", HOSTD_PORT)
        return vim.ServiceInstance("ServiceInstance", stub)

    def _acquire_local_credentials(self):
        si = self._create_local_service_instance()
        try:
            session_manager = si.content.sessionManager
        except httplib.HTTPException as http_exception:
            self._logger.info("Failed to retrieve credentials from hostd: %s" % http_exception)
            raise AcquireCredentialsException(http_exception)

        local_ticket = session_manager.AcquireLocalTicket(userName="******")
        username = local_ticket.userName
        password = file(local_ticket.passwordFilePath).read()
        return username, password

    def _post_connect(self):
        self._content = self._si.RetrieveContent()
        if self._auto_sync:
            # Start syncing vm cache periodically
            self._start_syncing_cache()

    def disconnect(self):
        """ Disconnect vim client
        :param wait: If wait is true, it waits until the sync thread exit.
        """
        self._logger.info("vimclient disconnect")
        self._stop_syncing_cache()
        try:
            connect.Disconnect(self._si)
        except:
            self._logger.warning("Failed to disconnect vim_client: %s" % sys.exc_info()[1])

    def _start_syncing_cache(self):
        self._logger.info("Start vim client sync vm cache thread")
        self._vim_cache = VimCache()
        self._vim_cache.poll_updates(self)
        self._sync_thread = SyncVimCacheThread(self, self._vim_cache, self._wait_timeout,
                                               self._min_interval, self._errback)
        self._sync_thread.start()

    def _stop_syncing_cache(self):
        if self._sync_thread:
            self._sync_thread.stop()
            self._sync_thread.join()

    @lock_with("_lock")
    def add_update_listener(self, listener):
        # Notify the listener immediately since there might have already been some updates.
        listener.datastores_updated()
        self._update_listeners.add(listener)

    @lock_with("_lock")
    def remove_update_listener(self, listener):
        self._update_listeners.discard(listener)

    @property
    @lock_with("_lock")
    def update_listeners(self):
        return self._update_listeners

    def query_config(self):
        env_browser = invt.GetEnv(si=self._si)
        return env_browser.QueryConfigOption("vmx-10", None)

    @property
    def _property_collector(self):
        return self._content.propertyCollector

    def _root_resource_pool(self):
        """Get the root resource pool for this host.
        :rtype: vim.ResourcePool
        """
        return host.GetRootResourcePool(self._si)

    def _vm_folder(self):
        """Get the default vm folder for this host.
        :rtype: vim.Folder
        """
        return invt.GetVmFolder(si=self._si)

    def host_system(self):
        return host.GetHostSystem(self._si)

    def _find_by_inventory_path(self, *path):
        """
        Finds a managed entity based on its location in the inventory.

        :param path: Inventory path
        :type path: tuple
        :rtype: vim.ManagedEntity
        """
        dc = (HA_DATACENTER_ID,)
        # Convert a tuple of strings to a path for use with `find_by_inventory_path`.
        p = "/".join(p.replace("/", "%2f") for p in dc + path if p)
        return self._content.searchIndex.FindByInventoryPath(p)

    def get_vm(self, vm_id):
        """Get the vm reference on a host.
        :param vm_id: The name of the vm.
        :rtype A vim vm reference.
        """
        vm = self._find_by_inventory_path(VM_FOLDER_NAME, vm_id)
        if not vm:
            raise VmNotFoundException("VM '%s' not found on host." % vm_id)

        return vm

    def get_all_datastores(self):
        """Get all datastores for this host.
        :rtype: list of vim.Datastore
        """
        datastores = []
        for ds in self._find_by_inventory_path(DATASTORE_FOLDER_NAME).childEntity:
            datastores.append(VimDatastore(ds))
        return datastores

    def _get_vms(self):
        """ Get VirtualMachine from hostd. Use get_vms_in_cache to have a
        better performance unless you want Vim Object.

        :return: list of vim.VirtualMachine
        """
        PC = vmodl.query.PropertyCollector
        traversal_spec = PC.TraversalSpec(name="folderTraversalSpec", type=vim.Folder, path="childEntity", skip=False)
        property_spec = PC.PropertySpec(type=vim.VirtualMachine,
                                        pathSet=["name", "runtime.powerState", "layout.disk", "config"])
        object_spec = PC.ObjectSpec(obj=self._vm_folder(), selectSet=[traversal_spec])
        filter_spec = PC.FilterSpec(propSet=[property_spec], objectSet=[object_spec])

        objects = self._property_collector.RetrieveContents([filter_spec])
        return [object.obj for object in objects]

    def get_vms_in_cache(self):
        """ Get information of all VMs from cache.

        :return: list of VmCache
        """
        return self._vim_cache.get_vms_in_cache()

    def get_vm_in_cache(self, vm_id):
        """ Get information of a VM from cache. The vm state is not
        guaranteed to be up-to-date. Also only name and power_state is
        guaranteed to be not None.

        :return: VmCache for the vm that is found
        :raise VmNotFoundException when vm is not found
        """
        return self._vim_cache.get_vm_in_cache(vm_id)

    def create_vm_spec(self, vm_id, datastore, memoryMB, cpus, metadata, env):
        spec = EsxVmConfigSpec(self.query_config())
        spec.init_for_create(vm_id, datastore, memoryMB, cpus, metadata, env)
        return spec

    def create_vm(self, vm_id, create_spec):
        """Create a new Virtual Maching given a VM create spec.

        :param vm_id: The Vm id
        :type vm_id: string
        :param create_spec: EsxVmConfigSpec
        :type ConfigSpec
        :raise: VmAlreadyExistException
        """
        # sanity check since VIM does not prevent this
        try:
            if self.get_vm_in_cache(vm_id):
                raise VmAlreadyExistException("VM already exists")
        except VmNotFoundException:
            pass

        folder = self._vm_folder()
        resource_pool = self._root_resource_pool()
        spec = create_spec.get_spec()
        # The scenario of the vm creation at ESX where intermediate directory
        # has to be created has not been well exercised and is known to be
        # racy and not informative on failures. So be defensive and proactively
        # create the intermediate directory ("/vmfs/volumes/<dsid>/vm_xy").
        try:
            self.make_directory(spec.files.vmPathName)
        except vim.fault.FileAlreadyExists:
            self._logger.debug("VM directory %s exists, will create VM using it" % spec.files.vmPathName)

        task = folder.CreateVm(spec, resource_pool, None)
        self.wait_for_task(task)
        self.wait_for_vm_create(vm_id)

    def get_networks(self):
        return [network.name for network
                in self._find_by_inventory_path(NETWORK_FOLDER_NAME).childEntity]

    def create_disk(self, path, size):
        spec = vim.VirtualDiskManager.FileBackedVirtualDiskSpec()
        spec.capacityKb = size * (1024 ** 2)
        spec.diskType = vim.VirtualDiskManager.VirtualDiskType.thin
        spec.adapterType = DEFAULT_DISK_ADAPTER_TYPE

        try:
            disk_mgr = self._content.virtualDiskManager
            vim_task = disk_mgr.CreateVirtualDisk(name=os_to_datastore_path(path), spec=spec)
            self.wait_for_task(vim_task)
        except vim.fault.FileAlreadyExists, e:
            raise DiskAlreadyExistException(e.msg)
        except vim.fault.FileFault, e:
            raise DiskFileException(e.msg)