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)
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)