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

Proxmox local connection support. #4027

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions changelogs/fragments/4027-proxmox-local-backend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
minor_changes:
- proxmox modules - use ``local`` backend if no ``api_host`` is specified (https://github.com/ansible-collections/community.general/pull/4027).
4 changes: 1 addition & 3 deletions plugins/doc_fragments/proxmox.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,17 @@ class ModuleDocFragment(object):
description:
- Specify the target host of the Proxmox VE cluster.
type: str
required: true
api_port:
description:
- Specify the target port of the Proxmox VE cluster.
- Uses the E(PROXMOX_PORT) environment variable if not specified.
type: int
required: false
version_added: 9.1.0
api_user:
description:
- Specify the user to authenticate with.
type: str
required: true
default: root@pam
api_password:
description:
- Specify the password to authenticate with.
Expand Down
36 changes: 24 additions & 12 deletions plugins/module_utils/proxmox.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,13 @@
def proxmox_auth_argument_spec():
return dict(
api_host=dict(type='str',
required=True,
fallback=(env_fallback, ['PROXMOX_HOST'])
),
api_port=dict(type='int',
fallback=(env_fallback, ['PROXMOX_PORT'])
),
api_user=dict(type='str',
required=True,
default='root@pam',
fallback=(env_fallback, ['PROXMOX_USER'])
),
api_password=dict(type='str',
Expand Down Expand Up @@ -92,21 +91,34 @@ def _connect(self):
api_token_secret = self.module.params['api_token_secret']
validate_certs = self.module.params['validate_certs']

auth_args = {'user': api_user}
auth_args = {}

if api_port:
auth_args['port'] = api_port
if api_host:
auth_args['backend'] = 'https'

if api_password:
auth_args['password'] = api_password
api_host = [api_host]
if api_port:
auth_args['port'] = api_port

auth_args['user'] = api_user
if api_password:
auth_args['password'] = api_password
elif api_token_id and api_token_secret:
if self.proxmoxer_version < LooseVersion('1.1.0'):
self.module.fail_json('Using "token_name" and "token_value" require proxmoxer>=1.1.0')

auth_args['token_name'] = api_token_id
auth_args['token_value'] = api_token_secret
else:
self.module.fail_json('api_host is specified but any of the following are missing: api_password, api_token_id')

auth_args['verify_ssl'] = validate_certs
else:
if self.proxmoxer_version < LooseVersion('1.1.0'):
self.module.fail_json('Using "token_name" and "token_value" require proxmoxer>=1.1.0')
auth_args['token_name'] = api_token_id
auth_args['token_value'] = api_token_secret
api_host = []
auth_args['backend'] = 'local'

try:
return ProxmoxAPI(api_host, verify_ssl=validate_certs, **auth_args)
return ProxmoxAPI(*api_host, **auth_args)
except Exception as e:
self.module.fail_json(msg='%s' % e, exception=traceback.format_exc())

Expand Down
1 change: 0 additions & 1 deletion plugins/modules/proxmox.py
Original file line number Diff line number Diff line change
Expand Up @@ -1078,7 +1078,6 @@ def main():
('state', 'present', ('clone', 'ostemplate', 'update'), True),
],
required_together=[("api_token_id", "api_token_secret")],
required_one_of=[("api_password", "api_token_id")],
mutually_exclusive=[
(
"clone",
Expand Down
2 changes: 1 addition & 1 deletion plugins/modules/proxmox_disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ def main():
module = AnsibleModule(
argument_spec=module_args,
required_together=[('api_token_id', 'api_token_secret')],
required_one_of=[('name', 'vmid'), ('api_password', 'api_token_id')],
required_one_of=[('name', 'vmid')],
required_if=[
('create', 'forced', ['storage']),
('state', 'resized', ['size']),
Expand Down
1 change: 0 additions & 1 deletion plugins/modules/proxmox_domain_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@ def main():

module = AnsibleModule(
argument_spec=module_args,
required_one_of=[('api_password', 'api_token_id')],
required_together=[('api_token_id', 'api_token_secret')],
supports_check_mode=True
)
Expand Down
1 change: 0 additions & 1 deletion plugins/modules/proxmox_group_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ def main():

module = AnsibleModule(
argument_spec=module_args,
required_one_of=[('api_password', 'api_token_id')],
required_together=[('api_token_id', 'api_token_secret')],
supports_check_mode=True
)
Expand Down
23 changes: 15 additions & 8 deletions plugins/modules/proxmox_kvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,7 @@
sample: "VM kropta with vmid = 110 is running"
'''

import json
import re
import time
from ansible.module_utils.six.moves.urllib.parse import quote
Expand Down Expand Up @@ -1105,14 +1106,16 @@ def create_vm(self, vmid, newid, node, name, memory, cpu, cores, sockets, update
self.module.fail_json(msg='%s is not a valid tag' % tag)
kwargs['tags'] = ",".join(kwargs['tags'])

# -args and skiplock require root@pam user - but can not use api tokens
if self.module.params['api_user'] == "root@pam" and self.module.params['args'] is not None:
kwargs['args'] = self.module.params['args']
elif self.module.params['api_user'] != "root@pam" and self.module.params['args'] is not None:
self.module.fail_json(msg='args parameter require root@pam user. ')
# -args and skiplock require root@pam user but cannot use API tokens
if self.module.params['api_user'] == 'root@pam':
if self.module.params['args'] is not None:
kwargs['args'] = self.module.params['args']
else:
if self.module.params['args'] is not None:
self.module.fail_json(msg='args parameter require root@pam user. ')

if self.module.params['api_user'] != "root@pam" and self.module.params['skiplock'] is not None:
self.module.fail_json(msg='skiplock parameter require root@pam user. ')
if self.module.params['skiplock'] is not None:
self.module.fail_json(msg='skiplock parameter require root@pam user. ')

if update:
if proxmox_node.qemu(vmid).config.set(name=name, memory=memory, cpu=cpu, cores=cores, sockets=sockets, **kwargs) is None:
Expand All @@ -1128,6 +1131,10 @@ def create_vm(self, vmid, newid, node, name, memory, cpu, cores, sockets, update
else:
taskid = proxmox_node.qemu.create(vmid=vmid, name=name, memory=memory, cpu=cpu, cores=cores, sockets=sockets, **kwargs)

# FIXME: Workaround for https://forum.proxmox.com/threads/api-bug-pvesh-create-nodes-node-qemu-output-format-json-response-format-broken.111469/.
if 'errors' in taskid:
taskid = json.loads(taskid['errors'].splitlines()[-1])

if not self.wait_for_task(node, taskid):
self.module.fail_json(msg='Reached timeout while waiting for creating VM. Last line in task before timeout: %s' %
proxmox_node.tasks(taskid).log.get()[:1])
Expand Down Expand Up @@ -1297,7 +1304,7 @@ def main():
argument_spec=module_args,
mutually_exclusive=[('delete', 'revert'), ('delete', 'update'), ('revert', 'update'), ('clone', 'update'), ('clone', 'delete'), ('clone', 'revert')],
required_together=[('api_token_id', 'api_token_secret')],
required_one_of=[('name', 'vmid'), ('api_password', 'api_token_id')],
required_one_of=[('name', 'vmid')],
required_if=[('state', 'present', ['node'])],
)

Expand Down
2 changes: 1 addition & 1 deletion plugins/modules/proxmox_nic.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ def main():
module = AnsibleModule(
argument_spec=module_args,
required_together=[('api_token_id', 'api_token_secret')],
required_one_of=[('name', 'vmid'), ('api_password', 'api_token_id')],
required_one_of=[('name', 'vmid')],
supports_check_mode=True,
)

Expand Down
1 change: 0 additions & 1 deletion plugins/modules/proxmox_node_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ def main():

module = AnsibleModule(
argument_spec=module_args,
required_one_of=[('api_password', 'api_token_id')],
required_together=[('api_token_id', 'api_token_secret')],
supports_check_mode=True,
)
Expand Down
14 changes: 10 additions & 4 deletions plugins/modules/proxmox_snap.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,17 @@ def snapshot_create(self, vm, vmid, timeout, snapname, description, vmstate, unb
# The correct permissions are required only to reconfig mounts.
# Not checking now would allow to remove the configuration BUT
# fail later, leaving the container in a misconfigured state.
if (
self.module.params['api_user'] != 'root@pam'
or not self.module.params['api_password']
if not (
self.module.params['api_user'] == 'root@pam' and (
# Either via API and password,
self.module.params['api_password'] or
# or directly via local backend.
not self.module.params['api_host']
)
):
self.module.fail_json(msg='`unbind=True` requires authentication as `root@pam` with `api_password`, API tokens are not supported.')
self.module.fail_json(
msg='`unbind=True` requires authentication as `root@pam` with `api_password` or local backend, API tokens are not supported.'
)
return False
mountpoints = self._container_mp_get(vm, vmid)
vmstatus = self.vmstatus(vm, vmid).current().get()['status']
Expand Down
1 change: 0 additions & 1 deletion plugins/modules/proxmox_storage_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ def main():

module = AnsibleModule(
argument_spec=module_args,
required_one_of=[('api_password', 'api_token_id')],
required_together=[('api_token_id', 'api_token_secret')],
mutually_exclusive=[('storage', 'type')],
supports_check_mode=True
Expand Down
1 change: 0 additions & 1 deletion plugins/modules/proxmox_tasks_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ def main():
module = AnsibleModule(
argument_spec=module_args,
required_together=[('api_token_id', 'api_token_secret')],
required_one_of=[('api_password', 'api_token_id')],
supports_check_mode=True)
result = dict(changed=False)

Expand Down
1 change: 0 additions & 1 deletion plugins/modules/proxmox_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ def main():
module = AnsibleModule(
argument_spec=module_args,
required_together=[('api_token_id', 'api_token_secret')],
required_one_of=[('api_password', 'api_token_id')],
required_if=[('state', 'absent', ['template'])]
)

Expand Down
1 change: 0 additions & 1 deletion plugins/modules/proxmox_user_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@ def main():

module = AnsibleModule(
argument_spec=module_args,
required_one_of=[('api_password', 'api_token_id')],
required_together=[('api_token_id', 'api_token_secret')],
mutually_exclusive=[('user', 'userid'), ('domain', 'userid')],
supports_check_mode=True
Expand Down
8 changes: 0 additions & 8 deletions tests/unit/plugins/modules/test_proxmox_vm_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,14 +450,6 @@ def tearDown(self):
self.connect_mock.stop()
super(TestProxmoxVmInfoModule, self).tearDown()

def test_module_fail_when_required_args_missing(self):
with pytest.raises(AnsibleFailJson) as exc_info:
set_module_args({})
self.module.main()

result = exc_info.value.args[0]
assert result["msg"] == "missing required arguments: api_host, api_user"

def test_get_lxc_vms_information(self):
with pytest.raises(AnsibleExitJson) as exc_info:
set_module_args(get_module_args(type="lxc"))
Expand Down