initial fixture based testing setup

* new dependencies files
* makes testing possible
* defines fixture storage paths
* creates initial fixture for testing
* defines test requirements
* fixes connection fault so that it raises a traceback for diagnostics

partial https://github.com/vmware/pyvmomi/issues/42
blocks https://github.com/vmware/pyvmomi/issues/55
This commit is contained in:
Shawn Hartsock 2014-07-21 18:58:16 -04:00
parent 66871e5f19
commit cd533499cb
10 changed files with 471 additions and 40 deletions

View File

@ -6,10 +6,10 @@ python:
before_install: before_install:
- if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install unittest2; fi
- pip install -r requirements.txt
- pip install -r test-requirements.txt
install: install:
- python setup.py bdist_egg - python setup.py -q install
- pip install -e file://$TRAVIS_BUILD_DIR
script: script: python setup.py test
nosetests

View File

@ -23,7 +23,7 @@ Connect to a VMOMI ServiceInstance.
Detailed description (for [e]pydoc goes here). Detailed description (for [e]pydoc goes here).
""" """
from six import reraise
import sys import sys
import threading import threading
import thread import thread
@ -313,7 +313,13 @@ def __Login(host, port, user, pwd, service, adapter, version, path,
except vmodl.MethodFault: except vmodl.MethodFault:
raise raise
except Exception, e: except Exception, e:
raise vim.fault.HostConnectFault(msg=str(e)) # NOTE (hartsock): preserve the traceback for diagnostics
# pulling and preserving the traceback makes diagnosing connection
# failures easier since the fault will also include where inside the
# library the fault occurred. Without the traceback we have no idea
# why the connection failed beyond the message string.
(type, value, traceback) = sys.exc_info()
reraise(vim.fault.HostConnectFault(msg=str(e)), None, traceback)
# Get a ticket if we're connecting to localhost and password is not specified # Get a ticket if we're connecting to localhost and password is not specified
if host == 'localhost' and not pwd: if host == 'localhost' and not pwd:

View File

@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import httplib from six.moves import http_client
import sys import sys
import os import os
import time import time
@ -854,7 +854,9 @@ class SoapStubAdapterBase(StubAdapterBase):
## Subclass of HTTPConnection that connects over a Unix domain socket ## Subclass of HTTPConnection that connects over a Unix domain socket
## instead of a TCP port. The path of the socket is passed in place of ## instead of a TCP port. The path of the socket is passed in place of
## the hostname. Fairly gross but does the job. ## the hostname. Fairly gross but does the job.
class UnixSocketConnection(httplib.HTTPConnection): # NOTE (hartsock): rewrite this class as a wrapper, see HTTPSConnectionWrapper
# below for a guide.
class UnixSocketConnection(http_client.HTTPConnection):
# The HTTPConnection ctor expects a single argument, which it interprets # The HTTPConnection ctor expects a single argument, which it interprets
# as the host to connect to; for UnixSocketConnection, we instead interpret # as the host to connect to; for UnixSocketConnection, we instead interpret
# the parameter as the filesystem path of the Unix domain socket. # the parameter as the filesystem path of the Unix domain socket.
@ -862,7 +864,7 @@ class UnixSocketConnection(httplib.HTTPConnection):
# Pass '' as the host to HTTPConnection; it doesn't really matter # Pass '' as the host to HTTPConnection; it doesn't really matter
# what we pass (since we've overridden the connect method) as long # what we pass (since we've overridden the connect method) as long
# as it's a valid string. # as it's a valid string.
httplib.HTTPConnection.__init__(self, '') http_client.HTTPConnection.__init__(self, '')
self.path = path self.path = path
def connect(self): def connect(self):
@ -884,7 +886,7 @@ try:
'''If there is a thumbprint, connect to the server and verify that the '''If there is a thumbprint, connect to the server and verify that the
SSL certificate matches the given thumbprint. An exception is thrown SSL certificate matches the given thumbprint. An exception is thrown
if there is a mismatch.''' if there is a mismatch.'''
if thumbprint and isinstance(connection, httplib.HTTPSConnection): if thumbprint and isinstance(connection, http_client.HTTPSConnection):
if not connection.sock: if not connection.sock:
connection.connect() connection.connect()
derCert = connection.sock.getpeercert(True) derCert = connection.sock.getpeercert(True)
@ -903,21 +905,28 @@ except ImportError:
SSL_THUMBPRINTS_SUPPORTED = False SSL_THUMBPRINTS_SUPPORTED = False
def _VerifyThumbprint(thumbprint, connection): def _VerifyThumbprint(thumbprint, connection):
if thumbprint and isinstance(connection, httplib.HTTPSConnection): if thumbprint and isinstance(connection, http_client.HTTPSConnection):
raise Exception( raise Exception(
"Thumbprint verification not supported on python < 2.6") "Thumbprint verification not supported on python < 2.6")
def _SocketWrapper(rawSocket, keyfile, certfile, *args, **kwargs): def _SocketWrapper(rawSocket, keyfile, certfile, *args, **kwargs):
wrappedSocket = socket.ssl(rawSocket, keyfile, certfile) wrappedSocket = socket.ssl(rawSocket, keyfile, certfile)
return httplib.FakeSocket(rawSocket, wrappedSocket) return http_client.FakeSocket(rawSocket, wrappedSocket)
## Internal version of https connection ## https connection wrapper
# #
# NOTE (hartsock): do not override core library types or implementations
# directly because this makes brittle code that is too easy to break and
# closely tied to implementation details we do not control. Instead, wrap
# the core object to introduce additional behaviors.
#
# Purpose:
# Support ssl.wrap_socket params which are missing from httplib # Support ssl.wrap_socket params which are missing from httplib
# HTTPSConnection (e.g. ca_certs) # HTTPSConnection (e.g. ca_certs)
# Note: Only works iff the ssl params are passing in as kwargs # Note: Only works iff the ssl params are passing in as kwargs
class _HTTPSConnection(httplib.HTTPSConnection): class HTTPSConnectionWrapper(object):
def __init__(self, *args, **kwargs): 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.HTTPConnection,
# and push back the params in connect() # and push back the params in connect()
self._sslArgs = {} self._sslArgs = {}
@ -927,15 +936,14 @@ class _HTTPSConnection(httplib.HTTPSConnection):
"ciphers"]: "ciphers"]:
if key in tmpKwargs: if key in tmpKwargs:
self._sslArgs[key] = tmpKwargs.pop(key) self._sslArgs[key] = tmpKwargs.pop(key)
httplib.HTTPSConnection.__init__(self, *args, **tmpKwargs) self._wrapped = wrapped
## Override connect to allow us to pass in additional ssl paramters to ## Override connect to allow us to pass in additional ssl paramters to
# ssl.wrap_socket (e.g. cert_reqs, ca_certs for ca cert verification) # ssl.wrap_socket (e.g. cert_reqs, ca_certs for ca cert verification)
def connect(self): def connect(self, wrapped):
if len(self._sslArgs) == 0: if len(self._sslArgs) == 0 or hasattr(self, '_baseclass'):
# No override # No override
httplib.HTTPSConnection.connect(self) return wrapped.connect
return
# Big hack. We have to copy and paste the httplib connect fn for # Big hack. We have to copy and paste the httplib connect fn for
# each python version in order to handle extra ssl paramters. Yuk! # each python version in order to handle extra ssl paramters. Yuk!
@ -943,18 +951,16 @@ class _HTTPSConnection(httplib.HTTPSConnection):
# Python 2.7 # Python 2.7
sock = socket.create_connection((self.host, self.port), sock = socket.create_connection((self.host, self.port),
self.timeout, self.source_address) self.timeout, self.source_address)
if self._tunnel_host: if wrapped._tunnel_host:
self.sock = sock wrapped.sock = sock
self._tunnel() wrapped._tunnel()
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, **self._sslArgs) wrapped.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, **self._sslArgs)
elif hasattr(self, "timeout"): elif hasattr(self, "timeout"):
# Python 2.6 # Python 2.6
sock = socket.create_connection((self.host, self.port), self.timeout) sock = socket.create_connection((self.host, self.port), self.timeout)
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, **self._sslArgs) wrapped.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, **self._sslArgs)
else:
# Unknown python version. Do nothing return wrapped.connect
httplib.HTTPSConnection.connect(self)
return
# TODO: Additional verification of peer cert if needed # TODO: Additional verification of peer cert if needed
#cert_reqs = self._sslArgs.get("cert_reqs", ssl.CERT_NONE) #cert_reqs = self._sslArgs.get("cert_reqs", ssl.CERT_NONE)
@ -965,6 +971,11 @@ class _HTTPSConnection(httplib.HTTPSConnection):
# dercert = self.sock.getpeercert(False) # dercert = self.sock.getpeercert(False)
# # pemcert = ssl.DER_cert_to_PEM_cert(dercert) # # pemcert = ssl.DER_cert_to_PEM_cert(dercert)
def __getattr__(self, item):
if item == 'connect':
return self.connect(self._wrapped)
return getattr(self._wrapped, item)
## Stand-in for the HTTPSConnection class that will connect to a proxy and ## Stand-in for the HTTPSConnection class that will connect to a proxy and
## issue a CONNECT command to start an SSL tunnel. ## issue a CONNECT command to start an SSL tunnel.
class SSLTunnelConnection(object): class SSLTunnelConnection(object):
@ -985,13 +996,13 @@ class SSLTunnelConnection(object):
for arg in kwargs.keys(): for arg in kwargs.keys():
if arg not in ("port", "strict", "timeout", "source_address"): if arg not in ("port", "strict", "timeout", "source_address"):
del kwargs[arg] del kwargs[arg]
tunnel = httplib.HTTPConnection(path, **kwargs) tunnel = http_client.HTTPConnection(path, **kwargs)
tunnel.request('CONNECT', self.proxyPath) tunnel.request('CONNECT', self.proxyPath)
resp = tunnel.getresponse() resp = tunnel.getresponse()
tunnelSocket = resp.fp tunnelSocket = resp.fp
if resp.status != 200: if resp.status != 200:
raise httplib.HTTPException("%d %s" % (resp.status, resp.reason)) raise http_client.HTTPException("%d %s" % (resp.status, resp.reason))
retval = httplib.HTTPSConnection(path) retval = http_client.HTTPSConnection(path)
retval.sock = _SocketWrapper(tunnelSocket, retval.sock = _SocketWrapper(tunnelSocket,
keyfile=key_file, certfile=cert_file) keyfile=key_file, certfile=cert_file)
return retval return retval
@ -1121,11 +1132,11 @@ class SoapStubAdapter(SoapStubAdapterBase):
# keyword argument as passed in. # keyword argument as passed in.
if urlpath not in ('', '/'): if urlpath not in ('', '/'):
path = urlpath path = urlpath
self.scheme = scheme == "http" and httplib.HTTPConnection \ self.scheme = scheme == "http" and http_client.HTTPConnection \
or scheme == "https" and _HTTPSConnection or scheme == "https" and HTTPSConnectionWrapper
else: else:
port, self.scheme = port < 0 and (-port, httplib.HTTPConnection) \ port, self.scheme = port < 0 and (-port, http_client.HTTPConnection) \
or (port, _HTTPSConnection) or (port, HTTPSConnectionWrapper)
if host.find(':') != -1: # is IPv6? if host.find(':') != -1: # is IPv6?
host = '[' + host + ']' host = '[' + host + ']'
self.host = '%s:%d' % (host, port) self.host = '%s:%d' % (host, port)
@ -1141,7 +1152,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
if sslProxyPath: if sslProxyPath:
self.scheme = SSLTunnelConnection(sslProxyPath) self.scheme = SSLTunnelConnection(sslProxyPath)
elif httpProxyHost: elif httpProxyHost:
if self.scheme == _HTTPSConnection: if self.scheme == HTTPSConnectionWrapper:
self.scheme = SSLTunnelConnection(self.host) self.scheme = SSLTunnelConnection(self.host)
else: else:
if url: if url:
@ -1206,7 +1217,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
try: try:
conn.request('POST', self.path, req, headers) conn.request('POST', self.path, req, headers)
resp = conn.getresponse() resp = conn.getresponse()
except (socket.error, httplib.HTTPException): except (socket.error, http_client.HTTPException):
# The server is probably sick, drop all of the cached connections. # The server is probably sick, drop all of the cached connections.
self.DropConnections() self.DropConnections()
raise raise
@ -1240,7 +1251,7 @@ class SoapStubAdapter(SoapStubAdapterBase):
raise obj # pylint: disable-msg=E0702 raise obj # pylint: disable-msg=E0702
else: else:
conn.close() conn.close()
raise httplib.HTTPException("%d %s" % (resp.status, resp.reason)) raise http_client.HTTPException("%d %s" % (resp.status, resp.reason))
## Clean up connection pool to throw away idle timed-out connections ## Clean up connection pool to throw away idle timed-out connections
# SoapStubAdapter lock must be acquired before this method is called. # SoapStubAdapter lock must be acquired before this method is called.
@ -1461,7 +1472,7 @@ class SessionOrientedStub(StubAdapterBase):
self._CallLoginMethod() self._CallLoginMethod()
# Invoke the method # Invoke the method
status, obj = self.soapStub.InvokeMethod(mo, info, args, self) status, obj = self.soapStub.InvokeMethod(mo, info, args, self)
except (socket.error, httplib.HTTPException, ExpatError): except (socket.error, http_client.HTTPException, ExpatError):
if self.retryDelay and retriesLeft: if self.retryDelay and retriesLeft:
time.sleep(self.retryDelay) time.sleep(self.retryDelay)
retriesLeft -= 1 retriesLeft -= 1
@ -1497,7 +1508,7 @@ class SessionOrientedStub(StubAdapterBase):
self._CallLoginMethod() self._CallLoginMethod()
# Invoke the method # Invoke the method
obj = StubAdapterBase.InvokeAccessor(self, mo, info) obj = StubAdapterBase.InvokeAccessor(self, mo, info)
except (socket.error, httplib.HTTPException, ExpatError): except (socket.error, http_client.HTTPException, ExpatError):
if self.retryDelay and retriesLeft: if self.retryDelay and retriesLeft:
time.sleep(self.retryDelay) time.sleep(self.retryDelay)
retriesLeft -= 1 retriesLeft -= 1

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
requests>=2.3.0
six>=1.7.3

View File

@ -16,9 +16,16 @@
from setuptools import setup from setuptools import setup
import os import os
def read(fname): def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read() return open(os.path.join(os.path.dirname(__file__), fname)).read()
with open('requirements.txt') as f:
required = f.read().splitlines()
with open('test-requirements.txt') as f:
required_for_tests = f.read().splitlines()
setup( setup(
name='pyvmomi', name='pyvmomi',
version='5.5.0_2014.dev', version='5.5.0_2014.dev',
@ -27,6 +34,7 @@ setup(
author_email='jhu@vmware.com', author_email='jhu@vmware.com',
url='https://github.com/vmware/pyvmomi', url='https://github.com/vmware/pyvmomi',
packages=['pyVmomi', 'pyVim'], packages=['pyVmomi', 'pyVim'],
install_requires=required,
license='Apache', license='Apache',
long_description=read('README.md'), long_description=read('README.md'),
classifiers=[ classifiers=[
@ -38,5 +46,7 @@ setup(
"Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: System :: Distributed Computing" "Topic :: System :: Distributed Computing"
], ],
test_suite='tests',
tests_require= required_for_tests,
zip_safe=True zip_safe=True
) )

4
test-requirements.txt Normal file
View File

@ -0,0 +1,4 @@
mock
PyYAML>=3.11
testtools>=0.9.34
vcrpy>=1.0.2

23
tests/__init__.py Normal file
View File

@ -0,0 +1,23 @@
# VMware vSphere Python SDK
# Copyright (c) 2008-2014 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.
import os
def tests_resource_path(local_path=''):
this_file = os.path.dirname(os.path.abspath(__file__))
return os.path.join(this_file, local_path)
# Fully qualified path to the fixtures directory underneath this module
fixtures_path = tests_resource_path('fixtures')

234
tests/fixtures/basic_connection.yaml vendored Normal file
View File

@ -0,0 +1,234 @@
interactions:
- request:
body: '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body><RetrieveServiceContent xmlns="urn:vim25"><_this type="ServiceInstance">ServiceInstance</_this></RetrieveServiceContent></soapenv:Body>
</soapenv:Envelope>'
headers:
Accept-Encoding: ['gzip, deflate']
Content-Type: [text/xml; charset=UTF-8]
Cookie: ['']
SOAPAction: ['"urn:vim25/4.1"']
method: POST
uri: https://vcsa:443/sdk
response:
body: {string: !!python/unicode "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soapenv:Envelope
xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n<soapenv:Body>\n<RetrieveServiceContentResponse
xmlns=\"urn:vim25\"><returnval><rootFolder type=\"Folder\">group-d1</rootFolder><propertyCollector
type=\"PropertyCollector\">propertyCollector</propertyCollector><viewManager
type=\"ViewManager\">ViewManager</viewManager><about><name>VMware vCenter
Server</name><fullName>VMware vCenter Server 5.5.0 build-1623101 (Sim)</fullName><vendor>VMware,
Inc.</vendor><version>5.5.0</version><build>1623101 (Sim)</build><localeVersion>INTL</localeVersion><localeBuild>000</localeBuild><osType>linux-x64</osType><productLineId>vpx</productLineId><apiType>VirtualCenter</apiType><apiVersion>5.5</apiVersion><instanceUuid>E8636946-5510-44E7-B288-20DA0AD9DA38</instanceUuid><licenseProductName>VMware
VirtualCenter Server</licenseProductName><licenseProductVersion>5.0</licenseProductVersion></about><setting
type=\"OptionManager\">VpxSettings</setting><userDirectory type=\"UserDirectory\">UserDirectory</userDirectory><sessionManager
type=\"SessionManager\">SessionManager</sessionManager><authorizationManager
type=\"AuthorizationManager\">AuthorizationManager</authorizationManager><perfManager
type=\"PerformanceManager\">PerfMgr</perfManager><scheduledTaskManager type=\"ScheduledTaskManager\">ScheduledTaskManager</scheduledTaskManager><alarmManager
type=\"AlarmManager\">AlarmManager</alarmManager><eventManager type=\"EventManager\">EventManager</eventManager><taskManager
type=\"TaskManager\">TaskManager</taskManager><extensionManager type=\"ExtensionManager\">ExtensionManager</extensionManager><customizationSpecManager
type=\"CustomizationSpecManager\">CustomizationSpecManager</customizationSpecManager><customFieldsManager
type=\"CustomFieldsManager\">CustomFieldsManager</customFieldsManager><diagnosticManager
type=\"DiagnosticManager\">DiagMgr</diagnosticManager><licenseManager type=\"LicenseManager\">LicenseManager</licenseManager><searchIndex
type=\"SearchIndex\">SearchIndex</searchIndex><fileManager type=\"FileManager\">FileManager</fileManager><virtualDiskManager
type=\"VirtualDiskManager\">virtualDiskManager</virtualDiskManager><snmpSystem
type=\"HostSnmpSystem\">SnmpSystem</snmpSystem><vmProvisioningChecker type=\"VirtualMachineProvisioningChecker\">ProvChecker</vmProvisioningChecker><vmCompatibilityChecker
type=\"VirtualMachineCompatibilityChecker\">CompatChecker</vmCompatibilityChecker><ovfManager
type=\"OvfManager\">OvfManager</ovfManager><ipPoolManager type=\"IpPoolManager\">IpPoolManager</ipPoolManager><dvSwitchManager
type=\"DistributedVirtualSwitchManager\">DVSManager</dvSwitchManager><hostProfileManager
type=\"HostProfileManager\">HostProfileManager</hostProfileManager><clusterProfileManager
type=\"ClusterProfileManager\">ClusterProfileManager</clusterProfileManager><complianceManager
type=\"ProfileComplianceManager\">MoComplianceManager</complianceManager><localizationManager
type=\"LocalizationManager\">LocalizationManager</localizationManager><storageResourceManager
type=\"StorageResourceManager\">StorageResourceManager</storageResourceManager></returnval></RetrieveServiceContentResponse>\n</soapenv:Body>\n</soapenv:Envelope>"}
headers:
cache-control: [no-cache]
connection: [Keep-Alive]
content-length: ['3332']
content-type: [text/xml; charset=utf-8]
date: ['Mon, 21 Jul 2014 22:31:05 GMT']
set-cookie: [vmware_soap_session="52970dd3-2b0f-647b-22b3-44bda6d49983"; Path=/;
HttpOnly; Secure;]
status: {code: 200, message: OK}
- request:
body: '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body><Login xmlns="urn:vim25"><_this type="SessionManager">SessionManager</_this><userName>my_user</userName><password>my_password</password></Login></soapenv:Body>
</soapenv:Envelope>'
headers:
Accept-Encoding: ['gzip, deflate']
Content-Type: [text/xml; charset=UTF-8]
Cookie: [vmware_soap_session="52970dd3-2b0f-647b-22b3-44bda6d49983"; Path=/;
HttpOnly; Secure;]
SOAPAction: ['"urn:vim25/4.1"']
method: POST
uri: https://vcsa:443/sdk
response:
body: {string: !!python/unicode "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soapenv:Envelope
xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n<soapenv:Body>\n<LoginResponse
xmlns=\"urn:vim25\"><returnval><key>52773cd3-35c6-b40a-17f1-fe664a9f08f3</key><userName>my_user</userName><fullName>My User
</fullName><loginTime>2014-07-21T22:31:05.480973Z</loginTime><lastActiveTime>2014-07-21T22:31:05.480973Z</lastActiveTime><locale>en</locale><messageLocale>en</messageLocale></returnval></LoginResponse>\n</soapenv:Body>\n</soapenv:Envelope>"}
headers:
cache-control: [no-cache]
connection: [Keep-Alive]
content-length: ['659']
content-type: [text/xml; charset=utf-8]
date: ['Mon, 21 Jul 2014 22:31:05 GMT']
status: {code: 200, message: OK}
- request:
body: '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body><RetrieveServiceContent xmlns="urn:vim25"><_this type="ServiceInstance">ServiceInstance</_this></RetrieveServiceContent></soapenv:Body>
</soapenv:Envelope>'
headers:
Accept-Encoding: ['gzip, deflate']
Content-Type: [text/xml; charset=UTF-8]
Cookie: [vmware_soap_session="52970dd3-2b0f-647b-22b3-44bda6d49983"; Path=/;
HttpOnly; Secure;]
SOAPAction: ['"urn:vim25/4.1"']
method: POST
uri: https://vcsa:443/sdk
response:
body: {string: !!python/unicode "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soapenv:Envelope
xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n<soapenv:Body>\n<RetrieveServiceContentResponse
xmlns=\"urn:vim25\"><returnval><rootFolder type=\"Folder\">group-d1</rootFolder><propertyCollector
type=\"PropertyCollector\">propertyCollector</propertyCollector><viewManager
type=\"ViewManager\">ViewManager</viewManager><about><name>VMware vCenter
Server</name><fullName>VMware vCenter Server 5.5.0 build-1623101 (Sim)</fullName><vendor>VMware,
Inc.</vendor><version>5.5.0</version><build>1623101 (Sim)</build><localeVersion>INTL</localeVersion><localeBuild>000</localeBuild><osType>linux-x64</osType><productLineId>vpx</productLineId><apiType>VirtualCenter</apiType><apiVersion>5.5</apiVersion><instanceUuid>E8636946-5510-44E7-B288-20DA0AD9DA38</instanceUuid><licenseProductName>VMware
VirtualCenter Server</licenseProductName><licenseProductVersion>5.0</licenseProductVersion></about><setting
type=\"OptionManager\">VpxSettings</setting><userDirectory type=\"UserDirectory\">UserDirectory</userDirectory><sessionManager
type=\"SessionManager\">SessionManager</sessionManager><authorizationManager
type=\"AuthorizationManager\">AuthorizationManager</authorizationManager><perfManager
type=\"PerformanceManager\">PerfMgr</perfManager><scheduledTaskManager type=\"ScheduledTaskManager\">ScheduledTaskManager</scheduledTaskManager><alarmManager
type=\"AlarmManager\">AlarmManager</alarmManager><eventManager type=\"EventManager\">EventManager</eventManager><taskManager
type=\"TaskManager\">TaskManager</taskManager><extensionManager type=\"ExtensionManager\">ExtensionManager</extensionManager><customizationSpecManager
type=\"CustomizationSpecManager\">CustomizationSpecManager</customizationSpecManager><customFieldsManager
type=\"CustomFieldsManager\">CustomFieldsManager</customFieldsManager><diagnosticManager
type=\"DiagnosticManager\">DiagMgr</diagnosticManager><licenseManager type=\"LicenseManager\">LicenseManager</licenseManager><searchIndex
type=\"SearchIndex\">SearchIndex</searchIndex><fileManager type=\"FileManager\">FileManager</fileManager><virtualDiskManager
type=\"VirtualDiskManager\">virtualDiskManager</virtualDiskManager><snmpSystem
type=\"HostSnmpSystem\">SnmpSystem</snmpSystem><vmProvisioningChecker type=\"VirtualMachineProvisioningChecker\">ProvChecker</vmProvisioningChecker><vmCompatibilityChecker
type=\"VirtualMachineCompatibilityChecker\">CompatChecker</vmCompatibilityChecker><ovfManager
type=\"OvfManager\">OvfManager</ovfManager><ipPoolManager type=\"IpPoolManager\">IpPoolManager</ipPoolManager><dvSwitchManager
type=\"DistributedVirtualSwitchManager\">DVSManager</dvSwitchManager><hostProfileManager
type=\"HostProfileManager\">HostProfileManager</hostProfileManager><clusterProfileManager
type=\"ClusterProfileManager\">ClusterProfileManager</clusterProfileManager><complianceManager
type=\"ProfileComplianceManager\">MoComplianceManager</complianceManager><localizationManager
type=\"LocalizationManager\">LocalizationManager</localizationManager><storageResourceManager
type=\"StorageResourceManager\">StorageResourceManager</storageResourceManager></returnval></RetrieveServiceContentResponse>\n</soapenv:Body>\n</soapenv:Envelope>"}
headers:
cache-control: [no-cache]
connection: [Keep-Alive]
content-length: ['3332']
content-type: [text/xml; charset=utf-8]
date: ['Mon, 21 Jul 2014 22:31:05 GMT']
status: {code: 200, message: OK}
- request:
body: '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body><RetrievePropertiesEx xmlns="urn:vim25"><_this type="PropertyCollector">propertyCollector</_this><specSet><propSet><type>ServiceInstance</type><all>false</all><pathSet>content</pathSet></propSet><objectSet><obj
type="ServiceInstance">ServiceInstance</obj><skip>false</skip></objectSet></specSet><options><maxObjects>1</maxObjects></options></RetrievePropertiesEx></soapenv:Body>
</soapenv:Envelope>'
headers:
Accept-Encoding: ['gzip, deflate']
Content-Type: [text/xml; charset=UTF-8]
Cookie: [vmware_soap_session="52970dd3-2b0f-647b-22b3-44bda6d49983"; Path=/;
HttpOnly; Secure;]
SOAPAction: ['"urn:vim25/4.1"']
method: POST
uri: https://vcsa:443/sdk
response:
body: {string: !!python/unicode "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soapenv:Envelope
xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n<soapenv:Body>\n<RetrievePropertiesExResponse
xmlns=\"urn:vim25\"><returnval><objects><obj type=\"ServiceInstance\">ServiceInstance</obj><propSet><name>content</name><val
xsi:type=\"ServiceContent\"><rootFolder type=\"Folder\">group-d1</rootFolder><propertyCollector
type=\"PropertyCollector\">propertyCollector</propertyCollector><viewManager
type=\"ViewManager\">ViewManager</viewManager><about><name>VMware vCenter
Server</name><fullName>VMware vCenter Server 5.5.0 build-1623101 (Sim)</fullName><vendor>VMware,
Inc.</vendor><version>5.5.0</version><build>1623101 (Sim)</build><localeVersion>INTL</localeVersion><localeBuild>000</localeBuild><osType>linux-x64</osType><productLineId>vpx</productLineId><apiType>VirtualCenter</apiType><apiVersion>5.5</apiVersion><instanceUuid>E8636946-5510-44E7-B288-20DA0AD9DA38</instanceUuid><licenseProductName>VMware
VirtualCenter Server</licenseProductName><licenseProductVersion>5.0</licenseProductVersion></about><setting
type=\"OptionManager\">VpxSettings</setting><userDirectory type=\"UserDirectory\">UserDirectory</userDirectory><sessionManager
type=\"SessionManager\">SessionManager</sessionManager><authorizationManager
type=\"AuthorizationManager\">AuthorizationManager</authorizationManager><perfManager
type=\"PerformanceManager\">PerfMgr</perfManager><scheduledTaskManager type=\"ScheduledTaskManager\">ScheduledTaskManager</scheduledTaskManager><alarmManager
type=\"AlarmManager\">AlarmManager</alarmManager><eventManager type=\"EventManager\">EventManager</eventManager><taskManager
type=\"TaskManager\">TaskManager</taskManager><extensionManager type=\"ExtensionManager\">ExtensionManager</extensionManager><customizationSpecManager
type=\"CustomizationSpecManager\">CustomizationSpecManager</customizationSpecManager><customFieldsManager
type=\"CustomFieldsManager\">CustomFieldsManager</customFieldsManager><diagnosticManager
type=\"DiagnosticManager\">DiagMgr</diagnosticManager><licenseManager type=\"LicenseManager\">LicenseManager</licenseManager><searchIndex
type=\"SearchIndex\">SearchIndex</searchIndex><fileManager type=\"FileManager\">FileManager</fileManager><virtualDiskManager
type=\"VirtualDiskManager\">virtualDiskManager</virtualDiskManager><snmpSystem
type=\"HostSnmpSystem\">SnmpSystem</snmpSystem><vmProvisioningChecker type=\"VirtualMachineProvisioningChecker\">ProvChecker</vmProvisioningChecker><vmCompatibilityChecker
type=\"VirtualMachineCompatibilityChecker\">CompatChecker</vmCompatibilityChecker><ovfManager
type=\"OvfManager\">OvfManager</ovfManager><ipPoolManager type=\"IpPoolManager\">IpPoolManager</ipPoolManager><dvSwitchManager
type=\"DistributedVirtualSwitchManager\">DVSManager</dvSwitchManager><hostProfileManager
type=\"HostProfileManager\">HostProfileManager</hostProfileManager><clusterProfileManager
type=\"ClusterProfileManager\">ClusterProfileManager</clusterProfileManager><complianceManager
type=\"ProfileComplianceManager\">MoComplianceManager</complianceManager><localizationManager
type=\"LocalizationManager\">LocalizationManager</localizationManager><storageResourceManager
type=\"StorageResourceManager\">StorageResourceManager</storageResourceManager></val></propSet></objects></returnval></RetrievePropertiesExResponse>\n</soapenv:Body>\n</soapenv:Envelope>"}
headers:
cache-control: [no-cache]
connection: [Keep-Alive]
content-length: ['3472']
content-type: [text/xml; charset=utf-8]
date: ['Mon, 21 Jul 2014 22:31:05 GMT']
status: {code: 200, message: OK}
- request:
body: '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body><RetrievePropertiesEx xmlns="urn:vim25"><_this type="PropertyCollector">propertyCollector</_this><specSet><propSet><type>SessionManager</type><all>false</all><pathSet>currentSession</pathSet></propSet><objectSet><obj
type="SessionManager">SessionManager</obj><skip>false</skip></objectSet></specSet><options><maxObjects>1</maxObjects></options></RetrievePropertiesEx></soapenv:Body>
</soapenv:Envelope>'
headers:
Accept-Encoding: ['gzip, deflate']
Content-Type: [text/xml; charset=UTF-8]
Cookie: [vmware_soap_session="52970dd3-2b0f-647b-22b3-44bda6d49983"; Path=/;
HttpOnly; Secure;]
SOAPAction: ['"urn:vim25/4.1"']
method: POST
uri: https://vcsa:443/sdk
response:
body: {string: !!python/unicode "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soapenv:Envelope
xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n<soapenv:Body>\n<RetrievePropertiesExResponse
xmlns=\"urn:vim25\"><returnval><objects><obj type=\"SessionManager\">SessionManager</obj><propSet><name>currentSession</name><val
xsi:type=\"UserSession\"><key>52773cd3-35c6-b40a-17f1-fe664a9f08f3</key><userName>my_user</userName><fullName>My User
</fullName><loginTime>2014-07-21T22:31:05.480973Z</loginTime><lastActiveTime>2014-07-21T22:31:05.480973Z</lastActiveTime><locale>en</locale><messageLocale>en</messageLocale></val></propSet></objects></returnval></RetrievePropertiesExResponse>\n</soapenv:Body>\n</soapenv:Envelope>"}
headers:
cache-control: [no-cache]
connection: [Keep-Alive]
content-length: ['835']
content-type: [text/xml; charset=utf-8]
date: ['Mon, 21 Jul 2014 22:31:05 GMT']
status: {code: 200, message: OK}
version: 1

View File

@ -0,0 +1,89 @@
interactions:
- request:
body: '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body><RetrieveServiceContent xmlns="urn:vim25"><_this type="ServiceInstance">ServiceInstance</_this></RetrieveServiceContent></soapenv:Body>
</soapenv:Envelope>'
headers:
Accept-Encoding: ['gzip, deflate']
Content-Type: [text/xml; charset=UTF-8]
Cookie: ['']
SOAPAction: ['"urn:vim25/4.1"']
method: POST
uri: https://vcsa:443/sdk
response:
body: {string: !!python/unicode "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soapenv:Envelope
xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n<soapenv:Body>\n<RetrieveServiceContentResponse
xmlns=\"urn:vim25\"><returnval><rootFolder type=\"Folder\">group-d1</rootFolder><propertyCollector
type=\"PropertyCollector\">propertyCollector</propertyCollector><viewManager
type=\"ViewManager\">ViewManager</viewManager><about><name>VMware vCenter
Server</name><fullName>VMware vCenter Server 5.5.0 build-1750787 (Sim)</fullName><vendor>VMware,
Inc.</vendor><version>5.5.0</version><build>1750787 (Sim)</build><localeVersion>INTL</localeVersion><localeBuild>000</localeBuild><osType>linux-x64</osType><productLineId>vpx</productLineId><apiType>VirtualCenter</apiType><apiVersion>5.5</apiVersion><instanceUuid>EAB4D846-C243-426B-A021-0547644CE59D</instanceUuid><licenseProductName>VMware
VirtualCenter Server</licenseProductName><licenseProductVersion>5.0</licenseProductVersion></about><setting
type=\"OptionManager\">VpxSettings</setting><userDirectory type=\"UserDirectory\">UserDirectory</userDirectory><sessionManager
type=\"SessionManager\">SessionManager</sessionManager><authorizationManager
type=\"AuthorizationManager\">AuthorizationManager</authorizationManager><perfManager
type=\"PerformanceManager\">PerfMgr</perfManager><scheduledTaskManager type=\"ScheduledTaskManager\">ScheduledTaskManager</scheduledTaskManager><alarmManager
type=\"AlarmManager\">AlarmManager</alarmManager><eventManager type=\"EventManager\">EventManager</eventManager><taskManager
type=\"TaskManager\">TaskManager</taskManager><extensionManager type=\"ExtensionManager\">ExtensionManager</extensionManager><customizationSpecManager
type=\"CustomizationSpecManager\">CustomizationSpecManager</customizationSpecManager><customFieldsManager
type=\"CustomFieldsManager\">CustomFieldsManager</customFieldsManager><diagnosticManager
type=\"DiagnosticManager\">DiagMgr</diagnosticManager><licenseManager type=\"LicenseManager\">LicenseManager</licenseManager><searchIndex
type=\"SearchIndex\">SearchIndex</searchIndex><fileManager type=\"FileManager\">FileManager</fileManager><virtualDiskManager
type=\"VirtualDiskManager\">virtualDiskManager</virtualDiskManager><snmpSystem
type=\"HostSnmpSystem\">SnmpSystem</snmpSystem><vmProvisioningChecker type=\"VirtualMachineProvisioningChecker\">ProvChecker</vmProvisioningChecker><vmCompatibilityChecker
type=\"VirtualMachineCompatibilityChecker\">CompatChecker</vmCompatibilityChecker><ovfManager
type=\"OvfManager\">OvfManager</ovfManager><ipPoolManager type=\"IpPoolManager\">IpPoolManager</ipPoolManager><dvSwitchManager
type=\"DistributedVirtualSwitchManager\">DVSManager</dvSwitchManager><hostProfileManager
type=\"HostProfileManager\">HostProfileManager</hostProfileManager><clusterProfileManager
type=\"ClusterProfileManager\">ClusterProfileManager</clusterProfileManager><complianceManager
type=\"ProfileComplianceManager\">MoComplianceManager</complianceManager><localizationManager
type=\"LocalizationManager\">LocalizationManager</localizationManager><storageResourceManager
type=\"StorageResourceManager\">StorageResourceManager</storageResourceManager></returnval></RetrieveServiceContentResponse>\n</soapenv:Body>\n</soapenv:Envelope>"}
headers:
cache-control: [no-cache]
connection: [Keep-Alive]
content-length: ['3332']
content-type: [text/xml; charset=utf-8]
date: ['Tue, 22 Jul 2014 17:36:32 GMT']
set-cookie: [vmware_soap_session="528b8755-46b5-df6a-47fd-89e57d4807c5"; Path=/;
HttpOnly; Secure;]
status: {code: 200, message: OK}
- request:
body: '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body><Login xmlns="urn:vim25"><_this type="SessionManager">SessionManager</_this><userName>my_user</userName><password>bad_password</password></Login></soapenv:Body>
</soapenv:Envelope>'
headers:
Accept-Encoding: ['gzip, deflate']
Content-Type: [text/xml; charset=UTF-8]
Cookie: [vmware_soap_session="528b8755-46b5-df6a-47fd-89e57d4807c5"; Path=/;
HttpOnly; Secure;]
SOAPAction: ['"urn:vim25/4.1"']
method: POST
uri: https://vcsa:443/sdk
response:
body: {string: !!python/unicode "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soapenv:Envelope
xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"\n xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"\n
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n<soapenv:Body>\n<soapenv:Fault><faultcode>ServerFaultCode</faultcode><faultstring>Cannot
complete login due to an incorrect user name or password.</faultstring><detail><InvalidLoginFault
xmlns=\"urn:vim25\" xsi:type=\"InvalidLogin\"></InvalidLoginFault></detail></soapenv:Fault>\n</soapenv:Body>\n</soapenv:Envelope>"}
headers:
cache-control: [no-cache]
connection: [Keep-Alive]
content-length: ['585']
content-type: [text/xml; charset=utf-8]
date: ['Tue, 22 Jul 2014 17:36:37 GMT']
status: {code: 500, message: Internal Server Error}
version: 1

52
tests/test_connect.py Normal file
View File

@ -0,0 +1,52 @@
# VMware vSphere Python SDK
# Copyright (c) 2008-2014 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.
from tests import fixtures_path
import logging
import unittest
import vcr
from pyVim import connect
from pyVmomi import vim
class ConnectionTests(unittest.TestCase):
def setUp(self):
logging.basicConfig()
vcr_log = logging.getLogger('vcr')
vcr_log.setLevel(logging.DEBUG)
@vcr.use_cassette('basic_connection.yaml',
cassette_library_dir=fixtures_path, record_mode='none')
def test_basic_connection(self):
# see: http://python3porting.com/noconv.html
si = connect.Connect(host='vcsa',
user='my_user',
pwd='my_password')
session_id = si.content.sessionManager.currentSession.key
# NOTE (hartsock): assertIsNotNone does not work in Python 2.6
self.assertTrue(session_id is not None)
self.assertEqual('52773cd3-35c6-b40a-17f1-fe664a9f08f3', session_id)
@vcr.use_cassette('basic_connection_bad_password.yaml',
cassette_library_dir=fixtures_path, record_mode='none')
def test_basic_connection_bad_password(self):
def should_fail():
connect.Connect(host='vcsa',
user='my_user',
pwd='bad_password')
self.assertRaises(vim.fault.InvalidLogin, should_fail)