Merge pull request #308 from tianhao64/master
More 6.0 related changes and bug fixes.
This commit is contained in:
commit
ef29bcf442
@ -1,13 +1,11 @@
|
||||
language: python
|
||||
python:
|
||||
- "2.6"
|
||||
- "2.7"
|
||||
- "pypy"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
|
||||
before_install:
|
||||
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi
|
||||
- pip install -r requirements.txt
|
||||
- pip install -r test-requirements.txt
|
||||
|
||||
|
@ -34,6 +34,7 @@ import requests
|
||||
from requests.auth import HTTPBasicAuth
|
||||
|
||||
from pyVmomi import vim, vmodl, SoapStubAdapter, SessionOrientedStub
|
||||
from pyVmomi.SoapAdapter import CONNECTION_POOL_IDLE_TIMEOUT_SEC
|
||||
from pyVmomi.VmomiSupport import nsMap, versionIdMap, versionMap, IsChildVersion
|
||||
from pyVmomi.VmomiSupport import GetServiceVersions
|
||||
|
||||
@ -177,7 +178,7 @@ class VimSessionOrientedStub(SessionOrientedStub):
|
||||
|
||||
def Connect(host='localhost', port=443, user='root', pwd='',
|
||||
service="hostd", adapter="SOAP", namespace=None, path="/sdk",
|
||||
version=None, keyFile=None, certFile=None,
|
||||
version=None, keyFile=None, certFile=None, thumbprint=None,
|
||||
sslContext=None):
|
||||
"""
|
||||
Connect to the specified server, login and return the service
|
||||
@ -212,6 +213,8 @@ def Connect(host='localhost', port=443, user='root', pwd='',
|
||||
@type keyFile: string
|
||||
@param certFile: ssl cert file path
|
||||
@type certFile: string
|
||||
@param thumbprint: host cert thumbprint
|
||||
@type thumbprint: string
|
||||
@param sslContext: SSL Context describing the various SSL options. It is only
|
||||
supported in Python 2.7.9 or higher.
|
||||
@type sslContext: SSL.Context
|
||||
@ -233,7 +236,7 @@ def Connect(host='localhost', port=443, user='root', pwd='',
|
||||
elif not version:
|
||||
version="vim.version.version6"
|
||||
si, stub = __Login(host, port, user, pwd, service, adapter, version, path,
|
||||
keyFile, certFile, sslContext)
|
||||
keyFile, certFile, thumbprint, sslContext)
|
||||
SetSi(si)
|
||||
|
||||
return si
|
||||
@ -268,7 +271,7 @@ def GetLocalTicket(si, user):
|
||||
## connected service instance object.
|
||||
|
||||
def __Login(host, port, user, pwd, service, adapter, version, path,
|
||||
keyFile, certFile, sslContext):
|
||||
keyFile, certFile, thumbprint, sslContext):
|
||||
"""
|
||||
Private method that performs the actual Connect and returns a
|
||||
connected service instance object.
|
||||
@ -293,6 +296,8 @@ def __Login(host, port, user, pwd, service, adapter, version, path,
|
||||
@type keyFile: string
|
||||
@param certFile: ssl cert file path
|
||||
@type certFile: string
|
||||
@param thumbprint: host cert thumbprint
|
||||
@type thumbprint: string
|
||||
@param sslContext: SSL Context describing the various SSL options. It is only
|
||||
supported in Python 2.7.9 or higher.
|
||||
@type sslContext: SSL.Context
|
||||
@ -304,7 +309,8 @@ def __Login(host, port, user, pwd, service, adapter, version, path,
|
||||
|
||||
# Create the SOAP stub adapter
|
||||
stub = SoapStubAdapter(host, port, version=version, path=path,
|
||||
certKeyFile=keyFile, certFile=certFile, sslContext=sslContext)
|
||||
certKeyFile=keyFile, certFile=certFile,
|
||||
thumbprint=thumbprint, sslContext=sslContext)
|
||||
|
||||
# Get Service instance
|
||||
si = vim.ServiceInstance("ServiceInstance", stub)
|
||||
@ -555,10 +561,54 @@ def __FindSupportedVersion(protocol, server, port, path, preferredApiVersions, s
|
||||
return desiredVersion
|
||||
return None
|
||||
|
||||
def SmartStubAdapter(host='localhost', port=443, path='/sdk',
|
||||
url=None, sock=None, poolSize=5,
|
||||
certFile=None, certKeyFile=None,
|
||||
httpProxyHost=None, httpProxyPort=80, sslProxyPath=None,
|
||||
thumbprint=None, cacertsFile=None, preferredApiVersions=None,
|
||||
acceptCompressedResponses=True,
|
||||
connectionPoolTimeout=CONNECTION_POOL_IDLE_TIMEOUT_SEC,
|
||||
samlToken=None, sslContext=None):
|
||||
"""
|
||||
Determine the most preferred API version supported by the specified server,
|
||||
then create a soap stub adapter using that version
|
||||
|
||||
The parameters are the same as for pyVmomi.SoapStubAdapter except for
|
||||
version which is renamed to prefferedApiVersions
|
||||
|
||||
@param preferredApiVersions: Acceptable API version(s) (e.g. vim.version.version3)
|
||||
If a list of versions is specified the versions should
|
||||
be ordered from most to least preferred. If None is
|
||||
specified, the list of versions support by pyVmomi will
|
||||
be used.
|
||||
@type preferredApiVersions: string or string list
|
||||
"""
|
||||
if preferredApiVersions is None:
|
||||
preferredApiVersions = GetServiceVersions('vim25')
|
||||
|
||||
supportedVersion = __FindSupportedVersion('https' if port > 0 else 'http',
|
||||
host,
|
||||
port,
|
||||
path,
|
||||
preferredApiVersions,
|
||||
sslContext)
|
||||
if supportedVersion is None:
|
||||
raise Exception("%s:%s is not a VIM server" % (host, port))
|
||||
|
||||
return SoapStubAdapter(host=host, port=port, path=path,
|
||||
url=url, sock=sock, poolSize=poolSize,
|
||||
certFile=certFile, certKeyFile=certKeyFile,
|
||||
httpProxyHost=httpProxyHost, httpProxyPort=httpProxyPort,
|
||||
sslProxyPath=sslProxyPath, thumbprint=thumbprint,
|
||||
cacertsFile=cacertsFile, version=supportedVersion,
|
||||
acceptCompressedResponses=acceptCompressedResponses,
|
||||
connectionPoolTimeout=connectionPoolTimeout,
|
||||
samlToken=samlToken, sslContext=sslContext)
|
||||
|
||||
def SmartConnect(protocol='https', host='localhost', port=443, user='root', pwd='',
|
||||
service="hostd", path="/sdk",
|
||||
preferredApiVersions=None, sslContext=None):
|
||||
preferredApiVersions=None,
|
||||
keyFile=None, certFile=None, thumbprint=None, sslContext=None):
|
||||
"""
|
||||
Determine the most preferred API version supported by the specified server,
|
||||
then connect to the specified server using that API version, login and return
|
||||
@ -591,6 +641,12 @@ def SmartConnect(protocol='https', host='localhost', port=443, user='root', pwd=
|
||||
specified, the list of versions support by pyVmomi will
|
||||
be used.
|
||||
@type preferredApiVersions: string or string list
|
||||
@param keyFile: ssl key file path
|
||||
@type keyFile: string
|
||||
@param certFile: ssl cert file path
|
||||
@type certFile: string
|
||||
@param thumbprint: host cert thumbprint
|
||||
@type thumbprint: string
|
||||
@param sslContext: SSL Context describing the various SSL options. It is only
|
||||
supported in Python 2.7.9 or higher.
|
||||
@type sslContext: SSL.Context
|
||||
@ -618,6 +674,9 @@ def SmartConnect(protocol='https', host='localhost', port=443, user='root', pwd=
|
||||
adapter='SOAP',
|
||||
version=supportedVersion,
|
||||
path=path,
|
||||
keyFile=keyFile,
|
||||
certFile=certFile,
|
||||
thumbprint=thumbprint,
|
||||
sslContext=sslContext)
|
||||
|
||||
def OpenUrlWithBasicAuth(url, user='root', pwd=''):
|
||||
|
@ -104,6 +104,17 @@ def encode(string, encoding):
|
||||
return string.encode(encoding)
|
||||
return u(string)
|
||||
|
||||
## Thumbprint mismatch exception
|
||||
#
|
||||
class ThumbprintMismatchException(Exception):
|
||||
def __init__(self, expected, actual):
|
||||
Exception.__init__(self, "Server has wrong SHA1 thumbprint: %s "
|
||||
"(required) != %s (server)" % (
|
||||
expected, actual))
|
||||
|
||||
self.expected = expected
|
||||
self.actual = actual
|
||||
|
||||
## Escape <, >, &
|
||||
def XmlEscape(xmlStr):
|
||||
escaped = xmlStr.replace("&", "&").replace(">", ">").replace("<", "<")
|
||||
@ -236,7 +247,7 @@ class SoapSerializer:
|
||||
# @param info the field
|
||||
def SerializeFaultDetail(self, val, info):
|
||||
""" Serialize an object """
|
||||
self._SerializeDataObject(val, info, '', self.defaultNS)
|
||||
self._SerializeDataObject(val, info, ' xsi:typ="{1}"'.format(val._wsdlName), self.defaultNS)
|
||||
|
||||
def _NSPrefix(self, ns):
|
||||
""" Get xml ns prefix. self.nsMap must be set """
|
||||
@ -494,12 +505,7 @@ class ExpatDeserializerNSHandlers:
|
||||
|
||||
## Get current default ns
|
||||
def GetCurrDefNS(self):
|
||||
namespaces = self.nsMap.get(None)
|
||||
if namespaces:
|
||||
ns = namespaces[-1]
|
||||
else:
|
||||
ns = ""
|
||||
return ns
|
||||
return self._GetNamespaceFromPrefix()
|
||||
|
||||
## Get namespace and wsdl name from tag
|
||||
def GetNSAndWsdlname(self, tag):
|
||||
@ -510,9 +516,17 @@ class ExpatDeserializerNSHandlers:
|
||||
else:
|
||||
prefix, name = None, tag
|
||||
# Map prefix to ns
|
||||
ns = self.nsMap[prefix][-1]
|
||||
ns = self._GetNamespaceFromPrefix(prefix)
|
||||
return ns, name
|
||||
|
||||
def _GetNamespaceFromPrefix(self, prefix = None):
|
||||
namespaces = self.nsMap.get(prefix)
|
||||
if namespaces:
|
||||
ns = namespaces[-1]
|
||||
else:
|
||||
ns = ""
|
||||
return ns
|
||||
|
||||
## Handle namespace begin
|
||||
def StartNamespaceDeclHandler(self, prefix, uri):
|
||||
namespaces = self.nsMap.get(prefix)
|
||||
@ -930,9 +944,7 @@ try:
|
||||
sha1.update(derCert)
|
||||
sha1Digest = sha1.hexdigest().lower()
|
||||
if sha1Digest != thumbprint:
|
||||
raise Exception("Server has wrong SHA1 thumbprint: {0} "
|
||||
"(required) != {1} (server)".format(
|
||||
thumbprint, sha1Digest))
|
||||
raise ThumbprintMismatchException(thumbprint, sha1Digest)
|
||||
|
||||
# Function used to wrap sockets with SSL
|
||||
_SocketWrapper = ssl.wrap_socket
|
||||
@ -963,7 +975,7 @@ except ImportError:
|
||||
class HTTPSConnectionWrapper(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
wrapped = http_client.HTTPSConnection(*args, **kwargs)
|
||||
# Extract ssl.wrap_socket param unknown to httplib.HTTPConnection,
|
||||
# Extract ssl.wrap_socket param unknown to httplib.HTTPSConnection,
|
||||
# and push back the params in connect()
|
||||
self._sslArgs = {}
|
||||
tmpKwargs = kwargs.copy()
|
||||
@ -1028,11 +1040,13 @@ class SSLTunnelConnection(object):
|
||||
# @param kwargs In case caller passed in extra parameters not handled by
|
||||
# SSLTunnelConnection
|
||||
def __call__(self, path, key_file=None, cert_file=None, **kwargs):
|
||||
# Don't pass any keyword args that HTTPConnection won't understand.
|
||||
for arg in kwargs.keys():
|
||||
if arg not in ("port", "strict", "timeout", "source_address"):
|
||||
del kwargs[arg]
|
||||
tunnel = http_client.HTTPConnection(path, **kwargs)
|
||||
# Only pass in the named arguments that HTTPConnection constructor
|
||||
# understands
|
||||
tmpKwargs = {}
|
||||
for key in http_client.HTTPConnection.__init__.__code__.co_varnames:
|
||||
if key in kwargs and key != 'self':
|
||||
tmpKwargs[key] = kwargs[key]
|
||||
tunnel = http_client.HTTPConnection(path, **tmpKwargs)
|
||||
tunnel.request('CONNECT', self.proxyPath)
|
||||
resp = tunnel.getresponse()
|
||||
if resp.status != 200:
|
||||
@ -1164,7 +1178,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
|
||||
# the UnixSocketConnection ctor expects to find it -- see above
|
||||
self.host = sock
|
||||
elif url:
|
||||
scheme, self.host, urlpath = urlparse.urlparse(url)[:3]
|
||||
scheme, self.host, urlpath = urlparse(url)[:3]
|
||||
# Only use the URL path if it's sensible, otherwise use the path
|
||||
# keyword argument as passed in.
|
||||
if urlpath not in ('', '/'):
|
||||
@ -1219,6 +1233,20 @@ class SoapStubAdapter(SoapStubAdapterBase):
|
||||
self.requestModifierList = []
|
||||
self._acceptCompressedResponses = acceptCompressedResponses
|
||||
|
||||
# Force a socket shutdown. Before python 2.7, ssl will fail to close
|
||||
# the socket (http://bugs.python.org/issue10127).
|
||||
# Not making this a part of the actual _HTTPSConnection since the internals
|
||||
# of the httplib.HTTP*Connection seem to pass around the descriptors and
|
||||
# depend on the behavior that close() still leaves the socket semi-functional.
|
||||
if sys.version_info[:2] < (2,7):
|
||||
def _CloseConnection(self, conn):
|
||||
# import pdb; pdb.set_trace()
|
||||
if self.scheme == HTTPSConnectionWrapper and conn.sock:
|
||||
conn.sock.shutdown(socket.SHUT_RDWR)
|
||||
conn.close()
|
||||
else:
|
||||
def _CloseConnection(self, conn):
|
||||
conn.close()
|
||||
|
||||
# Context modifier used to modify the SOAP request.
|
||||
# @param func The func that takes in the serialized message and modifies the
|
||||
@ -1287,7 +1315,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
|
||||
deserializer = SoapResponseDeserializer(outerStub)
|
||||
obj = deserializer.Deserialize(fd, info.result)
|
||||
except Exception as exc:
|
||||
conn.close()
|
||||
self._CloseConnection(conn)
|
||||
# NOTE (hartsock): This feels out of place. As a rule the lexical
|
||||
# context that opens a connection should also close it. However,
|
||||
# in this code the connection is passed around and closed in other
|
||||
@ -1306,7 +1334,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
|
||||
else:
|
||||
raise obj # pylint: disable-msg=E0702
|
||||
else:
|
||||
conn.close()
|
||||
self._CloseConnection(conn)
|
||||
raise http_client.HTTPException("{0} {1}".format(resp.status, resp.reason))
|
||||
|
||||
## Clean up connection pool to throw away idle timed-out connections
|
||||
@ -1324,7 +1352,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
|
||||
break
|
||||
|
||||
for conn, _ in idleConnections:
|
||||
conn.close()
|
||||
self._CloseConnection(conn)
|
||||
|
||||
## Get a HTTP connection from the pool
|
||||
def GetConnection(self):
|
||||
@ -1365,7 +1393,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
|
||||
self.pool = []
|
||||
self.lock.release()
|
||||
for conn, _ in oldConnections:
|
||||
conn.close()
|
||||
self._CloseConnection(conn)
|
||||
|
||||
## Return a HTTP connection to the pool
|
||||
def ReturnConnection(self, conn):
|
||||
@ -1380,7 +1408,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
|
||||
# NOTE (hartsock): this seems to violate good coding practice in that
|
||||
# the lexical context that opens a connection should also be the
|
||||
# same context responsible for closing it.
|
||||
conn.close()
|
||||
self._CloseConnection(conn)
|
||||
|
||||
## Disable nagle on a http connections
|
||||
def DisableNagle(self, conn):
|
||||
|
@ -465,6 +465,9 @@ class ManagedObject(object):
|
||||
self.__class__ == other.__class__ and \
|
||||
self._serverGuid == other._serverGuid
|
||||
|
||||
def __ne__(self, other):
|
||||
return not(self == other)
|
||||
|
||||
def __hash__(self):
|
||||
return str(self).__hash__()
|
||||
|
||||
@ -1248,11 +1251,15 @@ class _BuildVersions:
|
||||
self._nsMap = {}
|
||||
|
||||
def Add(self, version):
|
||||
vmodlNs = version.split(".",1)[0]
|
||||
if not (vmodlNs in self._verMap):
|
||||
self._verMap[vmodlNs] = version
|
||||
if not (vmodlNs in self._nsMap):
|
||||
self._nsMap[vmodlNs] = GetVersionNamespace(version)
|
||||
assert '.version.' in version, 'Invalid version %s' % version
|
||||
|
||||
vmodlNs = version.split(".version.", 1)[0].split(".")
|
||||
for idx in [1, len(vmodlNs)]:
|
||||
subVmodlNs = ".".join(vmodlNs[:idx])
|
||||
if not (subVmodlNs in self._verMap):
|
||||
self._verMap[subVmodlNs] = version
|
||||
if not (subVmodlNs in self._nsMap):
|
||||
self._nsMap[subVmodlNs] = GetVersionNamespace(version)
|
||||
|
||||
def Get(self, vmodlNs):
|
||||
return self._verMap[vmodlNs]
|
||||
|
@ -198,7 +198,7 @@ except ImportError:
|
||||
pyVmomi.VmomiSupport.GetVmodlType("vmodl.DynamicData")
|
||||
|
||||
from pyVmomi.SoapAdapter import SoapStubAdapter, StubAdapterBase, SoapCmdStubAdapter, \
|
||||
SessionOrientedStub
|
||||
SessionOrientedStub, ThumbprintMismatchException
|
||||
|
||||
types = pyVmomi.VmomiSupport.types
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user