Skip to content

Commit 395053f

Browse files
committed
add the vmware guestinfo service
1 parent 41161e5 commit 395053f

File tree

5 files changed

+168
-1
lines changed

5 files changed

+168
-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+
'.vmwareguestinfo.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 cloudconfig
20+
21+
class VMwareGuestInfoConfigOptions(cloudconfig.CloudConfigOptions):
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: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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 os
19+
20+
from cloudbaseinit import conf as cloudbaseinit_conf
21+
from cloudbaseinit import exception
22+
from cloudbaseinit.metadata.services import base
23+
from cloudbaseinit.osutils import factory as osutils_factory
24+
25+
CONF = cloudbaseinit_conf.CONF
26+
27+
class VMwareGuestInfoService(base.BaseMetadataService):
28+
"""
29+
This uses the VMware Guest Info interface to obtain the cloud init data
30+
from the VM extraconfig guestinfo properties using the VMware Tools
31+
rpctool cli application.
32+
33+
You can use the following guestinfo properties:
34+
35+
+--------------------+------------------------------------------------------+
36+
| property | description |
37+
+--------------------+------------------------------------------------------+
38+
| guestinfo.metadata | A JSON document containing the cloud-init metadata. |
39+
| guestinfo.userdata | A YAML document containing the cloud-init userdata, |
40+
| | or a MIME multipart message with the YAML document |
41+
| | containing the cloud-init user data and custom |
42+
| | scripts as described at doc/source/userdata.rst |
43+
+--------------------+------------------------------------------------------+
44+
45+
When using terraform, you can create a cloud init configuration with:
46+
47+
# a cloud-init userdata.
48+
# see https://www.terraform.io/docs/providers/template/d/cloudinit_config.html
49+
# see https://www.terraform.io/docs/configuration/expressions.html#string-literals
50+
data "template_cloudinit_config" "example" {
51+
gzip = true
52+
base64_encode = true
53+
part {
54+
content_type = "text/cloud-config"
55+
content = <<-EOF
56+
#cloud-config
57+
set_hostname: example
58+
EOF
59+
}
60+
}
61+
62+
# see https://www.terraform.io/docs/providers/vsphere/r/virtual_machine.html
63+
resource "vsphere_virtual_machine" "example" {
64+
...
65+
# NB this extra_config data ends-up inside the VM .vmx file and will be
66+
# exposed by cloudbase-init as a cloud-init datasource.
67+
extra_config = {
68+
"guestinfo.userdata" = data.template_cloudinit_config.example.rendered
69+
"guestinfo.metadata" = base64gzip(jsonencode({
70+
"admin-username": var.winrm_username,
71+
"admin-password": var.winrm_password,
72+
"public-keys": [trimspace(file("~/.ssh/id_rsa.pub"))],
73+
}))
74+
}
75+
...
76+
}
77+
78+
NB The base image must have the VMware Tools installed.
79+
"""
80+
81+
def __init__(self):
82+
super(VMwareGuestInfoService, self).__init__()
83+
self._os_utils = osutils_factory.get_os_utils()
84+
85+
def get_user_data(self):
86+
return self._get_cache_data('userdata')
87+
88+
def get_meta_data(self):
89+
return json.self._get_cache_data('metadata')
90+
91+
def _get_data(self, name):
92+
if not os.path.exists(CONF.vmwareguestinfo.rpctool_path):
93+
return None
94+
stdout, stderr, exit_code = self._os_utils.execute_process([
95+
CONF.vmwareguestinfo.rpctool_path,
96+
'info-get guestinfo.%s' % name
97+
])
98+
if exit_code:
99+
raise exception.CloudbaseInitException(
100+
'Failed to execute "%(rpctool_path)s" with '
101+
'exit code: %(exit_code)s\nstdout: %(stdout)s\nstderr: %(stderr)s' % {
102+
'rpctool_path': CONF.vmwareguestinfo.rpctool_path,
103+
'exit_code': exit_code,
104+
'stdout': stdout,
105+
'stderr': stderr})
106+
data = base64.b64decode(stdout)
107+
if data[:2] == self._GZIP_MAGIC_NUMBER:
108+
bio = io.BytesIO(data)
109+
with gzip.GzipFile(fileobj=bio, mode='rb') as out:
110+
data = out.read()
111+
return data
112+
113+
def get_host_name(self):
114+
return self.get_meta_data().get('local-hostname')
115+
116+
def get_public_keys(self):
117+
return self.get_meta_data().get('public-keys')
118+
119+
def get_admin_username(self):
120+
return self.get_meta_data().get('admin-username')
121+
122+
def get_admin_password(self):
123+
return self.get_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)