blob: 60b68dd2643c872bad7305d201e18f9f94d9f898 [file] [log] [blame]
akutz77457a62018-08-22 16:07:21 -05001# vi: ts=4 expandtab
2#
3# Copyright (C) 2017 VMware Inc.
4#
akutz6501f902018-08-24 12:19:05 -05005# Authors: Anish Swaminathan <anishs@vmware.com>
6# Andrew Kutz <akutz@vmware.com>
akutz77457a62018-08-22 16:07:21 -05007#
8import os
9import base64
akutz6501f902018-08-24 12:19:05 -050010import zlib
11import json
akutz77457a62018-08-22 16:07:21 -050012
13from cloudinit import log as logging
14from cloudinit import sources
15from cloudinit import util
akutz6501f902018-08-24 12:19:05 -050016from cloudinit import safeyaml
akutz77457a62018-08-22 16:07:21 -050017
18from distutils.spawn import find_executable
19
20LOG = logging.getLogger(__name__)
21
akutz6501f902018-08-24 12:19:05 -050022# Used with CentOS 7 which installs cloud-init 0.7.9. The URL for
23# the DataSource class is http://bit.ly/cloudinit-datasource-0-7-9.
24#
25# Setting the hostname:
26# The hostname is set by way of the metadata key "local-hostname".
27#
28# Setting the instance ID:
29# The instance ID may be set by way of the metadata key "instance-id".
30# However, if this value is absent then then the instance ID is
31# read from the file /sys/class/dmi/id/product_uuid.
32#
33# Configuring the network:
34# The network is configured by setting the metadata key "network"
35# with a value consistent with Network Config Versions 1 or 2,
36# depending on the Linux distro's version of cloud-init:
37#
38# Network Config Version 1 - http://bit.ly/cloudinit-net-conf-v1
39# Network Config Version 2 - http://bit.ly/cloudinit-net-conf-v2
40#
41# For example, CentOS 7's official cloud-init package is version
42# 0.7.9 and does not support Network Config Version 2. However,
43# this datasource still supports supplying Network Config Version 2
44# data as long as the Linux distro's cloud-init package is new
45# enough to parse the data.
46#
47# The metadata key "network.encoding" may be used to indicate the
48# format of the metadata key "network". Valid encodings are base64
49# and gzip+base64.
akutz77457a62018-08-22 16:07:21 -050050class DataSourceVmxGuestinfo(sources.DataSource):
51 def __init__(self, sys_cfg, distro, paths, ud_proc=None):
52 sources.DataSource.__init__(self, sys_cfg, distro, paths, ud_proc)
akutz77457a62018-08-22 16:07:21 -050053 self.vmtoolsd = find_executable("vmtoolsd")
54 if not self.vmtoolsd:
55 LOG.error("Failed to find vmtoolsd")
56
57 def get_data(self):
58 if not self.vmtoolsd:
59 LOG.error("vmtoolsd is required to fetch guestinfo value")
60 return False
akutz6501f902018-08-24 12:19:05 -050061
62 # Get the JSON metadata. Can be plain-text, base64, or gzip+base64.
63 metadata = self._get_encoded_guestinfo_data('metadata')
64 if metadata:
65 self.metadata = json.loads(metadata)
66
67 # Get the YAML userdata. Can be plain-text, base64, or gzip+base64.
68 self.userdata_raw = self._get_encoded_guestinfo_data('userdata')
69
70 # Get the YAML vendordata. Can be plain-text, base64, or gzip+base64.
71 self.vendordata_raw = self._get_encoded_guestinfo_data('vendordata')
72
akutz77457a62018-08-22 16:07:21 -050073 return True
74
akutz6501f902018-08-24 12:19:05 -050075 @property
76 def network_config(self):
77 # Pull the network configuration out of the metadata.
78 if self.metadata and 'network' in self.metadata:
79 data = self._get_encoded_metadata('network')
80 if data:
81 # Load the YAML-formatted network data into an object
82 # and return it.
83 net_config = safeyaml.load(data)
84 LOG.debug("Loaded network config: %s", net_config)
85 return net_config
86 return None
akutz77457a62018-08-22 16:07:21 -050087
88 def get_instance_id(self):
akutz6501f902018-08-24 12:19:05 -050089 # Pull the instance ID out of the metadata if present. Otherwise
90 # read the file /sys/class/dmi/id/product_uuid for the instance ID.
91 if self.metadata and 'instance-id' in self.metadata:
92 return self.metadata['instance-id']
akutz77457a62018-08-22 16:07:21 -050093 with open('/sys/class/dmi/id/product_uuid', 'r') as id_file:
94 return str(id_file.read()).rstrip()
95
akutz6501f902018-08-24 12:19:05 -050096 def _get_encoded_guestinfo_data(self, key):
97 data = self._get_guestinfo_value(key)
98 if not data:
99 return None
100 enc_type = self._get_guestinfo_value(key + '.encoding')
101 return self._get_encoded_data('guestinfo.' + key, enc_type, data)
102
103 def _get_encoded_metadata(self, key):
104 if not self.metadata or not key in self.metadata:
105 return None
106 data = self.metadata[key]
107 enc_type = self.metadata.get(key + '.encoding')
108 return self._get_encoded_data('metadata.' + key, enc_type, data)
109
110 def _get_encoded_data(self, key, enc_type, data):
111 LOG.debug("Getting encoded data for key=%s, enc=%s", key, enc_type)
112 if enc_type == "gzip+base64" or enc_type == "gz+b64":
113 LOG.debug("Decoding %s format %s", enc_type, key)
114 return zlib.decompress(base64.b64decode(data), zlib.MAX_WBITS | 16)
115 elif enc_type == "base64" or enc_type == "b64":
116 LOG.debug("Decoding %s format %s", enc_type, key)
117 return base64.b64decode(data)
118 else:
119 LOG.debug("Plain-text data %s", key)
120 return data
121
122 def _get_guestinfo_value(self, key):
123 NOVAL = "No value found"
124 LOG.debug("Getting guestinfo value for key %s", key)
125 try:
126 (stdout, stderr) = util.subp([self.vmtoolsd, "--cmd", "info-get guestinfo." + key])
127 if stderr == NOVAL:
128 LOG.debug("No value found for key %s", key)
129 elif not stdout:
130 LOG.error("Failed to get guestinfo value for key %s", key)
131 else:
132 return stdout.rstrip()
133 except util.ProcessExecutionError as error:
134 if error.stderr == NOVAL:
135 LOG.debug("No value found for key %s", key)
136 else:
137 util.logexc(LOG,"Failed to get guestinfo value for key %s: %s", key, error)
138 except Exception:
139 util.logexc(LOG,"Unexpected error while trying to get guestinfo value for key %s", key)
140 return None
141
akutz77457a62018-08-22 16:07:21 -0500142def get_datasource_list(depends):
143 """
144 Return a list of data sources that match this set of dependencies
145 """
akutz6501f902018-08-24 12:19:05 -0500146 return [DataSourceVmxGuestinfo]