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

Multiple fixes for Proxmox salt-cloud driver #62559

Merged
merged 5 commits into from
Sep 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions changelog/62211.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improved error handling and diagnostics in the proxmox salt-cloud driver
1 change: 1 addition & 0 deletions changelog/62521.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed the omitted "pool" parameter when cloning a VM with the proxmox salt-cloud driver
1 change: 1 addition & 0 deletions changelog/62558.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed salt-cloud cloning a proxmox VM with a specified new vmid.
1 change: 1 addition & 0 deletions changelog/62580.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed missing parameters when cloning a VM with the proxmox salt-cloud driver
58 changes: 41 additions & 17 deletions salt/cloud/clouds/proxmox.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import re
import socket
import time
import urllib

import salt.config as config
import salt.utils.cloud
Expand Down Expand Up @@ -136,7 +137,9 @@ def _authenticate():
connect_data = {"username": username, "password": passwd}
full_url = "https://{}:{}/api2/json/access/ticket".format(url, port)

returned_data = requests.post(full_url, verify=verify_ssl, data=connect_data).json()
response = requests.post(full_url, verify=verify_ssl, data=connect_data)
response.raise_for_status()
returned_data = response.json()

ticket = {"PVEAuthCookie": returned_data["data"]["ticket"]}
csrf = str(returned_data["data"]["CSRFPreventionToken"])
Expand Down Expand Up @@ -190,7 +193,12 @@ def query(conn_type, option, post_data=None):
elif conn_type == "get":
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:
returned_data = response.json()
Expand Down Expand Up @@ -558,20 +566,18 @@ def _reconfigure_clone(vm_, vmid):
log.warning("Reconfiguring clones is only available under `qemu`")
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")

# Modify the settings for the VM one at a time so we can see any problems with the values
# as quickly as possible
for setting in vm_:
if re.match(r"^(ide|sata|scsi)(\d+)$", setting):
postParams = {setting: vm_[setting]}
query(
"post",
"nodes/{}/qemu/{}/config".format(vm_["host"], vmid),
postParams,
)

postParams = None
if setting == "vmid":
pass # vmid gets passed in the URL and can't be reconfigured
elif re.match(r"^net(\d+)$", setting):
# 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
Expand All @@ -591,6 +597,13 @@ def _reconfigure_clone(vm_, vmid):

# Convert the dictionary back into a string list
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(
"post",
"nodes/{}/qemu/{}/config".format(vm_["host"], vmid),
Expand Down Expand Up @@ -655,23 +668,26 @@ def create(vm_):
newid = _get_next_vmid()
data = create_node(vm_, newid)
except Exception as exc: # pylint: disable=broad-except
msg = str(exc)
if (
isinstance(exc, requests.exceptions.RequestException)
and exc.response is not None
):
msg = msg + "\n" + exc.response.text
log.error(
"Error creating %s on PROXMOX\n\n"
"The following exception was thrown when trying to "
"run the initial deployment: \n%s",
vm_["name"],
exc,
msg,
# Show the traceback if the debug logging level is enabled
exc_info_on_loglevel=logging.DEBUG,
)
return False

ret["creation_data"] = data
name = vm_["name"] # hostname which we know
if "clone" in vm_ and vm_["clone"] is True:
vmid = newid
else:
vmid = data["vmid"] # vmid which we have received
vmid = data["vmid"] # vmid which we have received
host = data["node"] # host which we have received
nodeType = data["technology"] # VM tech (Qemu / OpenVZ)

Expand Down Expand Up @@ -1007,6 +1023,7 @@ def create_node(vm_, newid):
)
for prop in _get_properties("/nodes/{node}/qemu", "POST", static_props):
if prop in vm_: # if the property is set, use it for the VM request
# If specified, vmid will override newid.
newnode[prop] = vm_[prop]

# The node is ready. Lets request it to be added
Expand All @@ -1026,6 +1043,8 @@ def create_node(vm_, newid):
if "clone" in vm_ and vm_["clone"] is True and vm_["technology"] == "qemu":
postParams = {}
postParams["newid"] = newnode["vmid"]
if "pool" in vm_:
postParams["pool"] = vm_["pool"]

for prop in "description", "format", "full", "name":
if (
Expand All @@ -1047,7 +1066,12 @@ def create_node(vm_, newid):
)
else:
node = query("post", "nodes/{}/{}".format(vmhost, vm_["technology"]), newnode)
return _parse_proxmox_upid(node, vm_)
result = _parse_proxmox_upid(node, vm_)

# When cloning, the upid contains the clone_from vmid instead of the new vmid
result["vmid"] = newnode["vmid"]

return result


def show_instance(name, call=None):
Expand Down
Loading