Skip to content

Commit

Permalink
Major pyVmomi update for vSphere 8.0
Browse files Browse the repository at this point in the history
# Breaking changes:
- Minimum Python 2 requirement is 2.7.9
- DynamicTypeManagerHelper.py is removed
- ManagedMethodExecutorHelper.py is removed
- connect.ConnectNoSSL() and connect.SmartConnectNoSSL() are removed.
  Use connect.Connect(disableSslCertValidation=True) and connect.SmartConnect(disableSslCertValidation=True)
- VmomiSupport.UncallableManagedMethod is renamed to VmomiSupport.UnknownManagedMethod

# New modules:
Security.py
A new module is added to handle thumbprint verification.
There is a predefined set of available crypto functions to verify the certificate thumbprints.
Its possible to configure during runtime which of the available crypto functions are allowed.

Feature.py
A new module related to pyVmomi development within VMware.

VmomiJSONEncoder.py
The VmomiJSONEncoder is moved into a dedicated module.

# More changes:
- A new 'binaryIsBytearray' setting is added to select the base type for the binary type. By default, the binary type is 'str' for Python 2 and 'bytes' for Python 3. If binaryIsBytearray is True, the binary type for Python 2 is set to 'bytearray'. Required for VmomiJSONEncoder to work properly.
- The license note is removed from the Python files. LICENSE.txt holds the Apache 2 license note.
- pyVmomi now uses relative imports
- Dependency on “requests” is removed
- Added support for SAML token authentication
- Added timeout for HTTP requests
- Added option to set the maximum amount of time a task is allowed to run. On timeout, an exception is generated if raiseOnError is True.
- Add option to get all updates for the task.
- Add option to use a logger instead of the standard output
- Various bug fixes
- Code style improvements

# Deprecated:
- connect.OpenUrlWithBasicAuth()
- connect.OpenPathWithStub()
  • Loading branch information
ddraganov committed Nov 24, 2022
1 parent b9129d9 commit a90023f
Show file tree
Hide file tree
Showing 22 changed files with 5,610 additions and 5,202 deletions.
1,694 changes: 888 additions & 806 deletions pyVim/connect.py

Large diffs are not rendered by default.

295 changes: 171 additions & 124 deletions pyVim/sso.py

Large diffs are not rendered by default.

154 changes: 92 additions & 62 deletions pyVim/task.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,13 @@
# VMware vSphere Python SDK
# Copyright (c) 2016 VMware, Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#############################################################
# Copyright (c) 2005-2022 VMware, Inc.
#############################################################

## @file task.py
## @brief Task functions
##
## This module provies synchronization of client/server operations
## since many VIM operations return 'tasks' which can have
## varying completion times.

"""
Task functions
Expand All @@ -28,8 +16,12 @@
times.
"""

__author__ = 'VMware, Inc'

from pyVmomi import vmodl, vim

import time


##
## @brief Exception class to represent when task is blocked (e.g.:
Expand All @@ -48,12 +40,12 @@ class TaskBlocked(Exception):
# verbose information about task progress
#
def TaskUpdatesVerbose(task, progress):
if isinstance(task.info.progress, int):
info = task.info
info = task.info
if isinstance(info.progress, int):
if not isinstance(progress, str):
progress = '%d%% (%s)' % (info.progress, info.state)
print('Task %s (key:%s, desc:%s) - %s' % (
info.name.info.name, info.key, info.description, progress))
progress = '{:.0f}% ({})'.format(info.progress, info.state)
print('Task %s (key:%s, desc:%s) - %s' %
(info.name.info.name, info.key, info.description, progress))


globalTaskUpdate = None
Expand All @@ -72,11 +64,14 @@ def SetTasksVerbose(verbose=True):
## raiseOnError is set to true
## @param si [in] ServiceInstance to use. If set to None, use the default one.
## @param pc [in] property collector to use else retrieve one from cache
## @param maxWaitTime [in] The maximum amount of time the task is allowed to
## run. Throws an exception if raiseOnError is True.
## @param onProgressUpdate [in] callable to call with task progress updates.
## For example:
##
## def OnTaskProgressUpdate(task, percentDone):
## sys.stderr.write('# Task %s: %d%% complete ...\n' % (task, percentDone))
## sys.stderr.write('# Task {}: {:.0f}% complete ...\n'
## .format(task, percentDone))
##
## Given a task object and a service instance, wait for the task completion
##
Expand All @@ -89,58 +84,85 @@ def WaitForTask(task,
raiseOnError=True,
si=None,
pc=None,
onProgressUpdate=None):
maxWaitTime=None,
onProgressUpdate=None,
log=None,
getAllUpdates=False):
"""
Wait for task to complete.
@type raiseOnError : bool
@param raiseOnError : Any exception thrown is thrown up to the caller
if raiseOnError is set to true.
if raiseOnError is set to true.
@type si : ManagedObjectReference to a ServiceInstance.
@param si : ServiceInstance to use. If None, use the
information from the task.
information from the task.
@type pc : ManagedObjectReference to a PropertyCollector.
@param pc : Property collector to use. If None, get it from
the ServiceInstance.
the ServiceInstance.
@type maxWaitTime : numeric value
@param maxWaitTime : The maximum amount of time the task is allowed to
run. Throws an exception if raiseOnError is True.
@type onProgressUpdate : callable
@param onProgressUpdate : Callable to call with task progress updates.
@type log : Optional logger.
@param log : Logging.
@type getAllUpdates : Optional bool value. Default is False.
@param getAllUpdates : Get all updates for the task.
For example::
def OnTaskProgressUpdate(task, percentDone):
print 'Task %s is %d%% complete.' % (task, percentDone)
print 'Task {} is {:.0f}% complete.'.format(task, percentDone)
"""

if si is None:
si = vim.ServiceInstance("ServiceInstance", task._stub)
if pc is None:
if si is None: si = vim.ServiceInstance("ServiceInstance", task._stub)
pc = si.content.propertyCollector

progressUpdater = ProgressUpdater(task, onProgressUpdate)
progressUpdater = ProgressUpdater(task, onProgressUpdate, getAllUpdates)
progressUpdater.Update('created')

filter = CreateFilter(pc, task)

version, state = None, None

if maxWaitTime:
waitTime = maxWaitTime + time.time()

# Loop looking for updates till the state moves to a completed state.
while state not in (vim.TaskInfo.State.success, vim.TaskInfo.State.error):
try:
version, state = GetTaskStatus(task, version, pc)
progressUpdater.UpdateIfNeeded()
except vmodl.fault.ManagedObjectNotFound as e:

if maxWaitTime and waitTime < time.time():
err = "Task exceeded timeout of %d seconds" % maxWaitTime
progressUpdater.Update(err)
if raiseOnError is True:
raise Exception(err)
break
except vmodl.Fault.ManagedObjectNotFound as e:
print("Task object has been deleted: %s" % e.obj)
break

filter.Destroy()

info = task.info
if state == "error":
progressUpdater.Update('error: %s' % str(task.info.error))
progressUpdater.Update("error: %s" % info.error)
if raiseOnError:
raise task.info.error
raise info.error
else:
print("Task reported error: " + str(task.info.error))
else:
_LogMsg(log, "Task reported error: %s" % info.error)
elif state == "success":
progressUpdater.Update('completed')
elif state == "queued":
_LogMsg(log, "Task reports as queued: %s" % info)
progressUpdater.UpdateIfNeeded()
else: # state = running
_LogMsg(log, "Task reports as still running: %s" % info)
progressUpdater.UpdateIfNeeded()

return state

Expand All @@ -157,25 +179,23 @@ def WaitForTasks(tasks,
si=None,
pc=None,
onProgressUpdate=None,
results=None):
results=None,
getAllUpdates=False):
"""
Wait for mulitiple tasks to complete. Much faster than calling WaitForTask
N times
Wait for multiple tasks to complete. Much faster than calling WaitForTask
N times.
"""

if not tasks:
return

if si is None:
si = vim.ServiceInstance("ServiceInstance", tasks[0]._stub)
if pc is None:
pc = si.content.propertyCollector
if results is None:
results = []
if si is None: si = vim.ServiceInstance("ServiceInstance", tasks[0]._stub)
if pc is None: pc = si.content.propertyCollector
if results is None: results = []

progressUpdaters = {}
for task in tasks:
progressUpdater = ProgressUpdater(task, onProgressUpdate)
progressUpdater = ProgressUpdater(task, onProgressUpdate, getAllUpdates)
progressUpdater.Update('created')
progressUpdaters[str(task)] = progressUpdater

Expand Down Expand Up @@ -215,7 +235,8 @@ def WaitForTasks(tasks,
if raiseOnError:
raise err
else:
print("Task %s reported error: %s" % (taskId, str(err)))
print("Task %s reported error: %s" %
(taskId, str(err)))
progressUpdaters.pop(taskId)
else:
if onProgressUpdate:
Expand All @@ -230,11 +251,12 @@ def WaitForTasks(tasks,

def GetTaskStatus(task, version, pc):
update = pc.WaitForUpdates(version)
state = task.info.state
info = task.info
state = info.state

if (state == 'running' and task.info.name is not None and task.info.name.info.name != "Destroy"
and task.info.name.info.name != "Relocate"):
CheckForQuestionPending(task)
if (state == 'running' and info.name is not None and info.name.info.name
not in ['Destroy', 'Relocate', 'CreateVm']):
CheckForQuestionPending(info)

return update.version, state

Expand All @@ -250,34 +272,42 @@ def CreateTasksFilter(pc, tasks):
return None

# First create the object specification as the task object.
objspecs = [vmodl.query.PropertyCollector.ObjectSpec(obj=task)
for task in tasks]
objspecs = [
vmodl.Query.PropertyCollector.ObjectSpec(obj=task) for task in tasks
]

# Next, create the property specification as the state.
propspec = vmodl.query.PropertyCollector.PropertySpec(
type=vim.Task, pathSet=[], all=True)
propspec = vmodl.Query.PropertyCollector.PropertySpec(type=vim.Task,
pathSet=[],
all=True)

# Create a filter spec with the specified object and property spec.
filterspec = vmodl.query.PropertyCollector.FilterSpec()
filterspec = vmodl.Query.PropertyCollector.FilterSpec()
filterspec.objectSet = objspecs
filterspec.propSet = [propspec]

# Create the filter
return pc.CreateFilter(filterspec, True)


def CheckForQuestionPending(task):
"""
Check to see if VM needs to ask a question, throw exception
"""
def CheckForQuestionPending(info):
""" Check to see if VM needs to ask a question, throw exception """

vm = task.info.entity
vm = info.entity
if vm is not None and isinstance(vm, vim.VirtualMachine):
qst = vm.runtime.question
if qst is not None:
raise TaskBlocked("Task blocked, User Intervention required")


def _LogMsg(log, message):
"""Helper logging a message with logger or print depending on the flag."""
if log:
log(message)
else:
print(message)


##
## @brief Class that keeps track of task percentage complete and calls
## a provided callback when it changes.
Expand All @@ -287,10 +317,10 @@ class ProgressUpdater(object):
Class that keeps track of task percentage complete and calls a
provided callback when it changes.
"""

def __init__(self, task, onProgressUpdate):
def __init__(self, task, onProgressUpdate, getAllUpdates=False):
self.task = task
self.onProgressUpdate = onProgressUpdate
self.getAllUpdates = getAllUpdates
self.prevProgress = 0
self.progress = 0

Expand All @@ -305,7 +335,7 @@ def Update(self, state):
def UpdateIfNeeded(self):
self.progress = self.task.info.progress

if self.progress != self.prevProgress:
if self.getAllUpdates or self.progress != self.prevProgress:
self.Update(self.progress)

self.prevProgress = self.progress
58 changes: 23 additions & 35 deletions pyVmomi/Cache.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,30 @@
# VMware vSphere Python SDK
# Copyright (c) 2008-2015 VMware, Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Copyright (c) 2008-2022 VMware, Inc.
This module implements the cache decorator
"""
__author__ = "VMware, Inc."
__author__ = "VMware, Inc"


def Cache(fn):
""" Function cache decorator """
def fnCache(*args, **kwargs):
""" Cache function """
key = (args and tuple(args) or None,
kwargs and frozenset(kwargs.items()) or None)
if key not in fn.__cached__:
fn.__cached__[key] = cache = fn(*args, **kwargs)
else:
cache = fn.__cached__[key]
return cache
""" Function cache decorator """
def fnCache(*args, **kwargs):
""" Cache function """
key = (args and tuple(args)
or None, kwargs and frozenset(list(kwargs.items())) or None)
if key not in fn.__cached__:
fn.__cached__[key] = cache = fn(*args, **kwargs)
else:
cache = fn.__cached__[key]
return cache

def ResetCache():
""" Reset cache """
fn.__cached__ = {}
def ResetCache():
""" Reset cache """
fn.__cached__ = {}

setattr(fn, "__cached__", {})
setattr(fn, "__resetcache__", ResetCache)
fnCache.__name__ = fn.__name__
fnCache.__doc__ = fn.__doc__
fnCache.__dict__.update(fn.__dict__)
return fnCache
setattr(fn, "__cached__", {})
setattr(fn, "__resetcache__", ResetCache)
fnCache.__name__ = fn.__name__
fnCache.__doc__ = fn.__doc__
fnCache.__dict__.update(fn.__dict__)
return fnCache
Loading

2 comments on commit a90023f

@ptrcnull
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connect.ConnectNoSSL() and connect.SmartConnectNoSSL() are removed.

hey, have you considered removing them from the tests too? :/

@DanielDraganov
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

connect.ConnectNoSSL() and connect.SmartConnectNoSSL() are removed.

hey, have you considered removing them from the tests too? :/

Yes. We are currently reviewing stale issues and pull requests in both pyVmomi and the community samples projects and tests will be handled after that.

Please sign in to comment.