Skip to content

Commit

Permalink
Fix for machine_type on HyperCore 9.3
Browse files Browse the repository at this point in the history
Fixes #262

Signed-off-by: Justin Cinkelj <[email protected]>
  • Loading branch information
justinc1 committed Oct 2, 2023
1 parent d2998e4 commit 051b7da
Show file tree
Hide file tree
Showing 5 changed files with 379 additions and 37 deletions.
128 changes: 103 additions & 25 deletions plugins/module_utils/vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from ..module_utils.task_tag import TaskTag
from ..module_utils import errors
from ..module_utils.snapshot_schedule import SnapshotSchedule
from ..module_utils.hypercore_version import HyperCoreVersion

# FROM_ANSIBLE_TO_HYPERCORE_POWER_STATE and FROM_HYPERCORE_TO_ANSIBLE_POWER_STATE are mappings for how
# states are stored in python/ansible and how are they stored in hypercore
Expand All @@ -59,17 +60,6 @@
)


FROM_ANSIBLE_TO_HYPERCORE_MACHINE_TYPE = {
"UEFI": "scale-8.10",
"BIOS": "scale-7.2",
"vTPM+UEFI": "scale-uefi-tpm-9.2",
}

FROM_HYPERCORE_TO_ANSIBLE_MACHINE_TYPE = {
v: k for k, v in FROM_ANSIBLE_TO_HYPERCORE_MACHINE_TYPE.items()
}


VM_PAYLOAD_KEYS = [
"blockDevs",
"bootDevices",
Expand Down Expand Up @@ -109,6 +99,86 @@
)


class VmMachineType:
# In table below is left side output from 'sc vmmachinetypes show' command,
# on most recent HyperCore version.
_map_hypercore_to_ansible = {
"scale-bios-9.3": "BIOS",
"scale-7.2": "BIOS", # ?
"scale-5.4": "BIOS", # ?
"scale-8.10": "UEFI",
"scale-uefi-9.3": "UEFI",
"scale-uefi-tpm-compatible-9.3": "vTPM+UEFI-compatible",
"scale-bios-lsi-9.2": "BIOS", # ?
"scale-uefi-tpm-9.3": "vTPM+UEFI",
"scale-6.4": "BIOS", # ?
"scale-uefi-tpm-9.2": "vTPM+UEFI",
}

# Check in GUI what exactly is sent for selected VM machine type
# when creating a new VM.
# On 9.1.14.208456
_map_ansible_to_hypercore_91 = {
"BIOS": "scale-7.2",
"UEFI": "scale-8.10",
}
# On 9.2.13.211102
_map_ansible_to_hypercore_92 = {
"BIOS": "scale-7.2",
"UEFI": "scale-8.10",
"vTPM+UEFI": "scale-uefi-tpm-9.2",
}
# On 9.3.1.212486 (pre-release)
_map_ansible_to_hypercore_93 = {
"BIOS": "scale-bios-9.3",
"UEFI": "scale-uefi-9.3",
"vTPM+UEFI": "scale-uefi-tpm-9.3",
"vTPM+UEFI-compatible": "scale-uefi-tpm-compatible-9.3",
}
# HyperCore machineTypeKeyword can be: "bios" "uefi" "tpm" "tpm-compatible"
_map_ansible_to_hypercore_machine_type_keyword = {
"BIOS": "bios",
"UEFI": "uefi",
"vTPM+UEFI": "tpm",
"vTPM+UEFI-compatible": "tpm-compatible",
}

@classmethod
def from_ansible_to_hypercore(
cls, ansible_machine_type: str, hcversion: HyperCoreVersion
) -> str:
# Empty string is returned if ansible_machine_type cannot bve used with give HyperCore version.
if not ansible_machine_type:
return ""
if hcversion.verify(">=9.3.0"):
map_ansible_to_hypercore = cls._map_ansible_to_hypercore_93
elif hcversion.verify(">=9.2.0"):
map_ansible_to_hypercore = cls._map_ansible_to_hypercore_92
else:
# hcversion >=9.1.0, might work with earlier version too
map_ansible_to_hypercore = cls._map_ansible_to_hypercore_91
return map_ansible_to_hypercore.get(ansible_machine_type, "")

@classmethod
def from_hypercore_to_ansible(cls, vm_dict: dict) -> str:
# We receive whole vm_dict as returned by HC3,
# and use machineTypeKeyword if present.
if "machineTypeKeyword" in vm_dict:
_map_hypercore_machine_type_keyword_to_ansible = {
cls._map_ansible_to_hypercore_machine_type_keyword[k]: k
for k in cls._map_ansible_to_hypercore_machine_type_keyword
}
# "machineTypeKeyword" is available in HyperCore 9.3 or later
return _map_hypercore_machine_type_keyword_to_ansible.get(
vm_dict["machineTypeKeyword"], ""
)
return cls._map_hypercore_to_ansible.get(vm_dict["machineType"], "")

@classmethod
def from_ansible_to_hypercore_machine_type_keyword(cls, ansible_machine_type: str):
return cls._map_ansible_to_hypercore_machine_type_keyword[ansible_machine_type]


class VM(PayloadMapper):
# Fields cloudInitData, desiredDisposition and latestTaskTag are left out and won't be transferred between
# ansible and hypercore transformations
Expand Down Expand Up @@ -225,14 +295,7 @@ def from_hypercore(cls, vm_dict, rest_client) -> Optional[VM]:
snapshot_schedule = SnapshotSchedule.get_snapshot_schedule(
query={"uuid": vm_dict["snapshotScheduleUUID"]}, rest_client=rest_client
)
try:
machine_type = FROM_HYPERCORE_TO_ANSIBLE_MACHINE_TYPE[
vm_dict["machineType"]
]
except KeyError:
raise errors.ScaleComputingError(
f"Virtual machine: {vm_dict['name']} has an invalid Machine type: {vm_dict['machineType']}."
)
machine_type = VmMachineType.from_hypercore_to_ansible(vm_dict)
return cls(
uuid=vm_dict["uuid"], # No uuid when creating object from ansible
node_uuid=vm_dict["nodeUUID"], # Needed in vm_node_affinity
Expand Down Expand Up @@ -424,7 +487,7 @@ def import_vm(cls, rest_client, ansible_dict):
timeout=None,
)

def to_hypercore(self):
def to_hypercore(self, hcversion: HyperCoreVersion):
vm_dict = dict(
name=self.name,
description=self.description,
Expand All @@ -444,10 +507,16 @@ def to_hypercore(self):
vm_dict["state"] = FROM_ANSIBLE_TO_HYPERCORE_POWER_STATE.get(
self.power_state, "unknown-power-state-sorry"
)
if self.machine_type:
vm_dict["machineType"] = FROM_ANSIBLE_TO_HYPERCORE_MACHINE_TYPE[
self.machine_type
]

# if self.machine_type:
# vm_dict["machineType"] = FROM_ANSIBLE_TO_HYPERCORE_MACHINE_TYPE[
# self.machine_type
# ]
if self.machine_type and hcversion.verify("<9.3.0"):
vm_dict["machineType"] = VmMachineType.from_ansible_to_hypercore(
self.machine_type, hcversion
)

return vm_dict

def to_ansible(self):
Expand Down Expand Up @@ -510,7 +579,8 @@ def post_vm_payload(self, rest_client, ansible_dict):
# The rest of the keys from VM_PAYLOAD_KEYS will get set properly automatically
# Cloud init will be obtained through ansible_dict - If method will be reused outside of vm module,
# cloud_init should be one of the ansible_dict's keys.
payload = self.to_hypercore()
hcversion = HyperCoreVersion(rest_client)
payload = self.to_hypercore(hcversion)
VM._post_vm_payload_set_disks(payload, rest_client)
payload["netDevs"] = [
filter_dict(nic, *nic.keys()) for nic in payload["netDevs"]
Expand All @@ -521,6 +591,14 @@ def post_vm_payload(self, rest_client, ansible_dict):
if cloud_init_payload is not None:
dom["cloudInitData"] = cloud_init_payload
options = dict(attachGuestToolsISO=payload["attachGuestToolsISO"])
if hcversion.verify(">=9.3.0"):
machine_type_keyword = (
VmMachineType.from_ansible_to_hypercore_machine_type_keyword(
self.machine_type
)
)
if machine_type_keyword:
options.update(dict(machineTypeKeyword=machine_type_keyword))
return dict(dom=dom, options=options)

@staticmethod
Expand Down
27 changes: 25 additions & 2 deletions plugins/modules/vm.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,12 @@
- Scale I(Hardware) version.
- Required if creating a new VM.
- Only relevant when creating the VM. This property cannot be modified.
- HyperCore needs to support requested machine_type.
BIOS and UEFI - available since 9.1.
vTPM+UEFI - available since 9.2.
vTPM+UEFI-compatible - available since 9.3.
type: str
choices: [ BIOS, UEFI, vTPM+UEFI ]
choices: [ BIOS, UEFI, vTPM+UEFI, vTPM+UEFI-compatible ]
version_added: 1.1.0
notes:
- The C(record) return value will be changed from list (containing a single item) to dict.
Expand Down Expand Up @@ -399,8 +403,10 @@
ManageVMParams,
ManageVMDisks,
ManageVMNics,
VmMachineType,
)
from ..module_utils.task_tag import TaskTag
from ..module_utils.hypercore_version import HyperCoreVersion

MODULE_PATH = "scale_computing.hypercore.vm"

Expand Down Expand Up @@ -504,6 +510,19 @@ def ensure_absent(module, rest_client):
return False, [], dict(), reboot


def check_params(module, rest_client):
# Check if used machine_type is available in this HC3 version.
ansible_machine_type = module.params.get("machine_type")
if ansible_machine_type:
hcversion = HyperCoreVersion(rest_client)
hypercore_machine_type = VmMachineType.from_ansible_to_hypercore(
ansible_machine_type, hcversion
)
if not hypercore_machine_type:
msg = f"machine_type={ansible_machine_type} is not supported on HyperCore version {hcversion.version}."
module.fail_json(msg=msg)


def main():
module = AnsibleModule(
supports_check_mode=False, # False ATM
Expand Down Expand Up @@ -656,7 +675,10 @@ def main():
snapshot_schedule=dict(
type="str",
),
machine_type=dict(type="str", choices=["BIOS", "UEFI", "vTPM+UEFI"]),
machine_type=dict(
type="str",
choices=["BIOS", "UEFI", "vTPM+UEFI", "vTPM+UEFI-compatible"],
),
),
required_if=[
(
Expand Down Expand Up @@ -686,6 +708,7 @@ def main():
try:
client = Client.get_client(module.params["cluster_instance"])
rest_client = RestClient(client)
check_params(module, rest_client)
changed, record, diff, reboot = run(module, rest_client)
module.exit_json(changed=changed, record=record, diff=diff, vm_rebooted=reboot)
except errors.ScaleComputingError as e:
Expand Down
11 changes: 11 additions & 0 deletions tests/unit/plugins/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
TaskTag,
)

from ansible_collections.scale_computing.hypercore.plugins.module_utils.hypercore_version import (
HyperCoreVersion,
)


@pytest.fixture
def client(mocker):
Expand Down Expand Up @@ -193,3 +197,10 @@ def runner(module, params=None):
basic.AnsibleModule, exit_json=exit_json_mock, fail_json=fail_json_mock
)
return runner


@pytest.fixture
def hcversion(mocker, version_str="9.2.0.112233"):
hcv = HyperCoreVersion(rest_client=None) # type: ignore
hcv._version = version_str
return hcv
Loading

0 comments on commit 051b7da

Please sign in to comment.