text: # VMware vSphere Python SDK
  # Copyright (c) 2008-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.
  from __future__ import absolute_import
  
  import six
  from six import reraise
  from six.moves import http_client
  from six.moves import StringIO
  from six.moves import zip
  from six import u
  from six import iteritems
  
  import sys
  import os
  import platform
  import socket
  import subprocess
  import time
  from six.moves.urllib.parse import urlparse
  from datetime import datetime
  from xml.parsers.expat import ParserCreate
  # We have our own escape functionality.
  # from xml.sax.saxutils import escape
  
  from pyVmomi.VmomiSupport import *
  from pyVmomi.StubAdapterAccessorImpl import StubAdapterAccessorMixin
  import pyVmomi.Iso8601
  import base64
  from xml.parsers.expat import ExpatError
  import copy
  import contextlib
  
  try:
     USERWORLD = os.uname()[0] == 'VMkernel'
  except:
     USERWORLD = False
  
  # Timeout value used for idle connections in client connection pool.
  # Default value is 900 seconds (15 minutes).
  CONNECTION_POOL_IDLE_TIMEOUT_SEC = 900
  
  NS_SEP = " "
  
  XML_ENCODING = 'UTF-8'
  XML_HEADER = '<?xml version="1.0" encoding="{0}"?>'.format(XML_ENCODING)
  
  XMLNS_SOAPENC = "http://schemas.xmlsoap.org/soap/encoding/"
  XMLNS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/"
  
  XSI_TYPE = XMLNS_XSI + NS_SEP + u('type')
  
  # Note: Must make a copy to use the SOAP_NSMAP
  # TODO: Change to frozendict, if available
  SOAP_NSMAP = { XMLNS_SOAPENC: 'soapenc', XMLNS_SOAPENV: 'soapenv',
                 XMLNS_XSI: 'xsi', XMLNS_XSD: 'xsd' }
  
  SOAP_ENVELOPE_TAG = "{0}:Envelope".format(SOAP_NSMAP[XMLNS_SOAPENV])
  SOAP_HEADER_TAG = "{0}:Header".format(SOAP_NSMAP[XMLNS_SOAPENV])
  SOAP_FAULT_TAG = "{0}:Fault".format(SOAP_NSMAP[XMLNS_SOAPENV])
  SOAP_BODY_TAG = "{0}:Body".format(SOAP_NSMAP[XMLNS_SOAPENV])
  
  SOAP_ENVELOPE_START = '<{0} '.format(SOAP_ENVELOPE_TAG) + \
                        ' '.join(['xmlns:' + prefix + '="' + urn + '"' \
                                  for urn, prefix in iteritems(SOAP_NSMAP)]) + \
                        '>\n'
  SOAP_ENVELOPE_END = "\n</{0}>".format(SOAP_ENVELOPE_TAG)
  SOAP_HEADER_START = "<{0}>".format(SOAP_HEADER_TAG)
  SOAP_HEADER_END = "</{0}>".format(SOAP_HEADER_TAG)
  SOAP_BODY_START = "<{0}>".format(SOAP_BODY_TAG)
  SOAP_BODY_END = "</{0}>".format(SOAP_BODY_TAG)
  SOAP_START = SOAP_ENVELOPE_START + SOAP_BODY_START + '\n'
  SOAP_END = '\n' + SOAP_BODY_END + SOAP_ENVELOPE_END
  
  WSSE_PREFIX = "wsse"
  WSSE_HEADER_TAG = "{0}:Security".format(WSSE_PREFIX)
  WSSE_NS_URL = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
  WSSE_NS = 'xmlns:{0}="{1}"'.format(WSSE_PREFIX, WSSE_NS_URL)
  WSSE_HEADER_START = "<{0} {1}>".format(WSSE_HEADER_TAG, WSSE_NS)
  WSSE_HEADER_END = "</{0}>".format(WSSE_HEADER_TAG)
  
  ## MethodFault type
  MethodFault = GetVmodlType("vmodl.MethodFault")
  ## Localized MethodFault type
  LocalizedMethodFault = GetVmodlType("vmodl.LocalizedMethodFault")
  
  # These info are included in the http user-agent header
  PYTHON_VERSION = platform.python_version()
  OS_NAME = platform.uname()[0]
  OS_VERSION = platform.uname()[2]
  OS_ARCH = platform.uname()[4]
  
  ## 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("&", "&amp;").replace(">", "&gt;").replace("<", "&lt;")
      return escaped
  
  ## Get the start tag, end tag, and text handlers of a class
  def GetHandlers(obj):
     return (obj.StartElementHandler,
             obj.EndElementHandler,
             obj.CharacterDataHandler,
             obj.StartNamespaceDeclHandler,
             obj.EndNamespaceDeclHandler)
  
  ## Set the start tag, end tag, and text handlers of a parser
  def SetHandlers(obj, handlers):
     (obj.StartElementHandler,
      obj.EndElementHandler,
      obj.CharacterDataHandler,
      obj.StartNamespaceDeclHandler,
      obj.EndNamespaceDeclHandler) = handlers
  
  ## Serialize an object to bytes
  #
  # This function assumes CheckField(info, val) was already called
  # @param val the value to serialize
  # @param info the field
  # @param version the version
  # @param nsMap a dict of xml ns -> prefix
  # @return the serialized object as bytes
  # @param encoding Deprecated this is not used during serialization since we always
  #        use utf-8 to encode a request message. We didn't remove the
  #        parameter so it is still compatible with clients that are still using it.
  def Serialize(val, info=None, version=None, nsMap=None, encoding=None):
     return _SerializeToUnicode(val, info=info, version=version, nsMap=nsMap).encode(XML_ENCODING)
  
  ## Serialize an object to unicode
  #
  # This function assumes CheckField(info, val) was already called
  # @param val the value to serialize
  # @param info the field
  # @param version the version
  # @param nsMap a dict of xml ns -> prefix
  # @return the serialized object as unicode
  def SerializeToUnicode(val, info=None, version=None, nsMap=None):
     return _SerializeToUnicode(val, info=info, version=version, nsMap=nsMap)
  
  ## Serialize an object to unicode
  #
  # This function assumes CheckField(info, val) was already called
  # @param val the value to serialize
  # @param info the field
  # @param version the version
  # @param nsMap a dict of xml ns -> prefix
  # @return the serialized object as unicode
  def _SerializeToUnicode(val, info=None, version=None, nsMap=None):
     if version is None:
        try:
           if isinstance(val, list):
              itemType = val.Item
              version = itemType._version
           else:
              if val is None:
                 # neither val nor version is given
                 return ''
              # Pick up the version from val
              version = val._version
        except AttributeError:
           version = BASE_VERSION
     if info is None:
        info = Object(name="object", type=object, version=version, flags=0)
  
     writer = StringIO()
     SoapSerializer(writer, version, nsMap).Serialize(val, info)
     return writer.getvalue()
  
  ## Serialize fault detail
  #
  # Serializes a fault as the content of the detail element in a
  # soapenv:Fault (i.e. without a LocalizedMethodFault wrapper).
  #
  # This function assumes CheckField(info, val) was already called
  # @param val the value to serialize
  # @param info the field
  # @param version the version
  # @param nsMap a dict of xml ns -> prefix
  # @return the serialized object as a unicode string
  def SerializeFaultDetail(val, info=None, version=None, nsMap=None, encoding=None):
     if version is None:
        try:
           if not isinstance(val, MethodFault):
              raise TypeError('{0} is not a MethodFault'.format(str(val)))
           version = val._version
        except AttributeError:
           version = BASE_VERSION
     if info is None:
        info = Object(name="object", type=object, version=version, flags=0)
  
     writer = StringIO()
     SoapSerializer(writer, version, nsMap, encoding).SerializeFaultDetail(val, info)
     return writer.getvalue()
  
  ## SOAP serializer
  #
  class SoapSerializer:
     """ SoapSerializer """
     ## Serializer constructor
     #
     # @param writer File writer
     # @param version the version
     # @param nsMap a dict of xml ns -> prefix
     # @param encoding Deprecated this is not used during serialization since we always
     #        use utf-8 to encode a request message. We didn't remove the
     #        parameter so it is still compatible with clients that are still using it.
     def __init__(self, writer, version, nsMap, encoding=None):
        """ Constructor """
        self.writer = writer
        self.version = version
        self.nsMap = nsMap and nsMap or {}
        for ns, prefix in iteritems(self.nsMap):
           if prefix == '':
              self.defaultNS = ns
              break
        else:
           self.defaultNS = ''
  
        # Additional attr for outermost tag
        self.outermostAttrs = ''
  
        # Fill in required xmlns, if not defined
        for nsPrefix, ns, attrName in [('xsi', XMLNS_XSI, 'xsiPrefix'),
                                       ('xsd', XMLNS_XSD, 'xsdPrefix')]:
           prefix = self.nsMap.get(ns)
           if not prefix:
              prefix = nsPrefix
              self.outermostAttrs += ' xmlns:{0}="{1}"'.format(prefix, ns)
              self.nsMap = self.nsMap.copy()
              self.nsMap[ns] = prefix
           setattr(self, attrName, prefix + ":")
  
  
     ## Serialize an object
     #
     # This function assumes CheckField(info, val) was already called
     # @param val the value to serialize
     # @param info the field
     def Serialize(self, val, info):
        """ Serialize an object """
        self._Serialize(val, info, self.defaultNS)
  
     ## Serialize fault detail
     #
     # Serializes a fault as the content of the detail element in a
     # soapenv:Fault (i.e. without a LocalizedMethodFault wrapper).
     #
     # This function assumes CheckField(info, val) was already called
     # @param val the value to serialize
     # @param info the field
     def SerializeFaultDetail(self, val, info):
        """ Serialize an object """
        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 """
        if ns == self.defaultNS:
           return ''
        prefix = self.nsMap[ns]
        return prefix and prefix + ':' or ''
  
     def _QName(self, typ, defNS):
        """ Get fully qualified wsdl name (prefix:name) """
        attr = ''
        ns, name = GetQualifiedWsdlName(typ)
        if ns == defNS:
           prefix = ''
        else:
           try:
              prefix = self.nsMap[ns]
           except KeyError:
              # We have not seen this ns before
              prefix = ns.split(':', 1)[-1]
              attr = ' xmlns:{0}="{1}"'.format(prefix, ns)
        return attr, prefix and prefix + ':' + name or name
  
     ## Serialize an object to unicode (internal)
     #
     # @param val the value to serialize
     # @param info the field
     # @param defNS the default namespace
     def _Serialize(self, val, info, defNS):
        """ Serialize an object """
        if not IsChildVersion(self.version, info.version):
           return
  
        if val is None:
           if info.flags & F_OPTIONAL:
              return
           else:
              raise TypeError('Field "{0}" is not optional'.format(info.name))
        elif isinstance(val, list) and len(val) == 0:
           if info.type is object:
              # Make sure an empty array assigned to Any is typed
              if not isinstance(val, Array):
                 raise TypeError('Field "{0}": Cannot assign empty native python array to an Any'.format(info.name))
           elif info.flags & F_OPTIONAL:
              # Skip optional non-Any
              return
           else:
               raise TypeError('Field "{0}" not optional'.format(info.name))
  
        if self.outermostAttrs:
           attr = self.outermostAttrs
           self.outermostAttrs = None
        else:
           attr = ''
        currDefNS = defNS
        # Emit default ns if tag ns is not the same
        currTagNS = GetWsdlNamespace(info.version)
        if currTagNS != defNS:
           attr += ' xmlns="{0}"'.format(currTagNS)
           currDefNS = currTagNS
  
        if isinstance(val, DataObject):
           if isinstance(val, MethodFault):
              newVal = LocalizedMethodFault(fault=val, localizedMessage=val.msg)
              if info.type is object:
                 faultType = object
              else:
                 faultType = LocalizedMethodFault
              newInfo = Object(name=info.name, type=faultType,
                               version=info.version, flags=info.flags)
              self._SerializeDataObject(newVal, newInfo, attr, currDefNS)
           else:
              self._SerializeDataObject(val, info, attr, currDefNS)
        elif isinstance(val, ManagedObject):
           if info.type is object:
              nsattr, qName = self._QName(ManagedObject, currDefNS)
              attr += '{0} {1}type="{2}"'.format(nsattr, self.xsiPrefix, qName)
           if val._serverGuid is not None:
              attr += ' serverGuid="{0}"'.format(val._serverGuid)
           # val in vim type attr is not namespace qualified
           # TODO: Add a new "typens" attr?
           ns, name = GetQualifiedWsdlName(Type(val))
           attr += ' type="{0}"'.format(name)
           self.writer.write('<{0}{1}>{2}</{3}>'.format(info.name, attr,
                                                val._moId,
                                                info.name))
        elif isinstance(val, list):
           if info.type is object:
              itemType = val.Item
              if (itemType is ManagedMethod or itemType is PropertyPath or  itemType is type):
                 tag = 'string'
                 typ = GetVmodlType("string[]")
              elif issubclass(itemType, ManagedObject):
                 tag = 'ManagedObjectReference'
                 typ = ManagedObject.Array
              else:
                 tag = GetWsdlName(itemType)
                 typ = Type(val)
              nsattr, qName = self._QName(typ, currDefNS)
  
              # For WSDL, since we set tag of ManagedObjects to ManagedObjectReferences,
              # the name of its array should be ArrayOfManagedObjectReference
              if qName.endswith("ArrayOfManagedObject"):
                 qName += "Reference"
  
              attr += '{0} {1}type="{2}"'.format(nsattr, self.xsiPrefix, qName)
              self.writer.write('<{0}{1}>'.format(info.name, attr))
  
              itemInfo = Object(name=tag, type=itemType,
                                version=info.version, flags=info.flags)
              for it in val:
                 self._Serialize(it, itemInfo, currDefNS)
              self.writer.write('</{0}>'.format(info.name))
           else:
              itemType = info.type.Item
              itemInfo = Object(name=info.name, type=itemType,
                                version=info.version, flags=info.flags)
              for it in val:
                 self._Serialize(it, itemInfo, defNS)
        elif isinstance(val, type) or isinstance(val, type(Exception)):
           if info.type is object:
              attr += ' {0}type="{1}string"'.format(self.xsiPrefix, self.xsdPrefix)
           self.writer.write('<{0}{1}>{2}</{0}>'.format(
                             info.name, attr, GetWsdlName(val)))
        elif isinstance(val, ManagedMethod):
           if info.type is object:
              attr += ' {0}type="{1}string"'.format(self.xsiPrefix, self.xsdPrefix)
           self.writer.write('<{0}{1}>{2}</{0}>'.format(
                                info.name, attr, val.info.wsdlName))
        elif isinstance(val, datetime):
           if info.type is object:
              nsattr, qName = self._QName(Type(val), currDefNS)
              attr += '{0} {1}type="{2}"'.format(nsattr, self.xsiPrefix, qName)
           result = Iso8601.ISO8601Format(val)
           self.writer.write('<{0}{1}>{2}</{0}>'.format(info.name, attr, result))
        elif isinstance(val, binary):
           if info.type is object:
              nsattr, qName = self._QName(Type(val), currDefNS)
              attr += '{0} {1}type="{2}"'.format(nsattr, self.xsiPrefix, qName)
           result = base64.b64encode(val)
           if PY3:
              # In python3 the bytes result after the base64 encoding has a
              # leading 'b' which causes error when we use it to construct the
              # soap message. Workaround the issue by converting the result to
              # string. Since the result of base64 encoding contains only subset
              # of ASCII chars, converting to string will not change the value.
              result = str(result, XML_ENCODING)
           self.writer.write('<{0}{1}>{2}</{0}>'.format(info.name, attr, result))
        elif isinstance(val, bool):
           if info.type is object:
              nsattr, qName = self._QName(Type(val), currDefNS)
              attr += '{0} {1}type="{2}"'.format(nsattr, self.xsiPrefix, qName)
           result = val and "true" or "false"
           self.writer.write('<{0}{1}>{2}</{0}>'.format(info.name, attr, result))
        elif isinstance(val, six.integer_types) or isinstance(val, float):
           if info.type is object:
              nsattr, qName = self._QName(Type(val), currDefNS)
              attr += '{0} {1}type="{2}"'.format(nsattr, self.xsiPrefix, qName)
           result = six.text_type(val)
           self.writer.write('<{0}{1}>{2}</{0}>'.format(info.name, attr, result))
        elif isinstance(val, Enum):
           if info.type is object:
              nsattr, qName = self._QName(Type(val), currDefNS)
              attr += '{0} {1}type="{2}"'.format(nsattr, self.xsiPrefix, qName)
           self.writer.write('<{0}{1}>{2}</{0}>'.format(info.name, attr, val))
        else:
           if info.type is object:
              if isinstance(val, PropertyPath):
                 attr += ' {0}type="{1}string"'.format(self.xsiPrefix, self.xsdPrefix)
              else:
                 nsattr, qName = self._QName(Type(val), currDefNS)
                 attr += '{0} {1}type="{2}"'.format(nsattr, self.xsiPrefix, qName)
  
           if isinstance(val, six.binary_type):
              # Use UTF-8 rather than self.encoding.  self.encoding is for
              # output of serializer, while 'val' is our input.  And regardless
              # of what our output is, our input should be always UTF-8.  Yes,
              # it means that if you emit output in other encoding than UTF-8,
              # you cannot serialize it again once more.  That's feature, not
              # a bug.
              val = val.decode(XML_ENCODING)
           result = XmlEscape(val)
           self.writer.write(u'<{0}{1}>{2}</{0}>'.format(info.name, attr, result))
  
     ## Serialize a a data object (internal)
     #
     # @param val the value to serialize
     # @param info the field
     # @param attr attributes to serialized in the outermost elementt
     # @param currDefNS the current default namespace
     def _SerializeDataObject(self, val, info, attr, currDefNS):
        if info.flags & F_LINK:
           # Attribute is a link and Object is present instead of its key.
           # We need to serialize just the key and not the entire object
           self._Serialize(val.key, info, currDefNS)
           return
        dynType = GetCompatibleType(Type(val), self.version)
        if dynType != info.type:
           nsattr, qName = self._QName(dynType, currDefNS)
           attr += '{0} {1}type="{2}"'.format(nsattr, self.xsiPrefix, qName)
        self.writer.write('<{0}{1}>'.format(info.name, attr))
        if dynType is LocalizedMethodFault:
           # Serialize a MethodFault as LocalizedMethodFault on wire
           # See PR 670229
           for prop in val._GetPropertyList():
              propVal = getattr(val, prop.name)
              if prop.name == 'fault':
                 propVal = copy.copy(propVal)
                 propVal.msg = None
                 self._SerializeDataObject(propVal, prop, '', currDefNS)
              else:
                 self._Serialize(propVal, prop, currDefNS)
        else:
           for prop in val._GetPropertyList():
              self._Serialize(getattr(val, prop.name), prop, currDefNS)
  
        self.writer.write('</{0}>'.format(info.name))
  
  
  class ParserError(KeyError):
      # NOTE (hartsock): extends KeyError since parser logic is written to
      # catch KeyError types. Normally, I would want PerserError to be a root
      # type for all parser faults.
      pass
  
  def ParseData(parser, data):
     # NOTE (hartsock): maintaining library internal consistency here, this is
     # a refactoring that rolls up some repeated code blocks into a method so
     # that we can refactor XML parsing behavior in a single place.
     try:
        if isinstance(data, six.binary_type) or isinstance(data, six.text_type):
           parser.Parse(data)
        else:
           parser.ParseFile(data)
     except Exception:
        # wrap all parser faults with additional information for later
        # bug reporting on the XML parser code itself.
        (ec, ev, tb) = sys.exc_info()
        line = parser.CurrentLineNumber
        col = parser.CurrentColumnNumber
        pe = ParserError("xml document: "
                         "{0} parse error at: "
                         "line:{1}, col:{2}".format(data, line, col))
        # use six.reraise for python 2.x and 3.x compatability
        reraise(ParserError, pe, tb)
  
  ## Deserialize an object from a file or string
  #
  # This function will deserialize one top-level XML node.
  # @param data the data to deserialize (a file object or string)
  # @param resultType expected result type
  # @param stub stub for moRef deserialization
  # @return the deserialized object
  def Deserialize(data, resultType=object, stub=None):
     parser = ParserCreate(namespace_separator=NS_SEP)
     ds = SoapDeserializer(stub)
     ds.Deserialize(parser, resultType)
     ParseData(parser, data)
     return ds.GetResult()
  
  
  ## Expat deserializer namespace handler
  class ExpatDeserializerNSHandlers:
     def __init__(self, nsMap=None):
        # nsMap is a dict of ns prefix to a stack (list) of namespaces
        # The last element of the stack is current namespace
        if not nsMap:
           nsMap = {}
        self.nsMap = nsMap
  
     ## Get current default ns
     def GetCurrDefNS(self):
        return self._GetNamespaceFromPrefix()
  
     ## Get namespace and wsdl name from tag
     def GetNSAndWsdlname(self, tag):
        """ Map prefix:name tag into ns, name """
        idx = tag.find(":")
        if idx >= 0:
           prefix, name = tag[:idx], tag[idx + 1:]
        else:
           prefix, name = None, tag
        # Map prefix to ns
        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)
        if namespaces:
           namespaces.append(uri)
        else:
           self.nsMap[prefix] = [uri]
  
     ## Handle namespace end
     def EndNamespaceDeclHandler(self, prefix):
        self.nsMap[prefix].pop()
  
  
  ## SOAP -> Python Deserializer
  class SoapDeserializer(ExpatDeserializerNSHandlers):
     ## Constructor
     #
     # @param self self
     # @param stub Stub adapter to use for deserializing moRefs
     def __init__(self, stub=None, version=None):
        ExpatDeserializerNSHandlers.__init__(self)
        self.stub = stub
        if version:
           self.version = version
        elif self.stub:
           self.version = self.stub.version
        else:
           self.version = None
        self.result = None
  
     ## Deserialize a SOAP object
     #
     # @param self self
     # @param parser an expat parser
     # @param resultType the static type of the result
     # @param isFault true if the response is a fault response
     # @param nsMap a dict of prefix -> [xml ns stack]
     # @return the deserialized object
     def Deserialize(self, parser, resultType=object, isFault=False, nsMap=None):
        self.isFault = isFault
        self.parser = parser
        self.origHandlers = GetHandlers(parser)
        SetHandlers(parser, GetHandlers(self))
        self.resultType = resultType
        self.stack = []
        self.data = ""
        self.serverGuid = None
        if issubclass(resultType, list):
           self.result = resultType()
        else:
           self.result = None
        if not nsMap:
           nsMap = {}
        self.nsMap = nsMap
  
     ## Get the result of deserialization
     #  The links will not be resolved. User needs to explicitly resolve them
     #  using LinkResolver.
     def GetResult(self):
        return self.result
  
     def SplitTag(self, tag):
        """ Split tag into ns, name """
        idx = tag.find(NS_SEP)
        if idx >= 0:
           return tag[:idx], tag[idx + 1:]
        else:
           return "", tag
  
     def LookupWsdlType(self, ns, name, allowManagedObjectReference=False):
        """ Lookup wsdl type. Handle special case for some vmodl version """
        try:
           return GetWsdlType(ns, name)
        except KeyError:
           if allowManagedObjectReference:
              if name.endswith('ManagedObjectReference') and ns == XMLNS_VMODL_BASE:
                 return GetWsdlType(ns, name[:-len('Reference')])
           # WARNING!!! This is a temporary hack to get around server not
           # honoring @service tag (see bug 521744). Once it is fix, I am
           # going to back out this change
           if name.endswith('ManagedObjectReference') and allowManagedObjectReference:
              return GetWsdlType(XMLNS_VMODL_BASE, name[:-len('Reference')])
           return GuessWsdlType(name)
  
     ## Handle an opening XML tag
     def StartElementHandler(self, tag, attr):
        self.data = ""
        self.serverGuid = None
        deserializeAsLocalizedMethodFault = True
        if not self.stack:
           if self.isFault:
              ns, name = self.SplitTag(tag)
              objType = self.LookupWsdlType(ns, name[:-5])
              # Only top level soap fault should be deserialized as method fault
              deserializeAsLocalizedMethodFault = False
           else:
              objType = self.resultType
        elif isinstance(self.stack[-1], list):
           objType = self.stack[-1].Item
        elif isinstance(self.stack[-1], DataObject):
           # TODO: Check ns matches DataObject's namespace
           ns, name = self.SplitTag(tag)
           objType = self.stack[-1]._GetPropertyInfo(name).type
  
           # LocalizedMethodFault <fault> tag should be deserialized as method fault
           if name == "fault" and isinstance(self.stack[-1], LocalizedMethodFault):
              deserializeAsLocalizedMethodFault = False
        else:
           raise TypeError("Invalid type for tag {0}".format(tag))
  
        xsiType = attr.get(XSI_TYPE)
        if xsiType:
           # Ignore dynamic type for TypeName, MethodName, PropertyPath
           # @bug 150459
           if not (objType is type or objType is ManagedMethod or \
                                      objType is PropertyPath):
              ns, name = self.GetNSAndWsdlname(xsiType)
              dynType = self.LookupWsdlType(ns, name, allowManagedObjectReference=True)
              # TODO: Should be something like...
              #   dynType must be narrower than objType, except for
              #   ManagedObjectReference
              if not (issubclass(dynType, list) and issubclass(objType, list)):
                 objType = dynType
        else:
           if issubclass(objType, list):
              objType = objType.Item
  
        if self.version:
           objType = GetCompatibleType(objType, self.version)
        if issubclass(objType, ManagedObject):
           typeAttr = attr[u('type')]
           # val in vim type attr is not namespace qualified
           # However, this doesn't hurt to strip out namespace
           # TODO: Get the ns from "typens" attr?
           ns, name = self.GetNSAndWsdlname(typeAttr)
           if u('serverGuid') in attr:
              self.serverGuid = attr[u('serverGuid')]
           self.stack.append(GuessWsdlType(name))
        elif issubclass(objType, DataObject) or issubclass(objType, list):
           if deserializeAsLocalizedMethodFault and issubclass(objType, Exception):
              objType = LocalizedMethodFault
           self.stack.append(objType())
        else:
           self.stack.append(objType)
  
     ## Handle a closing XML tag
     def EndElementHandler(self, tag):
        try:
           obj = self.stack.pop()
        except IndexError:
           SetHandlers(self.parser, self.origHandlers)
           handler = self.parser.EndElementHandler
           del self.parser, self.origHandlers, self.stack, self.resultType
           if handler:
              return handler(tag)
           return
  
        data = self.data
        if isinstance(obj, type) or isinstance(obj, type(Exception)):
           if obj is type:
              if data is None or data == '':
                 obj = None
              else:
                 try:
                    # val in type val is not namespace qualified
                    # However, this doesn't hurt to strip out namespace
                    ns, name = self.GetNSAndWsdlname(data)
                    obj = GuessWsdlType(name)
                 except KeyError:
                    raise TypeError(data)
           elif obj is ManagedMethod:
              # val in Method val is not namespace qualified
              # However, this doesn't hurt to strip out namespace
              ns, name = self.GetNSAndWsdlname(data)
              try:
                 obj = GuessWsdlMethod(name)
              except KeyError:
                 obj = UncallableManagedMethod(name)
           elif obj is bool:
              if data == "0" or data.lower() == "false":
                 obj = bool(False)
              elif data == "1" or data.lower() == "true":
                 obj = bool(True)
              else:
                 raise TypeError(data)
           elif obj is binary:
              # Raise type error if decode failed
              obj = obj(base64.b64decode(data))
           elif obj is str:
              try:
                 obj = str(data)
              except ValueError:
                 obj = data
           elif obj is datetime:
              obj = pyVmomi.Iso8601.ParseISO8601(data)
              if not obj:
                 raise TypeError(data)
           # issubclass is very expensive. Test last
           elif issubclass(obj, ManagedObject):
              obj = obj(data, self.stub, self.serverGuid)
           elif issubclass(obj, Enum):
              obj = getattr(obj, data)
           else:
              obj = obj(data)
        elif isinstance(obj, LocalizedMethodFault):
           obj.fault.msg = obj.localizedMessage
           obj = obj.fault
  
        if self.stack:
           top = self.stack[-1]
           if isinstance(top, list):
              top.append(obj)
           elif isinstance(top, DataObject):
              ns, name = self.SplitTag(tag)
              info = top._GetPropertyInfo(name)
  
              if not isinstance(obj, list) and issubclass(info.type, list):
                 getattr(top, info.name).append(obj)
              else:
                 setattr(top, info.name, obj)
           else:
              ns, name = self.SplitTag(tag)
              setattr(top, name, obj)
        else:
           if not isinstance(obj, list) and issubclass(self.resultType, list):
              self.result.append(obj)
           else:
              self.result = obj
              SetHandlers(self.parser, self.origHandlers)
              del self.parser, self.origHandlers, self.stack, self.resultType
  
     ## Handle text data
     def CharacterDataHandler(self, data):
        self.data += data
  
  
  ## SOAP Response Deserializer class
  class SoapResponseDeserializer(ExpatDeserializerNSHandlers):
     ## Constructor
     #
     # @param self self
     # @param stub Stub adapter to use for deserializing moRefs
     def __init__(self, stub):
        ExpatDeserializerNSHandlers.__init__(self)
        self.stub = stub
        self.deser = SoapDeserializer(stub)
        self.soapFaultTag = XMLNS_SOAPENV + NS_SEP + "Fault"
  
     ## Deserialize a SOAP response
     #
     # @param self self
     # @param response the response (a file object or a string)
     # @param resultType expected result type
     # @param nsMap a dict of prefix -> [xml ns stack]
     # @return the deserialized object
     def Deserialize(self, response, resultType, nsMap=None):
        self.resultType = resultType
        self.stack = []
        self.msg = ""
        self.deser.result = None
        self.isFault = False
        self.parser = ParserCreate(namespace_separator=NS_SEP)
        try: # buffer_text only in python >= 2.3
           self.parser.buffer_text = True
        except AttributeError:
           pass
        if not nsMap:
           nsMap = {}
        self.nsMap = nsMap
        SetHandlers(self.parser, GetHandlers(self))
        ParseData(self.parser, response)
        result = self.deser.GetResult()
        if self.isFault:
           if result is None:
              result = GetVmodlType("vmodl.RuntimeFault")()
           result.msg = self.msg
        del self.resultType, self.stack, self.parser, self.msg, self.data, self.nsMap
        return result
  
     ## Handle an opening XML tag
     def StartElementHandler(self, tag, attr):
        self.data = ""
        if tag == self.soapFaultTag:
           self.isFault = True
        elif self.isFault and tag == "detail":
           self.deser.Deserialize(self.parser, object, True, self.nsMap)
        elif tag.endswith("Response"):
           self.deser.Deserialize(self.parser, self.resultType, False, self.nsMap)
  
     ## Handle text data
     def CharacterDataHandler(self, data):
        self.data += data
  
     ## Handle a closing XML tag
     def EndElementHandler(self, tag):
        if self.isFault and tag == "faultstring":
           try:
              self.msg = str(self.data)
           except ValueError:
              self.msg = self.data
  
  ## Base class that implements common functionality for stub adapters.
  ## Method that must be provided by the implementation class:
  ## -- InvokeMethod(ManagedObject mo, Object methodInfo, Object[] args)
  class StubAdapterBase(StubAdapterAccessorMixin):
     def __init__(self, version):
        StubAdapterAccessorMixin.__init__(self)
        self.ComputeVersionInfo(version)
  
     ## Compute the version information for the specified namespace
     #
     # @param ns the namespace
     def ComputeVersionInfo(self, version):
        versionNS = GetVersionNamespace(version)
        if versionNS.find("/") >= 0:
           self.versionId = '"urn:{0}"'.format(versionNS)
        else:
           self.versionId = ''
        self.version = version
  
  ## Base class that implements common functionality for SOAP-based stub adapters.
  ## Method that must be provided by the implementation class:
  ## -- InvokeMethod(ManagedObject mo, Object methodInfo, Object[] args)
  class SoapStubAdapterBase(StubAdapterBase):
     ## Serialize a VMOMI request to SOAP
     #
     # @param version API version
     # @param mo the 'this'
     # @param info method info
     # @param args method arguments
     # @return the serialized request
     def SerializeRequest(self, mo, info, args):
        if not IsChildVersion(self.version, info.version):
           raise GetVmodlType("vmodl.fault.MethodNotFound")(receiver=mo,
                                                            method=info.name)
        nsMap = SOAP_NSMAP.copy()
        defaultNS = GetWsdlNamespace(self.version)
        nsMap[defaultNS] = ''
  
        # Add xml header and soap envelope
        result = [XML_HEADER, '\n', SOAP_ENVELOPE_START]
  
        # Add request context and samlToken to soap header, if exists
        reqContexts = GetRequestContext()
        if self.requestContext:
           reqContexts.update(self.requestContext)
        samlToken = getattr(self, 'samlToken', None)
  
        if reqContexts or samlToken:
           result.append(SOAP_HEADER_START)
           for key, val in iteritems(reqContexts):
              # Note: Support req context of string type only
              if not isinstance(val, six.string_types):
                 raise TypeError("Request context key ({0}) has non-string value ({1}) of {2}".format(key, val, type(val)))
              ret = _SerializeToUnicode(val,
                              Object(name=key, type=str, version=self.version),
                              self.version,
                              nsMap)
              result.append(ret)
           if samlToken:
              result.append('{0} {1} {2}'.format(WSSE_HEADER_START,
                                                 samlToken,
                                                 WSSE_HEADER_END))
           result.append(SOAP_HEADER_END)
           result.append('\n')
  
        # Serialize soap body
        result.extend([SOAP_BODY_START,
                         '<{0} xmlns="{1}">'.format(info.wsdlName, defaultNS),
                         _SerializeToUnicode(mo, Object(name="_this", type=ManagedObject,
                                              version=self.version),
                                   self.version, nsMap)])
  
        # Serialize soap request parameters
        for (param, arg) in zip(info.params, args):
           result.append(_SerializeToUnicode(arg, param, self.version, nsMap))
        result.extend(['</{0}>'.format(info.wsdlName), SOAP_BODY_END, SOAP_ENVELOPE_END])
        return ''.join(result).encode(XML_ENCODING)
  
  ## 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
  ## the hostname.  Fairly gross but does the job.
  class UnixSocketConnection(http_client.HTTPConnection):
     # The HTTPConnection ctor expects a single argument, which it interprets
     # as the host to connect to; for UnixSocketConnection, we instead interpret
     # the parameter as the filesystem path of the Unix domain socket.
     def __init__(self, path):
        # Pass '' as the host to HTTPConnection; it doesn't really matter
        # what we pass (since we've overridden the connect method) as long
        # as it's a valid string.
        http_client.HTTPConnection.__init__(self, '')
        self.path = path
  
     def connect(self):
        # Hijack the connect method of HTTPConnection to connect to the
        # specified Unix domain socket instead.  Obey the same contract
        # as HTTPConnection.connect, which puts the socket in self.sock.
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        sock.connect(self.path)
        self.sock = sock
  
  try:
     # The ssl module is not available in python versions less than 2.6
     SSL_THUMBPRINTS_SUPPORTED = True
  
     import ssl
     import hashlib
  
     def _VerifyThumbprint(thumbprint, connection):
        '''If there is a thumbprint, connect to the server and verify that the
        SSL certificate matches the given thumbprint.  An exception is thrown
        if there is a mismatch.'''
        if thumbprint and isinstance(connection, http_client.HTTPSConnection):
           if not connection.sock:
              connection.connect()
           derCert = connection.sock.getpeercert(True)
           sha1 = hashlib.sha1()
           sha1.update(derCert)
           sha1Digest = sha1.hexdigest().lower()
           if sha1Digest != thumbprint:
              raise ThumbprintMismatchException(thumbprint, sha1Digest)
  
     # Function used to wrap sockets with SSL
     _SocketWrapper = ssl.wrap_socket
  
  except ImportError:
     SSL_THUMBPRINTS_SUPPORTED = False
  
     def _VerifyThumbprint(thumbprint, connection):
        if thumbprint and isinstance(connection, http_client.HTTPSConnection):
           raise Exception(
              "Thumbprint verification not supported on python < 2.6")
  
     def _SocketWrapper(rawSocket, keyfile, certfile, *args, **kwargs):
        wrappedSocket = socket.ssl(rawSocket, keyfile, certfile)
        return http_client.FakeSocket(rawSocket, wrappedSocket)
  
  
  ## Internal version of https connection
  #
  # Support ssl.wrap_socket params which are missing from httplib
  # HTTPSConnection (e.g. ca_certs)
  # Note: Only works if the ssl params are passing in as kwargs
  class _HTTPSConnection(http_client.HTTPSConnection):
     def __init__(self, *args, **kwargs):
        # Extract ssl.wrap_socket param unknown to httplib.HTTPSConnection,
        # and push back the params in connect()
        self._sslArgs = {}
        tmpKwargs = kwargs.copy()
        for key in ["server_side", "cert_reqs", "ssl_version", "ca_certs",
                    "do_handshake_on_connect", "suppress_ragged_eofs",
                    "ciphers"]:
           if key in tmpKwargs:
              self._sslArgs[key] = tmpKwargs.pop(key)
        http_client.HTTPSConnection.__init__(self, *args, **tmpKwargs)
  
     ## 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)
     def connect(self):
        if len(self._sslArgs) == 0:
           # No override
           http_client.HTTPSConnection.connect(self)
           return
  
        # Big hack. We have to copy and paste the httplib connect fn for
        # each python version in order to handle extra ssl paramters. Yuk!
        if hasattr(self, "source_address"):
           # Python 2.7
           sock = socket.create_connection((self.host, self.port),
                                           self.timeout, self.source_address)
           if self._tunnel_host:
              self.sock = sock
              self._tunnel()
           self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
                                       **self._sslArgs)
        elif hasattr(self, "timeout"):
           # Python 2.6
           sock = socket.create_connection((self.host, self.port), self.timeout)
           self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
                                       **self._sslArgs)
        else:
           # Unknown python version. Do nothing
           http_client.HTTPSConnection.connect(self)
           return
  
           # TODO: Additional verification of peer cert if needed
           # cert_reqs = self._sslArgs.get("cert_reqs", ssl.CERT_NONE)
           # ca_certs = self._sslArgs.get("ca_certs", None)
           # if cert_reqs != ssl.CERT_NONE and ca_certs:
           #   if hasattr(self.sock, "getpeercert"):
           #      # TODO: verify peer cert
           #      dercert = self.sock.getpeercert(False)
           #      # pemcert = ssl.DER_cert_to_PEM_cert(dercert)
  
  ## Stand-in for the HTTPSConnection class that will connect to a proxy and
  ## issue a CONNECT command to start an SSL tunnel.
  class SSLTunnelConnection(object):
     # @param proxyPath The path to pass to the CONNECT command.
     def __init__(self, proxyPath):
        self.proxyPath = proxyPath
  
     # Connects to a proxy server and initiates a tunnel to the destination
     # specified by proxyPath.  If successful, a new HTTPSConnection is returned.
     #
     # @param path The destination URL path.
     # @param key_file The SSL key file to use when wrapping the socket.
     # @param cert_file The SSL certificate file to use when wrapping the socket.
     # @param kwargs In case caller passed in extra parameters not handled by
     #        SSLTunnelConnection
     def __call__(self, path, key_file=None, cert_file=None, **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:
          raise http_client.HTTPException("{0} {1}".format(resp.status, resp.reason))
        retval = http_client.HTTPSConnection(path)
        retval.sock = _SocketWrapper(tunnel.sock,
                                     keyfile=key_file, certfile=cert_file)
        return retval
  
  
  class GzipReader:
     GZIP        = 1
     DEFLATE     = 2
  
     def __init__(self, rfile, encoding=GZIP, readChunkSize=512):
        self.rfile = rfile
        self.chunks = []
        self.bufSize = 0 # Remaining buffer
        assert(encoding in (GzipReader.GZIP, GzipReader.DEFLATE))
        self.encoding = encoding
        self.unzip = None
        self.readChunkSize = readChunkSize
  
     def _CreateUnzip(self, firstChunk):
        import zlib
        if self.encoding == GzipReader.GZIP:
           wbits = zlib.MAX_WBITS + 16
        elif self.encoding == GzipReader.DEFLATE:
           # Sniff out real deflate format
           chunkLen = len(firstChunk)
           # Assume raw deflate
           wbits = -zlib.MAX_WBITS
           if firstChunk[:3] == ['\x1f', '\x8b', '\x08']:
              # gzip: Apache mod_deflate will send gzip. Yurk!
              wbits = zlib.MAX_WBITS + 16
           elif chunkLen >= 2:
              b0 = ord(firstChunk[0])
              b1 = ord(firstChunk[1])
              if (b0 & 0xf) == 8 and (((b0 * 256 + b1)) % 31) == 0:
                 # zlib deflate
                 wbits = min(((b0 & 0xf0) >> 4) + 8, zlib.MAX_WBITS)
        else:
           assert(False)
        self.unzip = zlib.decompressobj(wbits)
        return self.unzip
  
     def read(self, bytes=-1):
        chunks = self.chunks
        bufSize = self.bufSize
  
        while bufSize < bytes or bytes == -1:
           # Read and decompress
           chunk = self.rfile.read(self.readChunkSize)
  
           if self.unzip == None:
              self._CreateUnzip(chunk)
  
           if chunk:
              inflatedChunk = self.unzip.decompress(chunk)
              bufSize += len(inflatedChunk)
              chunks.append(inflatedChunk)
           else:
              # Returns whatever we have
              break
  
        if bufSize <= bytes or bytes == -1:
           leftoverBytes = 0
           leftoverChunks = []
        else:
           leftoverBytes = bufSize - bytes
           # Adjust last chunk to hold only the left over bytes
           lastChunk = chunks.pop()
           chunks.append(lastChunk[:-leftoverBytes])
           leftoverChunks = [lastChunk[-leftoverBytes:]]
  
        self.chunks = leftoverChunks
        self.bufSize = leftoverBytes
  
        buf = b"".join(chunks)
        return buf
  
  ## SOAP stub adapter object
  class SoapStubAdapter(SoapStubAdapterBase):
     ## Constructor
     #
     # The endpoint can be specified individually as either a host/port
     # combination, or with a URL (using a url= keyword).
     #
     # @param self self
     # @param host host
     # @param port port (pass negative port number for no SSL)
     # @param **** Deprecated. Please use version instead **** ns API namespace
     # @param path location of SOAP VMOMI service
     # @param url URL (overrides host, port, path if set)
     # @param sock unix domain socket path (overrides host, port, url if set)
     # @param poolSize size of HTTP connection pool
     # @param certKeyFile The path to the PEM-encoded SSL private key file.
     # @param certFile The path to the PEM-encoded SSL certificate file.
     # @param httpProxyHost The host name of the proxy server.
     # @param httpProxyPort The proxy server port.
     # @param sslProxyPath Path to use when tunneling through VC's reverse proxy.
     # @param thumbprint The SHA1 thumbprint of the server's SSL certificate.
     #   Some use a thumbprint of the form xx:xx:xx..:xx.  We ignore the ":"
     #   characters.  If set to None, any thumbprint is accepted.
     # @param cacertsFile CA certificates file in PEM format
     # @param version API version
     # @param connectionPoolTimeout Timeout in secs for idle connections in client pool. Use -1 to disable any timeout.
     # @param samlToken SAML Token that should be used in SOAP security header for login
     # @param sslContext SSL Context describing the various SSL options. It is only
     #                   supported in Python 2.7.9 or higher.
     def __init__(self, host='localhost', port=443, ns=None, path='/sdk',
                  url=None, sock=None, poolSize=5,
                  certFile=None, certKeyFile=None,
                  httpProxyHost=None, httpProxyPort=80, sslProxyPath=None,
                  thumbprint=None, cacertsFile=None, version=None,
                  acceptCompressedResponses=True,
                  connectionPoolTimeout=CONNECTION_POOL_IDLE_TIMEOUT_SEC,
                  samlToken=None, sslContext=None, requestContext=None):
        if ns:
           assert(version is None)
           version = versionMap[ns]
        elif not version:
           version = 'vim.version.version1'
        SoapStubAdapterBase.__init__(self, version=version)
        self.cookie = ""
        if sock:
           self.scheme = UnixSocketConnection
           # Store sock in the host member variable because that's where
           # the UnixSocketConnection ctor expects to find it -- see above
           self.host = sock
        elif url:
           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 ('', '/'):
              path = urlpath
           self.scheme = scheme == "http" and http_client.HTTPConnection \
                      or scheme == "https" and _HTTPSConnection
        else:
           port, self.scheme = port < 0 and (-port, http_client.HTTPConnection) \
                                         or (port, _HTTPSConnection)
           if host.find(':') != -1:  # is IPv6?
              host = '[' + host + ']'
           self.host = '{0}:{1}'.format(host, port)
  
        self.path = path
        if thumbprint:
           self.thumbprint = thumbprint.replace(":", "").lower()
           if len(self.thumbprint) != 40:
             raise Exception("Invalid SHA1 thumbprint -- {0}".format(thumbprint))
        else:
           self.thumbprint = None
  
        self.is_ssl_tunnel = False
        if sslProxyPath:
           self.scheme = SSLTunnelConnection(sslProxyPath)
           self.is_ssl_tunnel = True
        elif httpProxyHost:
           if self.scheme == _HTTPSConnection:
              self.scheme = SSLTunnelConnection(self.host)
              self.is_ssl_tunnel = True
           else:
              if url:
                 self.path = url
              else:
                 self.path = "http://{0}/{1}".format(self.host, path)
           # Swap the actual host with the proxy.
           self.host = "{0}:{1}".format(httpProxyHost, httpProxyPort)
        self.poolSize = poolSize
        self.pool = []
        self.connectionPoolTimeout = connectionPoolTimeout
        self.lock = threading.Lock()
        self.schemeArgs = {}
        if certKeyFile:
           self.schemeArgs['key_file'] = certKeyFile
        if certFile:
           self.schemeArgs['cert_file'] = certFile
        if cacertsFile:
           self.schemeArgs['ca_certs'] = cacertsFile
           self.schemeArgs['cert_reqs'] = ssl.CERT_REQUIRED
        if sslContext:
           self.schemeArgs['context'] = sslContext
        self.samlToken = samlToken
        self.requestContext = requestContext
        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):
           if self.scheme == _HTTPSConnection 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
     #   the request. The func is appended to the requestModifierList and then
     #   popped after the request is modified.
     @contextlib.contextmanager
     def requestModifier(self, func):
        self.requestModifierList.append(func)
        try:
           yield
        finally:
           self.requestModifierList.pop()
     ## Invoke a managed method
     #
     # @param self self
     # @param mo the 'this'
     # @param info method info
     # @param args arguments
     # @param outerStub If not-None, this should be a reference to the wrapping
     #   stub adapter.  Any ManagedObject references returned from this method
     #   will have outerStub in their _stub field.  Note that this also changes
     #   the return type to a tuple containing the HTTP status and the
     #   deserialized object so that it's easier to distinguish an API error from
     #   a connection error.
     def InvokeMethod(self, mo, info, args, outerStub=None):
        if outerStub is None:
           outerStub = self
  
        headers = {'Cookie' : self.cookie,
                   'SOAPAction' : self.versionId,
                   'Content-Type': 'text/xml; charset={0}'.format(XML_ENCODING),
                   'User-Agent': 'pyvmomi Python/{0} ({1}; {2}; {3})'.
                      format(PYTHON_VERSION, OS_NAME, OS_VERSION, OS_ARCH)}
        if self._acceptCompressedResponses:
           headers['Accept-Encoding'] = 'gzip, deflate'
        req = self.SerializeRequest(mo, info, args)
        for modifier in self.requestModifierList:
           req = modifier(req)
        print(req)
        conn = self.GetConnection()
        try:
           conn.request('POST', self.path, req, headers)
           resp = conn.getresponse()
        except (socket.error, http_client.HTTPException):
           # The server is probably sick, drop all of the cached connections.
           self.DropConnections()
           raise
        # NOTE (hartsocks): this cookie handling code should go away in a future
        # release. The string 'set-cookie' and 'Set-Cookie' but both are
        # acceptable, but the supporting library may have a bug making it
        # case sensitive when it shouldn't be. The term 'set-cookie' will occur
        # more frequently than 'Set-Cookie' based on practical testing.
        cookie = resp.getheader('set-cookie')
        if cookie is None:
            # try case-sensitive header for compatibility
            cookie = resp.getheader('Set-Cookie')
        status = resp.status
  
        if cookie:
           self.cookie = cookie
        if status == 200 or status == 500:
           try:
              fd = resp
              encoding = resp.getheader('Content-Encoding', 'identity').lower()
              if encoding == 'gzip':
                 fd = GzipReader(resp, encoding=GzipReader.GZIP)
              elif encoding == 'deflate':
                 fd = GzipReader(resp, encoding=GzipReader.DEFLATE)
              deserializer = SoapResponseDeserializer(outerStub)
              obj = deserializer.Deserialize(fd, info.result)
           except Exception as exc:
              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
              # contexts (ie: methods) that we are blind to here. Refactor this.
  
              # The server might be sick, drop all of the cached connections.
              self.DropConnections()
              raise exc
           else:
              resp.read()
              self.ReturnConnection(conn)
           if outerStub != self:
              return (status, obj)
           if status == 200:
              return obj
           else:
              raise obj # pylint: disable-msg=E0702
        else:
           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
     #  SoapStubAdapter lock must be acquired before this method is called.
     def _CloseIdleConnections(self):
        if self.connectionPoolTimeout >= 0:
           currentTime = time.time()
           idleConnections = []
           for conn, lastAccessTime in self.pool:
              idleTime = currentTime - lastAccessTime
              if idleTime >= self.connectionPoolTimeout:
                 i = self.pool.index((conn, lastAccessTime))
                 idleConnections = self.pool[i:]
                 self.pool = self.pool[:i]
                 break
  
           for conn, _ in idleConnections:
              self._CloseConnection(conn)
  
     ## Get a HTTP connection from the pool
     def GetConnection(self):
        self.lock.acquire()
        self._CloseIdleConnections()
        if self.pool:
           result, _ = self.pool.pop(0)
           self.lock.release()
        else:
           self.lock.release()
           result = self.scheme(self.host, **self.schemeArgs)
  
           # Always disable NAGLE algorithm
           #
           # Python httplib (2.6 and below) is splitting a http request into 2
           # packets (header and body). It first send the header, but will not
           # send the body until it receives the ack (for header) from server
           # [NAGLE at work]. The delayed ack time on ESX is around 40 - 100 ms
           # (depends on version) and can go up to 200 ms. This effectively slow
           # down each pyVmomi call by the same amount of time.
           #
           # Disable NAGLE on client will force both header and body packets to
           # get out immediately, and eliminated the delay
           #
           # This bug is fixed in python 2.7, however, only if the request
           # body is a string (which is true for now)
           if sys.version_info[:2] < (2,7):
              self.DisableNagle(result)
  
           _VerifyThumbprint(self.thumbprint, result)
  
        return result
  
     ## Drop all cached connections to the server.
     def DropConnections(self):
        self.lock.acquire()
        oldConnections = self.pool
        self.pool = []
        self.lock.release()
        for conn, _ in oldConnections:
           self._CloseConnection(conn)
  
     ## Return a HTTP connection to the pool
     def ReturnConnection(self, conn):
        self.lock.acquire()
        self._CloseIdleConnections()
        # In case of ssl tunneling, only add the conn if the conn has not been closed
        if len(self.pool) < self.poolSize and (not self.is_ssl_tunnel or conn.sock):
           self.pool.insert(0, (conn, time.time()))
           self.lock.release()
        else:
           self.lock.release()
           # 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.
           self._CloseConnection(conn)
  
     ## Disable nagle on a http connections
     def DisableNagle(self, conn):
        # Override connections' connect function to force disable NAGLE
        if self.scheme != UnixSocketConnection and getattr(conn, "connect"):
           orgConnect = conn.connect
           def ConnectDisableNagle(*args, **kwargs):
              orgConnect(*args, **kwargs)
              sock = getattr(conn, "sock")
              if sock:
                 try:
                    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
                 except Exception:
                    pass
           conn.connect = ConnectDisableNagle
  
  ## Need to override the depcopy method. Since, the stub is not deep copyable
  #  due to the thread lock and connection pool, deep copy of a managed object
  #  fails. Further different instances of a managed object still share the
  #  same soap stub. Hence, returning self here is fine.
  def __deepcopy__(self, memo):
     return self
  
  HEADER_SECTION_END = '\r\n\r\n'
  
  ## Parse an HTTP response into its headers and body
  def ParseHttpResponse(httpResponse):
     headerEnd = httpResponse.find(HEADER_SECTION_END)
     if headerEnd == -1:
        return ('', '')
     headerEnd += len(HEADER_SECTION_END);
     headerText = httpResponse[:headerEnd]
     bodyText = httpResponse[headerEnd:]
     return (headerText, bodyText)
  
  
  ## SOAP-over-stdio stub adapter object
  class SoapCmdStubAdapter(SoapStubAdapterBase):
     ## Constructor
     #
     # @param self self
     # @param cmd command to execute
     # @param ns API namespace
     def __init__(self, cmd, version='vim.version.version1'):
        SoapStubAdapterBase.__init__(self, version=version)
        self.cmd = cmd
        self.systemError = GetVmodlType('vmodl.fault.SystemError')
  
     ## Invoke a managed method
     #
     # @param self self
     # @param mo the 'this'
     # @param info method info
     # @param args arguments
     def InvokeMethod(self, mo, info, args):
        argv = self.cmd.split()
        req = self.SerializeRequest(mo, info, args)
        env = dict(os.environ)
        env['REQUEST_METHOD'] = 'POST'
        env['CONTENT_LENGTH'] = str(len(req))
        env['HTTP_SOAPACTION'] = self.versionId[1:-1]
        p = subprocess.Popen(argv,
                             stdin=subprocess.PIPE,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             env=env)
        (outText, errText) = p.communicate(req)
        if p.returncode < 0:
           # Process died with a signal
           errText = "Process terminated with signal {0}\n{1}".format(-p.returncode, errText)
           raise self.systemError(msg=errText, reason=errText)
  
        try:
           (responseHeaders, responseBody) = ParseHttpResponse(outText)
           obj = SoapResponseDeserializer(self).Deserialize(responseBody, info.result)
        except:
           errText = "Failure parsing SOAP response ({0})\n{1}}".format(outText, errText)
           raise self.systemError(msg=errText, reason=errText)
  
        if p.returncode == 0:
           return obj
        elif obj is None:
           raise self.systemError(msg=errText, reason=errText)
        else:
           raise obj # pylint: disable-msg=E0702
  
  
  class SessionOrientedStub(StubAdapterBase):
     '''A session-oriented stub adapter that will relogin to the destination if a
     session-oriented exception is thrown.
  
  
     Here's an example.  First, we setup the communication substrate:
  
     >>> soapStub = SoapStubAdapter(host="192.168.1.2", ns="vim25/5.0")
  
     Create a SessionOrientedStub that uses the stub we just created for talking
     to the server:
  
     >>> from pyVim.connect import VimSessionOrientedStub
     >>> sessionStub = VimSessionOrientedStub(
     ...     soapStub,
     ...     VimSessionOrientedStub.makeUserLoginMethod("root", "vmware"))
  
     Perform some privileged operations without needing to explicitly login:
  
     >>> si = Vim.ServiceInstance("ServiceInstance", sessionStub)
     >>> si.content.sessionManager.sessionList
     >>> si.content.sessionManager.Logout()
     >>> si.content.sessionManager.sessionList
     '''
  
     STATE_UNAUTHENTICATED = 0
     STATE_AUTHENTICATED = 1
  
     SESSION_EXCEPTIONS = tuple()
  
     def __init__(self, soapStub, loginMethod, retryDelay=0.1, retryCount=4):
        '''Construct a SessionOrientedStub.
  
        The stub starts off in the "unauthenticated" state, so it will call the
        loginMethod on the first invocation of a method.  If a communication error
        is encountered, the stub will wait for retryDelay seconds and then try to
        call the method again.  If the server throws an exception that is in the
        SESSION_EXCEPTIONS tuple, it will be caught and the stub will transition
        back into the "unauthenticated" state so that another login will be
        performed.
  
        @param soapStub The communication substrate.
        @param loginMethod A function that takes a single parameter, soapStub, and
          performs the necessary operations to authenticate with the server.
        @param retryDelay The amount of time to sleep before retrying after a
          communication error.
        @param retryCount The number of times to retry connecting to the server.
        '''
        assert callable(loginMethod)
        assert retryCount >= 0
        StubAdapterBase.__init__(self, version=soapStub.version)
  
        self.lock = threading.Lock()
        self.soapStub = soapStub
        self.state = self.STATE_UNAUTHENTICATED
  
        self.loginMethod = loginMethod
        self.retryDelay = retryDelay
        self.retryCount = retryCount
  
     def InvokeMethod(self, mo, info, args):
        # This retry logic is replicated in InvokeAccessor and the two copies need
        # to be in sync
        retriesLeft = self.retryCount
        while retriesLeft > 0:
           try:
              if self.state == self.STATE_UNAUTHENTICATED:
                 self._CallLoginMethod()
              # Invoke the method
              status, obj = self.soapStub.InvokeMethod(mo, info, args, self)
           except (socket.error, http_client.HTTPException, ExpatError):
              if self.retryDelay and retriesLeft:
                 time.sleep(self.retryDelay)
              retriesLeft -= 1
              continue
  
           if status == 200:
              # Normal return from the server, return the object to the caller.
              return obj
  
           # An exceptional return from the server
           if isinstance(obj, self.SESSION_EXCEPTIONS):
              # Our session might've timed out, change our state and retry.
              self._SetStateUnauthenticated()
           else:
              # It's an exception from the method that was called, send it up.
              raise obj
  
        # Raise any socket/httplib errors caught above.
        raise SystemError()
  
     ## Retrieve a managed property
     #
     # @param self self
     # @param mo managed object
     # @param info property info
     def InvokeAccessor(self, mo, info):
        # This retry logic is replicated in InvokeMethod and the two copies need
        # to be in sync
        retriesLeft = self.retryCount
        while retriesLeft > 0:
           try:
              if self.state == self.STATE_UNAUTHENTICATED:
                 self._CallLoginMethod()
              # Invoke the method
              obj = StubAdapterBase.InvokeAccessor(self, mo, info)
           except (socket.error, http_client.HTTPException, ExpatError):
              if self.retryDelay and retriesLeft:
                 time.sleep(self.retryDelay)
              retriesLeft -= 1
              continue
           except Exception as e:
              if isinstance(e, self.SESSION_EXCEPTIONS):
                 # Our session might've timed out, change our state and retry.
                 self._SetStateUnauthenticated()
                 retriesLeft -= 1
                 continue
              else:
                 raise e
           return obj
        # Raise any socket/httplib errors caught above.
        raise SystemError()
  
     ## Handle the login method call
     #
     #  This method calls the login method on the soap stub and changes the state
     #  to authenticated
     def _CallLoginMethod(self):
        try:
           self.lock.acquire()
           if self.state == self.STATE_UNAUTHENTICATED:
              self.loginMethod(self.soapStub)
              self.state = self.STATE_AUTHENTICATED
        finally:
           self.lock.release()
  
     ## Change the state to unauthenticated
     def _SetStateUnauthenticated(self):
        self.lock.acquire()
        if self.state == self.STATE_AUTHENTICATED:
           self.state = self.STATE_UNAUTHENTICATED
        self.lock.release()
  