From f3245a6c33714b05451e5c6562671828733d6244 Mon Sep 17 00:00:00 2001 From: tianhao he Date: Fri, 20 May 2016 12:24:17 -0700 Subject: [PATCH 1/2] Fix issue #182 by creating an uncallable managed method VC is returning some internal managed method in 6.0. Looks like this issue will be resolved in 6.5 server types as it will be public in 6.5. To tempoarily workaound this issue, returning a dummy method and dummy obj instead of throwing exception when client side can't understand the returned managed method. --- pyVmomi/SoapAdapter.py | 5 ++- pyVmomi/VmomiSupport.py | 11 +++++ tests/files/unknown_method.xml | 78 ++++++++++++++++++++++++++++++++++ tests/test_deserializer.py | 33 ++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 tests/files/unknown_method.xml create mode 100644 tests/test_deserializer.py 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..3f4d94d 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 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() + From 0f4a4e5895c075040f33809213b4cc0b212e93f6 Mon Sep 17 00:00:00 2001 From: tianhao he Date: Mon, 6 Jun 2016 08:56:11 -0700 Subject: [PATCH 2/2] Try to query wsdl type/method from vim25 namespace first when namespace is unknown. Some type, in this case 'Task' is not defined in the specified namespace(urn:nfc). In such case, the deserializer will have to guess the namespace by interate namesapce in the _wsdlTypeMapNSs. 'Task' exists in multiple namespaces. In python2, the order in the map is fixed and vim25 is always tried first, but in python3 the order of the set is random. If wrong namespace is returned, the xml parser will fail later on. --- pyVmomi/VmomiSupport.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/pyVmomi/VmomiSupport.py b/pyVmomi/VmomiSupport.py index 3f4d94d..925d4a9 100644 --- a/pyVmomi/VmomiSupport.py +++ b/pyVmomi/VmomiSupport.py @@ -1049,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) @@ -1242,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)