Skip to content

Commit 5388c51

Browse files
committed
add the vmware guestinfo service
1 parent 41161e5 commit 5388c51

File tree

5 files changed

+174
-1
lines changed

5 files changed

+174
-1
lines changed

cloudbaseinit/conf/default.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ def __init__(self, config):
162162
default=[
163163
'cloudbaseinit.metadata.services.httpservice.HttpService',
164164
'cloudbaseinit.metadata.services'
165+
'.vmwareguestinfoservice.VMwareGuestInfoService',
166+
'cloudbaseinit.metadata.services'
165167
'.configdrive.ConfigDriveService',
166168
'cloudbaseinit.metadata.services.ec2service.EC2Service',
167169
'cloudbaseinit.metadata.services'

cloudbaseinit/conf/factory.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
'cloudbaseinit.conf.openstack.OpenStackOptions',
2424
'cloudbaseinit.conf.azure.AzureOptions',
2525
'cloudbaseinit.conf.ovf.OvfOptions',
26+
'cloudbaseinit.conf.vmwareguestinfo.VMwareGuestInfoConfigOptions',
2627
)
2728

2829

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Copyright 2019 ruilopes.com
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
"""Config options available for the VMwareGuestInfo metadata service."""
16+
17+
from oslo_config import cfg
18+
19+
from cloudbaseinit.conf import base as conf_base
20+
21+
class VMwareGuestInfoConfigOptions(conf_base.Options):
22+
"""Config options available for the VMwareGuestInfo metadata service."""
23+
24+
def __init__(self, config):
25+
super(VMwareGuestInfoConfigOptions, self).__init__(config, group='vmwareguestinfo')
26+
self._options = [
27+
cfg.StrOpt(
28+
'rpctool_path',
29+
default='C:/Program Files/VMware/VMware Tools/rpctool.exe',
30+
help='VMware Tools rpctool.exe path'),
31+
]
32+
33+
def register(self):
34+
"""Register the current options to the global ConfigOpts object."""
35+
group = cfg.OptGroup(self.group_name, title="VMwareGuestInfo Config Options")
36+
self._config.register_group(group)
37+
self._config.register_opts(self._options, group=group)
38+
39+
def list(self):
40+
"""Return a list which contains all the available options."""
41+
return self._options
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Copyright 2019 ruilopes.com
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
import base64
16+
import gzip
17+
import io
18+
import json
19+
import os
20+
21+
from cloudbaseinit import conf as cloudbaseinit_conf
22+
from cloudbaseinit import exception
23+
from cloudbaseinit.metadata.services import base
24+
from cloudbaseinit.osutils import factory as osutils_factory
25+
26+
CONF = cloudbaseinit_conf.CONF
27+
28+
class VMwareGuestInfoService(base.BaseMetadataService):
29+
"""
30+
This uses the VMware Guest Info interface to obtain the cloud init data
31+
from the VM extraconfig guestinfo properties using the VMware Tools
32+
rpctool cli application.
33+
34+
You can use the following guestinfo properties:
35+
36+
+--------------------+------------------------------------------------------+
37+
| property | description |
38+
+--------------------+------------------------------------------------------+
39+
| guestinfo.metadata | A JSON document containing the cloud-init metadata. |
40+
| guestinfo.userdata | A YAML document containing the cloud-init userdata, |
41+
| | or a MIME multipart message with the YAML document |
42+
| | containing the cloud-init user data and custom |
43+
| | scripts as described at doc/source/userdata.rst |
44+
+--------------------+------------------------------------------------------+
45+
46+
When using terraform, you can create a cloud init configuration with:
47+
48+
# a cloud-init userdata.
49+
# see https://www.terraform.io/docs/providers/template/d/cloudinit_config.html
50+
# see https://www.terraform.io/docs/configuration/expressions.html#string-literals
51+
data "template_cloudinit_config" "example" {
52+
part {
53+
content_type = "text/cloud-config"
54+
content = <<-EOF
55+
#cloud-config
56+
set_hostname: example
57+
EOF
58+
}
59+
}
60+
61+
# see https://www.terraform.io/docs/providers/vsphere/r/virtual_machine.html
62+
resource "vsphere_virtual_machine" "example" {
63+
...
64+
# NB this extra_config data ends-up inside the VM .vmx file and will be
65+
# exposed by cloudbase-init as a cloud-init datasource.
66+
extra_config = {
67+
"guestinfo.metadata" = base64gzip(jsonencode({
68+
"admin-username": var.winrm_username,
69+
"admin-password": var.winrm_password,
70+
"public-keys": [trimspace(file("~/.ssh/id_rsa.pub"))],
71+
}))
72+
"guestinfo.userdata" = data.template_cloudinit_config.example.rendered
73+
}
74+
...
75+
}
76+
77+
NB The base image must have the VMware Tools installed.
78+
"""
79+
80+
def __init__(self):
81+
super(VMwareGuestInfoService, self).__init__()
82+
self._meta_data = None
83+
self._user_data = None
84+
self._os_utils = osutils_factory.get_os_utils()
85+
86+
def load(self):
87+
super(VMwareGuestInfoService, self).load()
88+
if not os.path.exists(CONF.vmwareguestinfo.rpctool_path):
89+
return False
90+
self._meta_data = json.loads(self._get_guestinfo_value('metadata') or '{}')
91+
self._user_data = self._get_guestinfo_value('userdata')
92+
return True
93+
94+
def _get_guestinfo_value(self, name):
95+
stdout, stderr, exit_code = self._os_utils.execute_process([
96+
CONF.vmwareguestinfo.rpctool_path,
97+
'info-get guestinfo.%s' % name
98+
])
99+
if exit_code:
100+
raise exception.CloudbaseInitException(
101+
'Failed to execute "%(rpctool_path)s" with '
102+
'exit code: %(exit_code)s\nstdout: %(stdout)s\nstderr: %(stderr)s' % {
103+
'rpctool_path': CONF.vmwareguestinfo.rpctool_path,
104+
'exit_code': exit_code,
105+
'stdout': stdout,
106+
'stderr': stderr})
107+
data = base64.b64decode(stdout)
108+
if data[:2] == self._GZIP_MAGIC_NUMBER:
109+
with gzip.GzipFile(fileobj=io.BytesIO(data), mode='rb') as out:
110+
data = out.read()
111+
return data
112+
113+
def _get_data(self, path):
114+
pass
115+
116+
def get_user_data(self):
117+
return self._user_data
118+
119+
def get_host_name(self):
120+
return self._meta_data.get('local-hostname')
121+
122+
def get_public_keys(self):
123+
return self._meta_data.get('public-keys')
124+
125+
def get_admin_username(self):
126+
return self._meta_data.get('admin-username')
127+
128+
def get_admin_password(self):
129+
return self._meta_data.get('admin-password')

cloudbaseinit/plugins/common/sshpublickeys.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def execute(self, service, shared_data):
3434
LOG.debug('Public keys not found in metadata')
3535
return base.PLUGIN_EXECUTION_DONE, False
3636

37-
username = CONF.username
37+
username = service.get_admin_username() or CONF.username
3838

3939
osutils = osutils_factory.get_os_utils()
4040
user_home = osutils.get_user_home(username)

0 commit comments

Comments
 (0)