Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Vultr support #827

Merged
merged 37 commits into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
991444a
merged in upstream
ddymko Mar 1, 2021
584fd40
merge conflicts
ddymko Mar 1, 2021
a787657
fixing merge issues
ddymko Mar 1, 2021
7431c4c
cleaning up readme
ddymko Mar 1, 2021
e7ca197
added ddymko to cla signers
ddymko Mar 1, 2021
fc769fc
Requested Changes
Mar 4, 2021
c70c644
Add to signers
Mar 4, 2021
2d04eff
Make new requested changes
Mar 5, 2021
ca7149f
Make new requested changes
Mar 5, 2021
41476a0
Make new requested changes
Mar 5, 2021
671ede5
Merge pull request #8 from eb3095/vultr-cloudinit
ddymko Mar 5, 2021
66813df
Merge branch 'master' into vultr-cloudinit
ddymko Mar 5, 2021
a4e69ca
Merge branch 'master' into vultr-cloudinit
ddymko Mar 8, 2021
8f0c479
Fix upcload case
Mar 8, 2021
0f9e0e8
Merge remote-tracking branch 'upstream/master' into vultr-cloudinit
ddymko Mar 8, 2021
f7c9dca
Make remaining changes
Mar 8, 2021
62c4e99
Fix typo
Mar 8, 2021
5193ad6
Fix typo
Mar 8, 2021
4872462
Merge pull request #10 from eb3095/vultr-cloudinit
ddymko Mar 9, 2021
3805fe5
Wave one of requested changes
Mar 9, 2021
1596185
Wave two of requested changes
Mar 10, 2021
decfddc
Fix debug calls
Mar 10, 2021
889a932
Fix debug calls
Mar 10, 2021
55af391
Merge remote-tracking branch 'upstream/master' into benner-fixes
ddymko Mar 18, 2021
0b6093c
Adjust ethtool usage and v1 changes
Mar 31, 2021
24c182d
Merge remote-tracking branch 'upstream/master' into vultr-cloudinit
ddymko Apr 1, 2021
2c72f3f
Merge pull request #13 from eb3095/vultr-cloudinit
ddymko Apr 1, 2021
6f1698e
Cleanup
Apr 2, 2021
5be0868
Merge pull request #14 from eb3095/vultr-cloudinit
ddymko Apr 2, 2021
d2924aa
Merge branch 'master' into vultr-cloudinit
eb3095 Apr 12, 2021
9971254
Merge remote-tracking branch 'upstream/vultr-cloudinit' into vultr-cl…
Apr 12, 2021
70f4e9e
Remove excess lines
Apr 12, 2021
db18ba1
Merge pull request #15 from eb3095/vultr-cloudinit
ddymko Apr 12, 2021
e4b1348
Merge branch 'master' into vultr-cloudinit
ddymko Apr 13, 2021
c7edc51
Merge remote-tracking branch 'upstream/vultr-cloudinit' into vultr-cl…
Apr 13, 2021
07e9f5a
Cleanup variable usage for network generation
Apr 13, 2021
eab53d1
Merge pull request #16 from eb3095/vultr-cloudinit
ddymko Apr 13, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 26 additions & 19 deletions cloudinit/sources/DataSourceVultr.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,40 @@ def _get_data(self):
# Fetch metadata
md = self.get_metadata()

config = vultr.generate_config(md)
self.metadata_full = md
self.metadata['instanceid'] = md['instanceid']
self.metadata['local-hostname'] = md['hostname']
self.metadata['public-keys'] = md["public-keys"]
self.userdata_raw = md["user-data"]

# This requires info generated in the vendor config
script = md['startup-script']
user_scripts = []
user_scripts = vultr.generate_user_scripts(script, config)
# Generate config and process data
config = self.get_datasource_data(md)

# Dump vendor config so diagnosing failures is manageable
# Dump some data so diagnosing failures is manageable
LOG.debug("Vultr Vendor Config:")
LOG.debug(json.dumps(config))
LOG.debug("SUBID: %s", self.metadata['instanceid'])
LOG.debug("Hostname: %s", self.metadata['local-hostname'])
if self.userdata_raw is not None:
LOG.debug("User-Data:")
LOG.debug(self.userdata_raw)

self.metadata_full = md
self.metadata['instanceid'] = self.metadata_full['instanceid']
self.metadata['local-hostname'] = self.metadata_full['hostname']
return True

# Process metadata
def get_datasource_data(self, md):
# Grab config
config = vultr.generate_config(md)

# This requires info generated in the vendor config
user_scripts = []
user_scripts = vultr.generate_user_scripts(md, config)

# Default hostname is "vultr"
if self.metadata['local-hostname'] == "":
self.metadata['local-hostname'] = "vultr"

self.metadata['public-keys'] = md["public-keys"].splitlines()
self.metadata['public-keys'] = md["public-keys"]
self.userdata_raw = md["user-data"]
if self.userdata_raw == "":
self.userdata_raw = None
Expand All @@ -81,14 +95,7 @@ def _get_data(self):
self.vendordata_raw.append(uscript)
self.vendordata_raw.append("#cloud-config\n%s" % json.dumps(config))

# Dump some data so diagnosing failures is manageable
LOG.debug("SUBID: %s", self.metadata['instanceid'])
LOG.debug("Hostname: %s", self.metadata['local-hostname'])
if self.userdata_raw is not None:
LOG.debug("User-Data:")
LOG.debug(self.userdata_raw)

return True
return config

# Get the metadata by flag
def get_metadata(self):
Expand Down Expand Up @@ -116,7 +123,7 @@ def launch_index(self):

@property
def network_config(self):
md = self.get_metadata()
md = self.metadata_full['interfaces']
config = vultr.generate_network_config(md)

return config
Expand Down
66 changes: 35 additions & 31 deletions cloudinit/sources/helpers/vultr.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ def get_metadata(url, timeout, retries, sec_between):
metadata = v1_json

# This comes through as a string but is JSON, make a dict
metadata['vendor-config'] = json.loads(metadata['vendor-config'])
raw = metadata['vendor-data']['config']
metadata['vendor-data']['config'] = json.loads(raw)

return metadata

Expand Down Expand Up @@ -61,7 +62,7 @@ def is_vultr():
return True

# Baremetal requires a kernel parameter
if "vultr" in util.get_cmdline():
if "vultr" in util.get_cmdline().split():
return True
smoser marked this conversation as resolved.
Show resolved Hide resolved

return False
Expand Down Expand Up @@ -113,15 +114,13 @@ def generate_network_config(md):
]
}

intf = md['interfaces']

# Prepare interface 0, public
if len(intf) > 0:
network['config'].append(generate_public_network_interface(intf))
if len(md) > 0:
network['config'].append(generate_public_network_interface(md))

# Prepare interface 1, private
if len(intf) > 1:
network['config'].append(generate_private_network_interface(intf))
if len(md) > 1:
network['config'].append(generate_private_network_interface(md))

return network

Expand Down Expand Up @@ -210,43 +209,48 @@ def generate_private_network_interface(interfaces):
# images are deployed on Vultr before Cloud-Init
def generate_config(md):
# Create vendor config
config_template = copy.deepcopy(md['vendor-config'])
config_template = copy.deepcopy(md['vendor-data']['config'])

# Add generated network parts
config_template['network'] = generate_network_config(md)

# Linux specific packages
if util.is_Linux():
config_template['packages'].append("ethtool")
config_template['network'] = generate_network_config(md['interfaces'])

return config_template


# This is for the vendor and startup scripts
def generate_user_scripts(script, vendor_config):
# Define vendor script
vendor_script = "#!/bin/bash"

# Go through the interfaces
for netcfg in vendor_config['network']['config']:
# If the interface has a mac and is physical
if "mac_address" in netcfg and netcfg['type'] == "physical":
# Enable multi-queue on linux
# This is executed as a vendor script
if util.is_Linux():
def generate_user_scripts(md, vendor_config):
user_scripts = []

# Raid 1 script
if md['vendor-data']['raid1-script']:
user_scripts.append(md['vendor-data']['raid1-script'])

# Enable multi-queue on linux
if util.is_Linux() and md['vendor-data']['ethtool-script']:
ethtool_script = md['vendor-data']['ethtool-script']

# Tool location
tool = "/opt/vultr/ethtool"

# Go through the interfaces
for netcfg in vendor_config['network']['config']:
# If the interface has a mac and is physical
if "mac_address" in netcfg and netcfg['type'] == "physical":
# Set its multi-queue to num of cores as per RHEL Docs
name = netcfg['name']
command = "ethtool -L %s combined $(nproc --all)" % name
vendor_script = '%s\n%s' % (vendor_script, command)
command = "%s -L %s combined $(nproc --all)" % (tool, name)
ethtool_script = '%s\n%s' % (ethtool_script, command)

vendor_script = '%s\n' % vendor_script
user_scripts.append(ethtool_script)

# Vendor script and start the array
user_scripts = [vendor_script]
# This is for vendor scripts
if md['vendor-data']['vendor-script']:
user_scripts.append(md['vendor-data']['vendor-script'])

# Startup script
script = md['startup-script']
if script and script != "echo No configured startup script":
smoser marked this conversation as resolved.
Show resolved Hide resolved
user_scripts.append("%s\n" % script)
user_scripts.append(script)

return user_scripts

Expand Down
101 changes: 48 additions & 53 deletions tests/unittests/test_datasource/test_vultr.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,11 @@

from cloudinit import helpers
from cloudinit import settings
from cloudinit import util
from cloudinit.sources import DataSourceVultr
from cloudinit.sources.helpers import vultr

from cloudinit.tests.helpers import mock, CiTestCase

# Large data
SCRIPT1 = 'IyEvYmluL2Jhc2gKZXRodG9vbCAtTCBldGgwIGNvbWJp' \
'bmVkICQobnByb2MgLS1hbGwpCg=='
SCRIPT2 = 'IyEvYmluL2Jhc2gKZXRodG9vbCAtTCBldGgwIGNvbWJp' \
'bmVkICQobnByb2MgLS1hbGwpCmV0aHRvb2wgLUwgZXRo' \
'MSBjb21iaW5lZCAkKG5wcm9jIC0tYWxsKQo='

# Vultr metadata test data
VULTR_V1_1 = {
'bgp': {
Expand Down Expand Up @@ -60,29 +52,35 @@
'network-type': 'public'
}
],
'public-keys': 'ssh-rsa AAAAB3NzaC1y...IQQhv5PAOKaIl+mM3c= test3@key\n',
'public-keys': [
'ssh-rsa AAAAB3NzaC1y...IQQhv5PAOKaIl+mM3c= test3@key'
],
'region': {
'regioncode': 'EWR'
},
'user-defined': [
],
'startup-script': 'echo No configured startup script',
'raid1-script': '',
'user-data': [
],
'vendor-config': {
'package_upgrade': 'true',
'disable_root': 0,
'packages': [],
'ssh_pwauth': 1,
'chpasswd': {
'expire': False,
'list': [
'root:$6$S2Smuj.../VqxmIR9Urw0jPZ88i4yvB/'
]
},
'system_info': {
'default_user': {
'name': 'root'
'vendor-data': {
'vendor-script': '',
'ethtool-script': '',
'config': {
'package_upgrade': 'true',
'disable_root': 0,
'ssh_pwauth': 1,
'chpasswd': {
'expire': False,
'list': [
'root:$6$S2Smuj.../VqxmIR9Urw0jPZ88i4yvB/'
]
},
'system_info': {
'default_user': {
'name': 'root'
}
}
}
}
Expand Down Expand Up @@ -145,7 +143,9 @@
'networkid':'net5e7155329d730'
}
],
'public-keys': 'ssh-rsa AAAAB3NzaC1y...IQQhv5PAOKaIl+mM3c= test3@key\n',
'public-keys': [
'ssh-rsa AAAAB3NzaC1y...IQQhv5PAOKaIl+mM3c= test3@key'
],
'region': {
'regioncode': 'EWR'
},
Expand All @@ -154,20 +154,25 @@
'startup-script': 'echo No configured startup script',
'user-data': [
],
'vendor-config': {
'package_upgrade': 'true',
'disable_root': 0,
'packages': [],
'ssh_pwauth': 1,
'chpasswd': {
'expire': False,
'list': [
'root:$6$SxXx...k2mJNIzZB5vMCDBlYT1'
]
},
'system_info': {
'default_user': {
'name': 'root'

'vendor-data': {
'vendor-script': '',
'ethtool-script': '',
'raid1-script': '',
'config': {
'package_upgrade': 'true',
'disable_root': 0,
'ssh_pwauth': 1,
'chpasswd': {
'expire': False,
'list': [
'root:$6$SxXx...k2mJNIzZB5vMCDBlYT1'
]
},
'system_info': {
'default_user': {
'name': 'root'
}
}
}
}
Expand All @@ -183,9 +188,6 @@
EXPECTED_VULTR_CONFIG_1 = {
'package_upgrade': 'true',
'disable_root': 0,
'packages': [
'ethtool'
],
'ssh_pwauth': 1,
'chpasswd': {
'expire': False,
Expand Down Expand Up @@ -222,9 +224,6 @@
EXPECTED_VULTR_CONFIG_2 = {
'package_upgrade': 'true',
'disable_root': 0,
'packages': [
'ethtool'
],
'ssh_pwauth': 1,
'chpasswd': {
'expire': False,
Expand Down Expand Up @@ -371,18 +370,12 @@ def test_datasource(self,
orig_val = self.maxDiff
self.maxDiff = None

expected_script = util.b64d(SCRIPT2)
vendordata = source.vendordata_raw

# Test vendor script
self.assertEqual(
expected_script,
vendordata[0])

# Test vendor config
self.assertEqual(
EXPECTED_VULTR_CONFIG_2,
json.loads(vendordata[1].replace("#cloud-config", "")))
json.loads(vendordata[0].replace("#cloud-config", "")))

self.maxDiff = orig_val

Expand Down Expand Up @@ -417,16 +410,18 @@ def test_get_data_2(self, mock_netmap):
@mock.patch('cloudinit.net.get_interfaces_by_mac')
def test_network_config(self, mock_netmap):
mock_netmap.return_value = INTERFACE_MAP
interf = VULTR_V1_1['interfaces']

self.assertEqual(EXPECTED_VULTR_NETWORK_1,
vultr.generate_network_config(VULTR_V1_1))
vultr.generate_network_config(interf))

# Test Private Networking config generation
@mock.patch('cloudinit.net.get_interfaces_by_mac')
def test_private_network_config(self, mock_netmap):
mock_netmap.return_value = INTERFACE_MAP
interf = VULTR_V1_2['interfaces']

self.assertEqual(EXPECTED_VULTR_NETWORK_2,
vultr.generate_network_config(VULTR_V1_2))
vultr.generate_network_config(interf))

# vi: ts=4 expandtab