Skip to content

Commit 67a02b5

Browse files
Make cluster creation idempotent (#125)
* Make cluster creation idempotent * Fix a bug in the cluster name validation * added changelog * Fix sanity * Update unit tests * Update unit tests * Update unit tests * Update unit tests * Fix when cluster_data is not present at all
1 parent 29f412e commit 67a02b5

File tree

3 files changed

+31
-19
lines changed

3 files changed

+31
-19
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
minor_changes:
3+
- proxmox_cluster - cluster creation has been made idempotent (https://github.com/ansible-collections/community.proxmox/pull/125).

plugins/modules/proxmox_cluster.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"""
9090

9191

92+
import re
9293
from ansible.module_utils.basic import AnsibleModule
9394
from ansible_collections.community.proxmox.plugins.module_utils.proxmox import (
9495
proxmox_auth_argument_spec, ProxmoxAnsible)
@@ -99,24 +100,29 @@ def cluster_create(self):
99100
cluster_name = self.module.params.get("cluster_name") or self.module.params.get("api_host")
100101
payload = {"clustername": cluster_name}
101102

103+
# Get cluster data
104+
cluster_data = self.proxmox_api.cluster.config.totem.get()
105+
# If we have data, check if we're already member of the desired cluster or a different one
106+
if cluster_data and 'cluster_name' in cluster_data and cluster_data['cluster_name'] is not None:
107+
if cluster_data['cluster_name'] == cluster_name:
108+
self.module.exit_json(changed=False, msg="Cluster '{}' already present.".format(cluster_name), cluster=cluster_name)
109+
else:
110+
self.module.fail_json(msg='Error creating cluster: Node is already part of a different cluster - "{}"!'.format(cluster_data['cluster_name']))
111+
102112
if self.module.params.get("link0") is not None:
103113
payload["link0"] = self.module.params.get("link0")
104114
if self.module.params.get("link1") is not None:
105115
payload["link1"] = self.module.params.get("link1")
106116

107117
if self.module.check_mode:
108-
cluster_objects = self.proxmox_api.cluster.config.nodes.get()
109-
if len(cluster_objects) > 0:
110-
self.module.fail_json(msg="Error while creating cluster: Node is already part of a cluster!")
111-
else:
112-
self.module.exit_json(changed=True, msg="Cluster '{}' would be created (check mode).".format(cluster_name), cluster=cluster_name)
113-
114-
try:
115-
self.proxmox_api.cluster.config.post(**payload)
116-
except Exception as e:
117-
self.module.fail_json(msg="Error while creating cluster: {}".format(str(e)))
118+
self.module.exit_json(changed=True, msg="Cluster '{}' would be created (check mode).".format(cluster_name), cluster=cluster_name)
119+
else:
120+
try:
121+
self.proxmox_api.cluster.config.post(**payload)
122+
except Exception as e:
123+
self.module.fail_json(msg="Error while creating cluster: {}".format(str(e)))
118124

119-
self.module.exit_json(changed=True, msg="Cluster '{}' created.".format(cluster_name), cluster=cluster_name)
125+
self.module.exit_json(changed=True, msg="Cluster '{}' created.".format(cluster_name), cluster=cluster_name)
120126

121127
def cluster_join(self):
122128
master_ip = self.module.params.get("master_ip")
@@ -155,13 +161,15 @@ def proxmox_cluster_join_info_argument_spec():
155161
return dict()
156162

157163

158-
def validate_cluster_name(module, cluster_args, min_length=1, max_length=15):
159-
if not isinstance(cluster_args['cluster_name'], str):
160-
module.fail_json(msg="Cluster name must be a string.")
164+
def validate_cluster_name(module, min_length=1, max_length=15):
165+
cluster_name = module.params.get("cluster_name")
161166

162-
if not (min_length <= len(cluster_args['cluster_name']) <= max_length):
167+
if not (min_length <= len(cluster_name) <= max_length):
163168
module.fail_json(msg="Cluster name must be between {} and {} characters long.".format(min_length, max_length))
164169

170+
if not re.match(r"^[a-zA-Z0-9\-]+$", cluster_name):
171+
module.fail_json(msg="Cluster name must contain only letters, digits, or hyphens.")
172+
165173

166174
def main():
167175
module_args = proxmox_auth_argument_spec()
@@ -188,7 +196,7 @@ def main():
188196
)
189197

190198
proxmox = ProxmoxClusterAnsible(module)
191-
validate_cluster_name(module, cluster_args)
199+
validate_cluster_name(module)
192200

193201
# The Proxmox VE API currently does not support leaving a cluster
194202
# or removing a node from a cluster. Therefore, we only support creating

tests/unit/plugins/modules/test_proxmox_cluster.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ def test_cluster_create(mock_api, mock_init, module_args_create):
107107
mock_api_instance.cluster.config.post.assert_called_once_with(**expected_payload)
108108

109109

110-
def test_validate_cluster_name_valid():
110+
def test_validate_cluster_name_valid(module_args_create):
111111
module = MagicMock(spec=AnsibleModule)
112-
cluster_args = {"cluster_name": "devcluster"}
113-
validate_cluster_name(module, cluster_args)
112+
module.params = module_args_create
113+
114+
validate_cluster_name(module)

0 commit comments

Comments
 (0)