Package: python3-pyvmomi
Version: 6.7.1-4
Severity: important
Tags: patch


Running ansible vmware_guest module in debian stable fails with this error:

    AttributeError: module 'pyVmomi.VmomiSupport' has no attribute 'VmomiJSONEncoder'

Actually the vmware_guest module in stable cannot be used and version 6.7.1-6 in testing and sid is still too old, as support for JSON encoding in vmomi is tracked in pyvmomi #732[1] and solved in v6.7.1.2018.12 Bug Fix release.

It would be nice if python3-pyvmomi can be upgraded to a ever newer version (now is at 8.0U1 patch 2), but I include a patch with differences in file pyVmomi/VmomiSupport between version v6.7.1.2018.12 and v6.7.1, if you want to stay minimal (in the upstream diff there are also many other changes in docs and examples that aren't related to this bug). I tested it in my environment and it works for me.


-- System Information:
Debian Release: 12.1
  APT prefers stable-updates
  APT policy: (500, 'stable-updates'), (500, 'stable-security'), (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 6.1.0-12-amd64 (SMP w/1 CPU thread; PREEMPT)
Locale: LANG=it_IT.UTF-8, LC_CTYPE=it_IT.UTF-8 (charmap=UTF-8), LANGUAGE not set
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

Versions of packages python3-pyvmomi depends on:
ii  dpkg              1.21.22
ii  python3           3.11.2-1+b1
ii  python3-requests  2.28.1+dfsg-1
ii  python3-six       1.16.0-4

python3-pyvmomi recommends no packages.

Versions of packages python3-pyvmomi suggests:
pn  python-pyvmomi-doc  <none>

-- no debconf information



Best regards.

Paolo


1. https://github.com/vmware/pyvmomi/pull/732
diff --git a/pyVmomi/VmomiSupport.py b/pyVmomi/VmomiSupport.py
index 925d4a9..b3b7878 100644
--- a/pyVmomi/VmomiSupport.py
+++ b/pyVmomi/VmomiSupport.py
@@ -1,5 +1,5 @@
 # VMware vSphere Python SDK
-# Copyright (c) 2008-2016 VMware, Inc. All Rights Reserved.
+# Copyright (c) 2008-2018 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.
@@ -27,6 +27,7 @@ from six import PY3
 from datetime import datetime
 from pyVmomi import Iso8601
 import base64
+import json
 import threading
 if PY3:
    from functools import cmp_to_key
@@ -273,6 +274,131 @@ class LazyModule(object):
       else:
          raise AttributeError("'%s' does not exist" % self.name)
 
+
+class VmomiJSONEncoder(json.JSONEncoder):
+    """
+        Custom JSON encoder to encode vSphere objects.
+
+        When a ManagedObject is encoded, it gains three properties:
+            _vimid is the _moId (ex: 'vm-42')
+            _vimref is the moRef (ex: 'vim.VirtualMachine:vm-42')
+            _vimtype is the class name (ex: 'vim.VirtualMachine')
+
+        When a DataObject is encoded, it gains one property:
+            _vimtype is the class name (ex: 'vim.VirtualMachineQuestionInfo')
+
+        If the dynamicProperty and dynamicType are empty, they are optionally
+            omitted from the results of DataObjects and ManagedObjects
+
+        @example "Explode only the object passed in"
+            data = json.dumps(vm, cls=VmomiJSONEncoder)
+
+        @example "Explode specific objects"
+            data = json.dumps(vm, cls=VmomiJSONEncoder,
+                              explode=[vm, vm.network[0]])
+
+        @example "Explode all virtual machines in a list and their snapshots"
+            data = json.dumps([vm1, vm2], cls=VmomiJSONEncoder,
+                              explode=[templateOf('VirtualMachine'),
+                                       templateOf('VirtualMachineSnapshot')])
+    """
+    def __init__(self, *args, **kwargs):
+        """
+        Consumer must specify what types to explode into full content
+        and what types to leave as a ManagedObjectReference.  If the
+        root object of the encoding is a ManagedObject, it will be
+        added to the explode list automatically.
+
+        A ManagedObject is only exploded once, the first time it is
+        encountered.  All subsequent times it will be a moRef.
+
+        @param explode (list) A list of objects and/or types to
+            expand in the conversion process.  Directly add any
+            objects to the list, or add the type of any object
+            using type(templateOf('VirtualMachine')) for example.
+
+        @param strip_dynamic (bool) Every object normally contains
+            a dynamicProperty list and a dynamicType field even if
+            the contents are [] and None respectively.  These fields
+            clutter the output making it more difficult for a person
+            to read and bloat the size of the result unnecessarily.
+            This option removes them if they are the empty values above.
+            The default is False.
+        """
+        self._done = set()
+        self._first = True
+        self._explode = kwargs.pop('explode', None)
+        self._strip_dynamic = kwargs.pop('strip_dynamic', False)
+        super(VmomiJSONEncoder, self).__init__(*args, **kwargs)
+
+    def _remove_empty_dynamic_if(self, result):
+        if self._strip_dynamic:
+            if 'dynamicProperty' in result and len(result['dynamicProperty']) 
== 0:
+                result.pop('dynamicProperty') 
+            if 'dynamicType' in result and not result['dynamicType']:
+                result.pop('dynamicType')
+        return result
+
+    def default(self, obj):  # pylint: disable=method-hidden
+        if self._first:
+            self._first = False
+            if not self._explode:
+                self._explode = []
+            if isinstance(obj, ManagedObject):
+                self._explode.append(obj)
+        if isinstance(obj, ManagedObject):
+            if self.explode(obj):
+                result = {}
+                result['_vimid'] = obj._moId
+                result['_vimref'] = '{}:{}'.format(obj.__class__.__name__,
+                                                   obj._moId)
+                result['_vimtype'] = obj.__class__.__name__
+                for prop in obj._GetPropertyList():
+                    result[prop.name] = getattr(obj, prop.name)
+                return self._remove_empty_dynamic_if(result)
+            return str(obj).strip("'")  # see FormatObject below
+        if isinstance(obj, DataObject):
+            result = {k:v for k,v in obj.__dict__.items()}
+            result['_vimtype'] = obj.__class__.__name__
+            return self._remove_empty_dynamic_if(result)
+        if isinstance(obj, binary):
+            result = base64.b64encode(obj)
+            if PY3:  # see FormatObject below
+                result = str(result, 'utf-8')
+            return result
+        if isinstance(obj, datetime):
+            return Iso8601.ISO8601Format(obj)
+        if isinstance(obj, UncallableManagedMethod):
+            return obj.name
+        if isinstance(obj, ManagedMethod):
+            return str(obj)  # see FormatObject below
+        if isinstance(obj, type):
+            return obj.__name__
+        super(VmomiJSONEncoder, self).default(obj)
+
+    def explode(self, obj):
+        """ Determine if the object should be exploded. """
+        if obj in self._done:
+            return False
+        result = False
+        for item in self._explode:
+            if hasattr(item, '_moId'):
+                # If it has a _moId it is an instance
+                if obj._moId == item._moId:
+                    result = True
+            else:
+                # If it does not have a _moId it is a template
+                if obj.__class__.__name__ == item.__name__:
+                    result = True
+        if result:
+            self._done.add(obj)
+        return result
+
+
+def templateOf(typestr):
+    """ Returns a class template. """
+    return GetWsdlType(XMLNS_VMODL_BASE, typestr)
+
 ## Format a python VMOMI object
 #
 # @param val the object
@@ -1325,7 +1451,14 @@ double = type("double", (float,), {})
 if PY3:
    long = type("long", (int,), {})
 URI = type("URI", (str,), {})
-binary = type("binary", (binary_type,), {})
+if not PY3:
+    # six defines binary_type in python2 as a string; this means the
+    # JSON encoder sees checksum properties as strings and attempts
+    # to perform utf-8 decoding on them because they contain high-bit
+    # characters.
+    binary = type("binary", (bytearray,), {})
+else:
+    binary = type("binary", (binary_type,), {})
 PropertyPath = type("PropertyPath", (text_type,), {})
 
 # _wsdlTypeMapNSs store namespaces added to _wsdlTypeMap in _SetWsdlType

Reply via email to