Add support for all parameters when cloning a VM with the proxmox salt-cloud driver

Fixes #62580
This commit is contained in:
pjcreath 2022-08-30 19:29:24 +00:00 committed by Megan Wilhite
parent b296fa2cbc
commit 2c8088f005
3 changed files with 82 additions and 13 deletions

1
changelog/62580.fixed Normal file
View file

@ -0,0 +1 @@
Fixed missing parameters when cloning a VM with the proxmox salt-cloud driver

View file

@ -31,6 +31,7 @@ import pprint
import re import re
import socket import socket
import time import time
import urllib
import salt.config as config import salt.config as config
import salt.utils.cloud import salt.utils.cloud
@ -192,7 +193,12 @@ def query(conn_type, option, post_data=None):
elif conn_type == "get": elif conn_type == "get":
response = requests.get(full_url, verify=verify_ssl, cookies=ticket) response = requests.get(full_url, verify=verify_ssl, cookies=ticket)
response.raise_for_status() try:
response.raise_for_status()
except requests.exceptions.RequestException:
# Log the details of the response.
log.error("Error in %s query to %s:\n%s", conn_type, full_url, response.text)
raise
try: try:
returned_data = response.json() returned_data = response.json()
@ -560,20 +566,18 @@ def _reconfigure_clone(vm_, vmid):
log.warning("Reconfiguring clones is only available under `qemu`") log.warning("Reconfiguring clones is only available under `qemu`")
return return
# TODO: Support other settings here too as these are not the only ones that can be modified after a clone operation # Determine which settings can be reconfigured.
query_path = "nodes/{}/qemu/{}/config"
valid_settings = set(_get_properties(query_path.format("{node}", "{vmid}"), "POST"))
log.info("Configuring cloned VM") log.info("Configuring cloned VM")
# Modify the settings for the VM one at a time so we can see any problems with the values # Modify the settings for the VM one at a time so we can see any problems with the values
# as quickly as possible # as quickly as possible
for setting in vm_: for setting in vm_:
if re.match(r"^(ide|sata|scsi)(\d+)$", setting): postParams = None
postParams = {setting: vm_[setting]} if setting == "vmid":
query( pass # vmid gets passed in the URL and can't be reconfigured
"post",
"nodes/{}/qemu/{}/config".format(vm_["host"], vmid),
postParams,
)
elif re.match(r"^net(\d+)$", setting): elif re.match(r"^net(\d+)$", setting):
# net strings are a list of comma seperated settings. We need to merge the settings so that # net strings are a list of comma seperated settings. We need to merge the settings so that
# the setting in the profile only changes the settings it touches and the other settings # the setting in the profile only changes the settings it touches and the other settings
@ -593,6 +597,13 @@ def _reconfigure_clone(vm_, vmid):
# Convert the dictionary back into a string list # Convert the dictionary back into a string list
postParams = {setting: _dictionary_to_stringlist(new_setting)} postParams = {setting: _dictionary_to_stringlist(new_setting)}
elif setting == "sshkeys":
postParams = {setting: urllib.parse.quote(vm_[setting], safe="")}
elif setting in valid_settings:
postParams = {setting: vm_[setting]}
if postParams:
query( query(
"post", "post",
"nodes/{}/qemu/{}/config".format(vm_["host"], vmid), "nodes/{}/qemu/{}/config".format(vm_["host"], vmid),

View file

@ -4,6 +4,7 @@
import io import io
import textwrap import textwrap
import urllib
import requests import requests
@ -11,7 +12,7 @@ from salt import config
from salt.cloud.clouds import proxmox from salt.cloud.clouds import proxmox
from tests.support.helpers import TstSuiteLoggingHandler from tests.support.helpers import TstSuiteLoggingHandler
from tests.support.mixins import LoaderModuleMockMixin from tests.support.mixins import LoaderModuleMockMixin
from tests.support.mock import ANY, MagicMock, patch from tests.support.mock import ANY, MagicMock, call, patch
from tests.support.unit import TestCase from tests.support.unit import TestCase
PROFILE = { PROFILE = {
@ -98,9 +99,12 @@ class ProxmoxTest(TestCase, LoaderModuleMockMixin):
result = proxmox._dictionary_to_stringlist({"a": "a", "b": "b"}) result = proxmox._dictionary_to_stringlist({"a": "a", "b": "b"})
self.assertEqual(result, "a=a,b=b") self.assertEqual(result, "a=a,b=b")
def test__reconfigure_clone(self): def test__reconfigure_clone_net_hdd(self):
# The return_value is for the net reconfigure assertions, it is irrelevant for the rest # The return_value is for the net reconfigure assertions, it is irrelevant for the rest
with patch.object( with patch(
"salt.cloud.clouds.proxmox._get_properties",
MagicMock(return_value=["net0", "ide0", "sata0", "scsi0"]),
), patch.object(
proxmox, "query", return_value={"net0": "c=overwritten,g=h"} proxmox, "query", return_value={"net0": "c=overwritten,g=h"}
) as query: ) as query:
# Test a vm that lacks the required attributes # Test a vm that lacks the required attributes
@ -127,6 +131,59 @@ class ProxmoxTest(TestCase, LoaderModuleMockMixin):
"post", "nodes/127.0.0.1/qemu/0/config", {"scsi0": "data"} "post", "nodes/127.0.0.1/qemu/0/config", {"scsi0": "data"}
) )
def test__reconfigure_clone_params(self):
"""
Test cloning a VM with parameters to be reconfigured.
"""
vmid = 201
properties = {
"ide2": "cdrom",
"sata1": "satatest",
"scsi0": "bootvol",
"net0": "model=virtio",
"agent": "1",
"args": "argsvalue",
"balloon": "128",
"ciuser": "root",
"cores": "2",
"description": "desc",
"memory": "256",
"name": "new2",
"onboot": "0",
"sshkeys": "ssh-rsa ABCDEF user@host\n",
}
query_calls = [call("get", "nodes/myhost/qemu/{}/config".format(vmid))]
for key, value in properties.items():
if key == "sshkeys":
value = urllib.parse.quote(value, safe="")
query_calls.append(
call(
"post",
"nodes/myhost/qemu/{}/config".format(vmid),
{key: value},
)
)
mock_query = MagicMock(return_value="")
with patch(
"salt.cloud.clouds.proxmox._get_properties",
MagicMock(return_value=list(properties.keys())),
), patch("salt.cloud.clouds.proxmox.query", mock_query):
vm_ = {
"profile": "my_proxmox",
"driver": "proxmox",
"technology": "qemu",
"name": "new2",
"host": "myhost",
"clone": True,
"clone_from": 123,
"ip_address": "10.10.10.10",
}
vm_.update(properties)
proxmox._reconfigure_clone(vm_, vmid)
mock_query.assert_has_calls(query_calls, any_order=True)
def test_clone(self): def test_clone(self):
""" """
Test that an integer value for clone_from Test that an integer value for clone_from