diff --git a/pyVmomi/SoapAdapter.py b/pyVmomi/SoapAdapter.py index 8f88071..658c2bb 100644 --- a/pyVmomi/SoapAdapter.py +++ b/pyVmomi/SoapAdapter.py @@ -734,7 +734,10 @@ class SoapDeserializer(ExpatDeserializerNSHandlers): # val in Method val is not namespace qualified # However, this doesn't hurt to strip out namespace ns, name = self.GetNSAndWsdlname(data) - obj = GuessWsdlMethod(name) + try: + obj = GuessWsdlMethod(name) + except KeyError: + obj = UncallableManagedMethod(name) elif obj is bool: if data == "0" or data.lower() == "false": obj = bool(False) diff --git a/pyVmomi/VmomiSupport.py b/pyVmomi/VmomiSupport.py index 5a9afee..925d4a9 100644 --- a/pyVmomi/VmomiSupport.py +++ b/pyVmomi/VmomiSupport.py @@ -307,6 +307,8 @@ def FormatObject(val, info=Object(name="", type=object, flags=0), indent=0): result = "(%s) []" % itemType.__name__ elif isinstance(val, type): result = val.__name__ + elif isinstance(val, UncallableManagedMethod): + result = val.name elif isinstance(val, ManagedMethod): result = '%s.%s' % (val.info.type.__name__, val.info.name) elif isinstance(val, bool): @@ -588,6 +590,15 @@ class ManagedMethod(Curry): Curry.__init__(self, ManagedObject._InvokeMethod, info) self.info = info +# Method used to represent any unknown wsdl method returned by server response. +# Server may return unknown method name due to server defects or newer version. +class UncallableManagedMethod(ManagedMethod): + def __init__(self, name): + self.name = name + + def __call__(self, *args, **kwargs): + raise Exception("Managed method {} is not available".format(self.name)) + ## Create the vmodl.MethodFault type # # This type must be generated dynamically because it inherits from @@ -1038,9 +1049,15 @@ class UnknownWsdlTypeError(KeyError): # @return type if found in any one of the name spaces else throws KeyError def GuessWsdlType(name): with _lazyLock: - # Because the types are lazily loaded, if some name is present - # in multiple namespaces, we will load the first type that we - # encounter and return it. + # Some types may exist in multiple namespaces, and returning + # the wrong one will cause a deserialization error. + # Since in python3 the order of entries in set is not deterministic, + # we will try to get the type from vim25 namespace first. + try: + return GetWsdlType(XMLNS_VMODL_BASE, name) + except KeyError: + pass + for ns in _wsdlTypeMapNSs: try: return GetWsdlType(ns, name) @@ -1231,6 +1248,15 @@ def GetWsdlMethod(ns, wsdlName): # KeyError def GuessWsdlMethod(name): with _lazyLock: + # Some methods may exist in multiple namespaces, and returning + # the wrong one will cause a deserialization error. + # Since in python3 the order of entries in set is not deterministic, + # we will try to get the method from vim25 namespace first. + try: + return GetWsdlMethod(XMLNS_VMODL_BASE, name) + except KeyError: + pass + for ns in _wsdlMethodNSs: try: return GetWsdlMethod(ns, name) diff --git a/tests/files/unknown_method.xml b/tests/files/unknown_method.xml new file mode 100644 index 0000000..1521299 --- /dev/null +++ b/tests/files/unknown_method.xml @@ -0,0 +1,78 @@ + + + + + + task-3996443 + task-3996443 + + com.vmware.vdp2.task.description + + workOrderId + cp.20141020150650 + + + com.vmware.vdp2.integritycheck + vm-133364 + VDP3 + running + false + true + + vmware\\sa-vdp3 + + 2014-10-20T15:09:08.789999Z + 2014-10-20T15:09:09.073Z + 21691195 + + + task-1178117 + task-1178117 + com.vmware.vcIntegrity.CheckNotificationTask + group-d1 + Datacenters + queued + false + true + + VMware vSphere Update Manager Check Notification + schedule-2 + + 2013-01-08T14:12:01.556999Z + 3410272 + + + task-746590 + task-746590 + LeaseMapDiskRegion + host.DiskManager.Lease.MapDiskRegion + vm-982 + Applicure + queued + false + false + + VMWARE\\sa-veeam1 + + 2012-09-27T12:03:03.412999Z + 2325872 + + + task-56327 + task-56327 + LeaseMapDiskRegion + host.DiskManager.Lease.MapDiskRegion + vm-1010 + vCD-Oracle + queued + false + false + + VMWARE\\sa-veeam1 + + 2012-02-27T10:56:50.656999Z + 345916 + + + + \ No newline at end of file diff --git a/tests/test_deserializer.py b/tests/test_deserializer.py new file mode 100644 index 0000000..0229ff9 --- /dev/null +++ b/tests/test_deserializer.py @@ -0,0 +1,33 @@ +# VMware vSphere Python SDK +# Copyright (c) 2016 VMware, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pyVmomi import vim +from pyVmomi.SoapAdapter import SoapStubAdapter, SoapResponseDeserializer +import unittest + + +class DeserializerTests(unittest.TestCase): + + def test_deserialize_unknown_managed_method(self): + with open('tests/files/unknown_method.xml', 'rb') as f: + data = f.read() + stub = SoapStubAdapter(version="vim.version.version6") + deserializer = SoapResponseDeserializer(stub) + result = vim.TaskHistoryCollector._GetMethodInfo("ReadNext").result + obj = deserializer.Deserialize(data, result) + print(obj) + with self.assertRaisesRegexp(Exception, "Managed method LeaseMapDiskRegion is not available"): + obj[-1].name() +