Skip to content

Commit 065c1f7

Browse files
committed
more instances fixes
1 parent b996458 commit 065c1f7

File tree

3 files changed

+58
-61
lines changed

3 files changed

+58
-61
lines changed

plugins/modules/instance.py

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,12 @@
166166
- Name of the zone in which the instance should be deployed.
167167
type: str
168168
required: true
169-
ssh_key:
169+
ssh_keys:
170170
description:
171-
- Name of the SSH key to be deployed on the new instance.
172-
type: str
171+
- List of names of SSH keys to be deployed on the new instance.
172+
type: list
173+
elements: str
174+
aliases: [ ssh_key ]
173175
affinity_groups:
174176
description:
175177
- Affinity groups names to be applied to the new instance.
@@ -336,11 +338,6 @@
336338
returned: if available
337339
type: str
338340
sample: Ge2oe7Do
339-
ssh_key:
340-
description: Name of SSH key deployed to instance.
341-
returned: if available
342-
type: str
343-
sample: key@work
344341
ssh_keys:
345342
description: Names of SSH key deployed to instance.
346343
returned: if available
@@ -467,7 +464,6 @@ def __init__(self, module):
467464
"isoname": "iso",
468465
"templatename": "template",
469466
"templatedisplaytext": "template_display_text",
470-
"keypair": "ssh_key",
471467
"keypairs": "ssh_keys",
472468
"hostname": "host",
473469
}
@@ -587,9 +583,9 @@ def get_template_or_iso(self, key=None):
587583

588584
self.module.fail_json(msg="ISO '%s' not found" % iso)
589585

590-
def get_instance(self):
586+
def get_instance(self, refresh=False):
591587
instance = self.instance
592-
if not instance:
588+
if not instance or refresh:
593589
instance_name = self.get_or_fallback("name", "display_name")
594590
args = {
595591
"account": self.get_account(key="name"),
@@ -602,8 +598,17 @@ def get_instance(self):
602598
if instances:
603599
for v in instances:
604600
if instance_name.lower() in [v["name"].lower(), v["displayname"].lower(), v["id"]]:
601+
602+
if "keypairs" not in v:
603+
v["keypairs"] = list()
604+
605+
# Workaround for keypairs not a list
606+
if not isinstance(v["keypairs"], list):
607+
v["keypairs"] = [v["keypairs"]]
608+
605609
self.instance = v
606610
break
611+
607612
return self.instance
608613

609614
def get_user_data_id_by_name(self):
@@ -654,48 +659,45 @@ def get_iptonetwork_mappings(self):
654659
res.append(dict(networkid=ids[i], **data))
655660
return res
656661

657-
def get_ssh_keypair(self, key=None, name=None, fail_on_missing=True):
658-
ssh_key_name = name or self.module.params.get("ssh_key")
659-
if ssh_key_name is None:
660-
return
661-
662+
def get_ssh_keypair(self, name, key=None, fail_on_missing=True):
662663
args = {
663664
"domainid": self.get_domain("id"),
664665
"account": self.get_account("name"),
665666
"projectid": self.get_project("id"),
666-
"name": ssh_key_name,
667+
"name": name,
667668
}
668669
ssh_key_pairs = self.query_api("listSSHKeyPairs", **args)
669670
if "sshkeypair" in ssh_key_pairs:
670671
return self._get_by_key(key=key, my_dict=ssh_key_pairs["sshkeypair"][0])
671672

672673
elif fail_on_missing:
673-
self.module.fail_json(msg="SSH key not found: %s" % ssh_key_name)
674+
self.module.fail_json(msg="SSH key not found: %s" % name)
674675

675-
def ssh_key_has_changed(self):
676-
ssh_key_name = self.module.params.get("ssh_key")
677-
if ssh_key_name is None:
676+
def ssh_keys_changed(self):
677+
ssh_keys = self.module.params.get("ssh_keys")
678+
if ssh_keys is None:
678679
return False
679680

680-
# Fails if keypair for param is inexistent
681-
param_ssh_key_fp = self.get_ssh_keypair(key="fingerprint")
681+
instance_ssh_keys = self.instance.get("keypairs") or [self.instance.get("keypair") or ""]
682682

683-
# CloudStack 4.5 does return keypair on instance for a non existent key.
684-
instance_ssh_key_name = self.instance.get("keypair")
685-
if instance_ssh_key_name is None:
686-
return True
683+
param_ssh_key_fingerprints = [self.get_ssh_keypair(key="fingerprint", name=ssh_key) for ssh_key in ssh_keys]
687684

688-
# Get fingerprint for keypair of instance but do not fail if inexistent.
689-
instance_ssh_key_fp = self.get_ssh_keypair(key="fingerprint", name=instance_ssh_key_name, fail_on_missing=False)
690-
if not instance_ssh_key_fp:
691-
return True
685+
for instance_ssh_key in instance_ssh_keys:
686+
687+
if not ssh_keys:
688+
return True
692689

693-
# Compare fingerprints to ensure the keypair changed
694-
if instance_ssh_key_fp != param_ssh_key_fp:
695-
return True
690+
# Get fingerprint for keypair of instance but do not fail if inexistent.
691+
instance_ssh_key_fingerprint = self.get_ssh_keypair(key="fingerprint", name=instance_ssh_key, fail_on_missing=False)
692+
if not instance_ssh_key_fingerprint:
693+
return True
694+
695+
# Compare fingerprints to ensure the keypair changed
696+
if instance_ssh_key_fingerprint not in param_ssh_key_fingerprints:
697+
return True
696698
return False
697699

698-
def security_groups_has_changed(self):
700+
def security_groups_changed(self):
699701
security_groups = self.module.params.get("security_groups")
700702
if security_groups is None:
701703
return False
@@ -758,6 +760,7 @@ def present_instance(self, start_vm=True):
758760

759761
# In check mode, we do not necessarily have an instance
760762
if instance:
763+
instance = self.get_instance(refresh=True)
761764
instance = self.ensure_tags(resource=instance, resource_type="UserVm")
762765
# refresh instance data
763766
self.instance = instance
@@ -820,7 +823,7 @@ def deploy_instance(self, start_vm=True):
820823
args["name"] = self.module.params.get("name")
821824
args["displayname"] = self.get_or_fallback("display_name", "name")
822825
args["group"] = self.module.params.get("group")
823-
args["keypair"] = self.get_ssh_keypair(key="name")
826+
args["keypairs"] = self.module.params.get("ssh_keys")
824827
args["size"] = self.module.params.get("disk_size")
825828
args["startvm"] = start_vm
826829
args["rootdisksize"] = self.module.params.get("root_disk_size")
@@ -870,9 +873,9 @@ def update_instance(self, instance, start_vm=True):
870873
args_instance_update["displayname"] = self.module.params.get("display_name")
871874
instance_changed = self.has_changed(args_instance_update, instance)
872875

873-
ssh_key_changed = self.ssh_key_has_changed()
876+
ssh_keys_changed = self.ssh_keys_changed()
874877

875-
security_groups_changed = self.security_groups_has_changed()
878+
security_groups_changed = self.security_groups_changed()
876879

877880
# Volume data
878881
args_volume_update = {}
@@ -905,7 +908,7 @@ def update_instance(self, instance, start_vm=True):
905908
service_offering_changed,
906909
instance_changed,
907910
security_groups_changed,
908-
ssh_key_changed,
911+
ssh_keys_changed,
909912
root_disk_size_changed,
910913
]
911914

@@ -936,12 +939,13 @@ def update_instance(self, instance, start_vm=True):
936939
self.instance = instance
937940

938941
# Reset SSH key
939-
if ssh_key_changed:
942+
if ssh_keys_changed:
940943
# SSH key data
941-
args_ssh_key = {}
942-
args_ssh_key["id"] = instance["id"]
943-
args_ssh_key["projectid"] = self.get_project(key="id")
944-
args_ssh_key["keypair"] = self.module.params.get("ssh_key")
944+
args_ssh_key = {
945+
"id": instance["id"],
946+
"projectid": self.get_project(key="id"),
947+
"keypairs": self.module.params.get("ssh_keys"),
948+
}
945949
instance = self.query_api("resetSSHKeyForVirtualMachine", **args_ssh_key)
946950
instance = self.poll_job(instance, "virtualmachine")
947951
self.instance = instance
@@ -1091,12 +1095,9 @@ def restore_instance(self):
10911095
return instance
10921096

10931097
def get_result(self, resource):
1098+
resource = self.get_instance(refresh=True)
10941099
super(AnsibleCloudStackInstance, self).get_result(resource)
10951100
if resource:
1096-
# 4.18 does not return keypairs as list as doc claims
1097-
if "ssh_keys" in self.result and not isinstance(self.result["ssh_keys"], list):
1098-
self.result["ssh_keys"] = [self.result["ssh_keys"]]
1099-
11001101
self.result["user_data"] = self._get_instance_user_data(resource)
11011102
if "securitygroup" in resource:
11021103
security_groups = []
@@ -1158,7 +1159,7 @@ def main():
11581159
user_data_name=dict(),
11591160
user_data_details=dict(type="dict"),
11601161
zone=dict(required=True),
1161-
ssh_key=dict(no_log=False),
1162+
ssh_keys=dict(type="list", elements="str", aliases=["ssh_key"], no_log=False),
11621163
force=dict(type="bool", default=False),
11631164
tags=dict(type="list", elements="dict", aliases=["tag"]),
11641165
details=dict(type="dict"),

tests/integration/targets/instance/tasks/present_display_name.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
that:
103103
- instance is changed
104104
- instance.display_name == cs_resource_prefix + "-vm-" + instance_number|string
105-
- instance.service_offering == "{{ test_cs_instance_offering_2 }}"
105+
- instance.service_offering == test_cs_instance_offering_2
106106
- instance.state == "Stopped"
107107

108108
- name: test starting instance with display_name
@@ -116,7 +116,7 @@
116116
that:
117117
- instance is changed
118118
- instance.display_name == cs_resource_prefix + "-vm-" + instance_number|string
119-
- instance.service_offering == "{{ test_cs_instance_offering_2 }}"
119+
- instance.service_offering == test_cs_instance_offering_2
120120
- instance.state == "Running"
121121

122122
- name: test starting instance with display_name idempotence
@@ -130,7 +130,7 @@
130130
that:
131131
- instance is not changed
132132
- instance.display_name == cs_resource_prefix + "-vm-" + instance_number|string
133-
- instance.service_offering == "{{ test_cs_instance_offering_2 }}"
133+
- instance.service_offering == test_cs_instance_offering_2
134134
- instance.state == "Running"
135135

136136
- name: test force update running instance with display_name

tests/integration/targets/instance/tasks/sshkeys.yml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
assert:
1313
that:
1414
- instance is failed
15-
- 'instance.msg == "SSH key not found:" + cs_resource_prefix + "-sshkey-does-not-exist"'
15+
- '"Not all specified keyparis exist" in instance.msg'
1616

1717
- name: test create instance without keypair in check mode
1818
ngine_io.cloudstack.instance:
@@ -25,7 +25,6 @@
2525
- name: verify create instance without keypair in check mode
2626
assert:
2727
that:
28-
- instance is successful
2928
- instance is changed
3029

3130
- name: test create instance without keypair
@@ -38,9 +37,8 @@
3837
- name: verify create instance without keypair
3938
assert:
4039
that:
41-
- instance is successful
4240
- instance is changed
43-
- instance.ssh_keys is not defined
41+
- not instance.ssh_keys
4442

4543
- name: test create instance without keypair idempotence
4644
ngine_io.cloudstack.instance:
@@ -52,9 +50,8 @@
5250
- name: verify create instance without keypair idempotence
5351
assert:
5452
that:
55-
- instance is successful
5653
- instance is not changed
57-
- instance.ssh_keys is not defined
54+
- not instance.ssh_keys
5855

5956
- name: setup ssh key2
6057
ngine_io.cloudstack.ssh_key:
@@ -77,7 +74,7 @@
7774
assert:
7875
that:
7976
- instance is changed
80-
- instance.ssh_keys is not defined
77+
- not instance.ssh_keys
8178

8279
- name: test update instance ssh key2
8380
ngine_io.cloudstack.instance:
@@ -127,7 +124,6 @@
127124
assert:
128125
that:
129126
- instance is failed
130-
- 'instance.msg == "SSH key not found: {{ cs_resource_prefix }}-sshkey2"'
131127

132128
- name: test update instance ssh key in check mode
133129
ngine_io.cloudstack.instance:
@@ -141,7 +137,7 @@
141137
assert:
142138
that:
143139
- instance is changed
144-
- instance.ssh_keys is not defined
140+
- 'cs_resource_prefix + "-sshkey2" in instance.ssh_keys'
145141

146142
- name: test update instance ssh key
147143
ngine_io.cloudstack.instance:

0 commit comments

Comments
 (0)