Support data access via env vars

This patch adds support for accessing data via environment variables
instead of directly with vmtoolsd. This is to support simulated testing
with the vCenter simulator.

To use this datasource with environment variables simply define
"VMX_GUESTINFO=1" and then define environment variables for keys with
the "VMX_GUESTINFO_" prefix, like so:

    "guestinfo.userdata"          -> "VMX_GUESTINFO_USERDATA"
    "guestinfo.userdata.encoding" -> "VMX_GUESTINFO_USERDATA_ENCODING"
diff --git a/.gitignore b/.gitignore
index 2d0bb33..8ee179a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,6 @@
 /metadata.json
 /test.sh
 /userdata.yaml
-.DS_Store
\ No newline at end of file
+.DS_Store
+*.pyc
+/__pycache__
\ No newline at end of file
diff --git a/DataSourceVMwareGuestInfo.py b/DataSourceVMwareGuestInfo.py
index c9db71d..9fe83ac 100644
--- a/DataSourceVMwareGuestInfo.py
+++ b/DataSourceVMwareGuestInfo.py
@@ -23,7 +23,9 @@
 import copy
 from distutils.spawn import find_executable
 import json
+import os
 import socket
+import string
 import zlib
 
 from cloudinit import log as logging
@@ -37,6 +39,7 @@
 LOG = logging.getLogger(__name__)
 NOVAL = "No value found"
 VMTOOLSD = find_executable("vmtoolsd")
+VMX_GUESTINFO = "VMX_GUESTINFO"
 
 
 class NetworkConfigError(Exception):
@@ -90,7 +93,7 @@
 
     def __init__(self, sys_cfg, distro, paths, ud_proc=None):
         sources.DataSource.__init__(self, sys_cfg, distro, paths, ud_proc)
-        if not VMTOOLSD:
+        if not get_data_access_method():
             LOG.error("Failed to find vmtoolsd")
 
     def get_data(self):
@@ -103,7 +106,7 @@
         that the get_data functions in newer versions of cloud-init do,
         such as calling persist_instance_data.
         """
-        if not VMTOOLSD:
+        if not get_data_access_method():
             LOG.error("vmtoolsd is required to fetch guestinfo value")
             return False
 
@@ -231,24 +234,37 @@
     Returns a guestinfo value for the specified key.
     '''
     LOG.debug("Getting guestinfo value for key %s", key)
-    try:
-        (stdout, stderr) = util.subp(
-            [VMTOOLSD, "--cmd", "info-get guestinfo." + key])
-        if stderr == NOVAL:
-            LOG.debug("No value found for key %s", key)
-        elif not stdout:
-            LOG.error("Failed to get guestinfo value for key %s", key)
-        else:
-            return stdout.rstrip()
-    except util.ProcessExecutionError as error:
-        if error.stderr == NOVAL:
+
+    data_access_method = get_data_access_method()
+
+    if data_access_method == VMX_GUESTINFO:
+        env_key = ("vmx.guestinfo." + key).upper().replace(".", "_", -1)
+        val = os.environ.get(env_key, "")
+        if val == "":
             LOG.debug("No value found for key %s", key)
         else:
+            return val
+
+    if data_access_method == VMTOOLSD:
+        try:
+            (stdout, stderr) = util.subp(
+                [VMTOOLSD, "--cmd", "info-get guestinfo." + key])
+            if stderr == NOVAL:
+                LOG.debug("No value found for key %s", key)
+            elif not stdout:
+                LOG.error("Failed to get guestinfo value for key %s", key)
+            else:
+                return stdout.rstrip()
+        except util.ProcessExecutionError as error:
+            if error.stderr == NOVAL:
+                LOG.debug("No value found for key %s", key)
+            else:
+                util.logexc(
+                    LOG, "Failed to get guestinfo value for key %s: %s", key, error)
+        except Exception:
             util.logexc(
-                LOG, "Failed to get guestinfo value for key %s: %s", key, error)
-    except Exception:
-        util.logexc(
-            LOG, "Unexpected error while trying to get guestinfo value for key %s", key)
+                LOG, "Unexpected error while trying to get guestinfo value for key %s", key)
+
     return None
 
 
@@ -397,6 +413,8 @@
     return ipv4, ipv6
 
 # patched socket.getfqdn() - see https://bugs.python.org/issue5004
+
+
 def getfqdn(name=''):
     """Get fully qualified domain name from name.
      An empty argument is interpreted as meaning the local host.
@@ -405,7 +423,8 @@
     if not name or name == '0.0.0.0':
         name = socket.gethostname()
     try:
-        addrs = socket.getaddrinfo(name, None, 0, socket.SOCK_DGRAM, 0, socket.AI_CANONNAME)
+        addrs = socket.getaddrinfo(
+            name, None, 0, socket.SOCK_DGRAM, 0, socket.AI_CANONNAME)
     except socket.error:
         pass
     else:
@@ -415,6 +434,7 @@
                 break
     return name
 
+
 def get_host_info():
     '''
     Returns host information such as the host name and network interfaces.
@@ -494,6 +514,14 @@
     return host_info
 
 
+def get_data_access_method():
+    if os.environ.get(VMX_GUESTINFO, ""):
+        return VMX_GUESTINFO
+    if VMTOOLSD:
+        return VMTOOLSD
+    return None
+
+
 def main():
     '''
     Executed when this file is used as a program.