From e9ce9d5b7a618a17c8fadfbfcd2942a61d1b3e43 Mon Sep 17 00:00:00 2001 From: Justin Cinkelj Date: Wed, 20 Dec 2023 19:32:52 +0100 Subject: [PATCH] Fixing problem with multiple VM reboots Try to differentiate power_state vm power_action Surprise - vm_params can send RESET to stopped VM, it does not fail (no 500 error), but VM does not get booted (it was not running before). Signed-off-by: Justin Cinkelj --- plugins/module_utils/vm.py | 131 +++++++--- plugins/modules/vm.py | 3 +- .../targets/i_vm_params/tasks/01_main.yml | 232 +++++++++++++++++ .../i_vm_params/tasks/02_machine_type.yml | 240 ++++++++++++++++++ .../targets/i_vm_params/tasks/main.yml | 220 +--------------- tests/unit/plugins/module_utils/test_disk.py | 4 + tests/unit/plugins/module_utils/test_vm.py | 7 +- 7 files changed, 578 insertions(+), 259 deletions(-) create mode 100644 tests/integration/targets/i_vm_params/tasks/01_main.yml create mode 100644 tests/integration/targets/i_vm_params/tasks/02_machine_type.yml diff --git a/plugins/module_utils/vm.py b/plugins/module_utils/vm.py index 747fd52a..ce79ebf9 100644 --- a/plugins/module_utils/vm.py +++ b/plugins/module_utils/vm.py @@ -52,9 +52,18 @@ stop="STOP", reboot="REBOOT", reset="RESET", - started="START", + started="START", # TODO remove? ) +FROM_ANSIBLE_POWER_ACTION_TO_ANSIBLE_POWER_STATE = dict( + start="started", + # both "stop" and "shutdown" result in "stopped" state + shutdown="stopped", + stop="stopped", + reboot="started", + reset="started", + started="started", # TODO remove? +) VM_PAYLOAD_KEYS = [ "blockDevs", @@ -83,7 +92,9 @@ "NVRAM", ] - +# List VM params that require VM reboot, +# either because VM then can be changed only if VM is shutdown, +# or because change is applied only after shutdown. REBOOT_LOOKUP = dict( vm_name=False, description=False, @@ -208,6 +219,7 @@ def __init__( tags=None, # tags are stored internally as list of strings description=None, power_state=None, + power_action=None, nics=None, # nics represents a list of type Nic disks=None, # disks represents a list of type Nic # boot_devices are stored as list of nics and/or disks internally. @@ -228,7 +240,6 @@ def __init__( self.tags = tags self.description = description self.mem = memory - self.power_state = power_state self.numVCPU = vcpu self.nics = nics or [] self.disks = disks or [] @@ -240,8 +251,34 @@ def __init__( self.machine_type = machine_type self.replication_source_vm_uuid = replication_source_vm_uuid - if power_state not in FROM_HYPERCORE_TO_ANSIBLE_POWER_STATE.values(): + power_state_values = list(FROM_HYPERCORE_TO_ANSIBLE_POWER_STATE.values()) + [None] + power_action_values = list(FROM_ANSIBLE_TO_HYPERCORE_POWER_ACTION.keys()) + [None] + if power_state not in power_state_values: raise AssertionError(f"Unknown VM power_state={power_state}") + if power_action not in power_action_values: + raise AssertionError(f"Unknown VM power_action={power_action}") + # VM.from_hypercore() will get only power_state + # VM.from_ansible() will get only power_action + if power_state and power_action: + # is bug, or is this useful? + raise AssertionError( + f"Both power_state={power_state} and power_action={power_action} are set" + ) + if power_state is None and power_action is None: + # is bug, or is this useful? + raise AssertionError( + f"Neither power_state={power_state} nor power_action={power_action} is set" + ) + self._power_state = power_state + self._power_action = power_action + if power_state and power_action is None: + # compute required action (if current state is known) + pass + if power_state is None and power_action: + # compute final power_state after action is applied + pass + # self._power_state = FROM_ANSIBLE_POWER_ACTION_TO_ANSIBLE_POWER_STATE[power_action] + self._initially_running = power_state == "started" # .was_nice_shutdown_tried is True if nice ACPI shutdown was tried self._was_nice_shutdown_tried = False @@ -268,9 +305,8 @@ def from_ansible(cls, ansible_data): vm_dict = ansible_data # Unfortunately we were using in playbooks "start" instead of "started", etc. - power_state_value = vm_dict.get("power_state", None) - if power_state_value == "start": - power_state_value = "started" + # Ansible module param with name "power_state" is actually power_action. + power_action = vm_dict.get("power_state", None) return cls( uuid=vm_dict.get("uuid", None), # No uuid when creating object from ansible @@ -286,7 +322,7 @@ def from_ansible(cls, ansible_data): boot_devices=vm_dict.get("boot_devices", []), attach_guest_tools_iso=vm_dict["attach_guest_tools_iso"] or False, operating_system=vm_dict.get("operating_system"), - power_state=power_state_value, + power_action=power_action, machine_type=vm_dict.get("machine_type", None), ) @@ -536,9 +572,9 @@ def to_hypercore(self, hcversion: HyperCoreVersion): if self.operating_system: vm_dict["operatingSystem"] = self.operating_system # state attribute is used by HC3 only during VM create. - if self.power_state: + if self._power_action: vm_dict["state"] = FROM_ANSIBLE_TO_HYPERCORE_POWER_ACTION.get( - self.power_state, "unknown-power-state-sorry" + self._power_action, "unknown-power-state-sorry" ) if self.machine_type and hcversion.verify("<9.3.0"): @@ -554,7 +590,7 @@ def to_ansible(self): vm_name=self.name, description=self.description, operating_system=self.operating_system, - power_state=self.power_state, + power_state=self._power_state, memory=self.mem, vcpu=self.numVCPU, disks=[disk.to_ansible() for disk in self.disk_list], @@ -713,7 +749,7 @@ def clone_vm(self, rest_client, ansible_dict): timeout=None, ) - def __eq__(self, other): + def __eq__(self, other: "VM"): """One VM is equal to another if it has ALL attributes exactly the same""" return all( ( @@ -724,7 +760,8 @@ def __eq__(self, other): self.tags == other.tags, self.description == other.description, self.mem == other.mem, - self.power_state == other.power_state, + self._power_state == other._power_state, + self._power_action == other._power_action, self.numVCPU == other.numVCPU, self.nics == other.nics, self.disks == other.disks, @@ -812,22 +849,22 @@ def get_vm_and_boot_devices(cls, ansible_dict, rest_client): ] return vm, boot_devices_uuid, boot_devices_ansible - def update_vm_power_state(self, module, rest_client, desired_power_state): + def update_vm_power_state(self, module, rest_client, desired_power_action): """Sets the power state to what is stored in self.power_state""" # desired_power_state must be present in FROM_ANSIBLE_TO_HYPERCORE_ACTION_STATE's keys - if not self.power_state: + if not self._power_state: raise errors.ScaleComputingError("No information about VM's power state.") # keep a record what was done - if desired_power_state == "start": + if desired_power_action == "start": if self._was_start_tried: raise AssertionError("VM _was_start_tried already set") self._was_start_tried = True - if desired_power_state == "shutdown": + if desired_power_action == "shutdown": if self._was_nice_shutdown_tried: raise AssertionError("VM _was_nice_shutdown_tried already set") self._was_nice_shutdown_tried = True - if desired_power_state == "stop": + if desired_power_action == "stop": if self._was_force_shutdown_tried: raise AssertionError("VM _was_force_shutdown_tried already set") self._was_force_shutdown_tried = True @@ -839,7 +876,7 @@ def update_vm_power_state(self, module, rest_client, desired_power_state): dict( virDomainUUID=self.uuid, actionType=FROM_ANSIBLE_TO_HYPERCORE_POWER_ACTION[ - desired_power_state + desired_power_action ], cause="INTERNAL", ) @@ -936,10 +973,21 @@ def wait_shutdown(self, module, rest_client): return False def vm_power_up(self, module, rest_client): - # Powers up a VM in case it was shutdown during module action. - # Do not power up VM that was initially stopped. + # Powers up a VM in case: + # - VM was shutdown during module execution or + # - started/running state was explicitly requested (by module param power_state). + # But: VM is not started if + # - VM was initially stopped and + # - module param power_state is omitted or contains "stop". if self.was_vm_shutdown() and self._initially_running: self.update_vm_power_state(module, rest_client, "start") + return + # Also start VM if module power_state requires a power on. + # Field _power_action is set only if VM instance was created with from_ansible(); + # it is None if VM instance was created with from_hypercore(). + requested_power_action = module.params.get("power_state") + if requested_power_action == "start": + self.update_vm_power_state(module, rest_client, "start") def was_vm_shutdown(self) -> bool: """ @@ -1064,9 +1112,24 @@ def _to_be_changed(vm, module, param_subset: List[str]): # is in FROM_ANSIBLE_TO_HYPERCORE_POWER_ACTION.keys(), whereas vm.power_state # is in FROM_HYPERCORE_TO_ANSIBLE_POWER_STATE.values(). # state in playbook is different than read from HC3 (start/started) + # E.g. "start" in "started", "stop" in "stopped". # TODO double check if text above is still true - is_substring = module.params["power_state"] not in vm.power_state - changed_params["power_state"] = is_substring + # is_substring = module.params["power_state"] not in vm._power_action + # changed_params["power_state"] = is_substring + # q(module.params["power_state"], vm._power_state, vm._power_action, changed_params["power_state"]) + + # vm._power_state describes actual state from HC3. + # module.params["power_state"] is ansible power_action, describing desired state. + requested_power_action = module.params["power_state"] + if requested_power_action in ["reset", "reboot"]: + # "reset" and "reboot" needs to be applied always. + changed_params["power_state"] = True + else: + desired_power_state = FROM_ANSIBLE_POWER_ACTION_TO_ANSIBLE_POWER_STATE[ + requested_power_action + ] + changed_params["power_state"] = desired_power_state != vm._power_state + if module.params.get("machine_type") is not None: changed_params["machine_type"] = ( vm.machine_type != module.params["machine_type"] @@ -1132,7 +1195,7 @@ def _build_after_diff(module, rest_client): if module.params["vcpu"] is not None: after["vcpu"] = vm.numVCPU if module.params["power_state"]: - after["power_state"] = vm.power_state + after["power_state"] = vm._power_state if module.params["snapshot_schedule"] is not None: after["snapshot_schedule"] = vm.snapshot_schedule return after @@ -1153,7 +1216,7 @@ def _build_before_diff(vm, module): if module.params["vcpu"]: before["vcpu"] = vm.numVCPU if module.params["power_state"]: - before["power_state"] = vm.power_state + before["power_state"] = vm._power_state if module.params["snapshot_schedule"] is not None: before["snapshot_schedule"] = vm.snapshot_schedule return before @@ -1172,17 +1235,8 @@ def set_vm_params(cls, module, rest_client, vm, param_subset: List[str]): TaskTag.wait_task(rest_client, task_tag) if ManageVMParams._needs_reboot( module, changed_parameters - ) and vm.power_state not in ["stop", "stopped", "shutdown"]: + ) and vm._power_action not in ["stop", "stopped", "shutdown"]: vm.do_shutdown_steps(module, rest_client) - else: - # boot/reboot/reset VM if requested - # power_state needs different endpoint - # Wait_task in update_vm_power_state doesn't handle check_mode - # TODO likely this is to early for VM module - if module.params["power_state"] and not module.check_mode: - vm.update_vm_power_state( - module, rest_client, module.params["power_state"] - ) return ( True, dict( @@ -1218,11 +1272,14 @@ def _check_if_required_disks_are_present( f"machine_type={module.params['machine_type']} not included in set_vm_params." ) # At end of module execution we will have VM with final_disks. - final_disks = vm.disks - if module.params["disks"]: + if "disks" in module.params: + # vm module, "disks" param was passed final_disks = [ Disk.from_ansible(disk) for disk in module.params["disks"] ] + else: + # vm_params has no disks, we need to check the actual VM disks + final_disks = vm.disks nvram_disks = [disk for disk in final_disks if disk.type == "nvram"] vtpm_disks = [disk for disk in final_disks if disk.type == "vtpm"] fail_message_requirements = [] diff --git a/plugins/modules/vm.py b/plugins/modules/vm.py index 3940d776..804662cc 100644 --- a/plugins/modules/vm.py +++ b/plugins/modules/vm.py @@ -525,7 +525,8 @@ def ensure_absent(module, rest_client): reboot = False vm = VM.get_by_name(module.params, rest_client) if vm: - if vm.power_state != "shutdown": # First, shut it off and then delete + if vm._power_state != "shutdown": # First, shut it off and then delete + # TODO ==shutdown or ==stopped ?? vm.update_vm_power_state(module, rest_client, "stop") task_tag = rest_client.delete_record( "{0}/{1}".format("/rest/v1/VirDomain", vm.uuid), module.check_mode diff --git a/tests/integration/targets/i_vm_params/tasks/01_main.yml b/tests/integration/targets/i_vm_params/tasks/01_main.yml new file mode 100644 index 00000000..17726745 --- /dev/null +++ b/tests/integration/targets/i_vm_params/tasks/01_main.yml @@ -0,0 +1,232 @@ +--- +- environment: + SC_HOST: "{{ sc_host }}" + SC_USERNAME: "{{ sc_config[sc_host].sc_username }}" + SC_PASSWORD: "{{ sc_config[sc_host].sc_password }}" + SC_TIMEOUT: "{{ sc_timeout }}" + + block: + # =================================================================== + # Prepare test VM with known initial state + - name: Delete XLAB-vm_params_CI_test and XLAB-vm_params_CI_test - updated (if it exists from before) + scale_computing.hypercore.vm: &delete-XLAB-vm_params_CI_test + vm_name: "{{ item }}" + state: absent + loop: + - XLAB-vm_params_CI_test + - XLAB-vm_params_CI_test - updated + + - name: Create XLAB-vm_params_CI_test + scale_computing.hypercore.vm: + vm_name: XLAB-vm_params_CI_test + description: VM for testing vm_params module + power_state: start + machine_type: BIOS + state: present + tags: + - Xlab + memory: "{{ '512 MB' | human_to_bytes }}" + vcpu: 2 + disks: [] + nics: [] + register: initial + - ansible.builtin.assert: + that: + - initial is changed + + - name: Delete snapshot schedule (if it exists from before) + scale_computing.hypercore.snapshot_schedule: &delete-snapshot-schedule + name: demo-snap-schedule-params + state: absent + + - name: Setup snapshot schedule + scale_computing.hypercore.snapshot_schedule: + name: demo-snap-schedule-params + state: present + recurrences: + - name: weekly-tuesday + frequency: "FREQ=WEEKLY;INTERVAL=1;BYDAY=TU" # RFC-2445 + start: "2010-01-01 00:00:00" + local_retention: "{{ 10 * 7*24*60*60 }}" # 10 days, unit seconds + register: snapshot_schedule + + # =================================================================== + # Set VMs params, except for name + - name: Set VMs params + scale_computing.hypercore.vm_params: &Set-VMs-parameters + vm_name: XLAB-vm_params_CI_test + description: VM for testing vm_params module - updated + tags: + - Xlab + - updated + vcpu: 3 + memory: "{{ '1024 MB' | human_to_bytes }}" + power_state: stop + snapshot_schedule: "{{ snapshot_schedule.record[0].name }}" + force_reboot: True + shutdown_timeout: 10 + register: output + - ansible.builtin.assert: + that: + - output is changed + - output.vm_rebooted is false + + - name: Check VMs params changed + scale_computing.hypercore.vm_info: + vm_name: XLAB-vm_params_CI_test + register: updated + - ansible.builtin.assert: + that: + - updated.records[0].description == "VM for testing vm_params module - updated" + - updated.records[0].tags == ["Xlab", "updated"] + - updated.records[0].vcpu == 3 + - updated.records[0].memory == 1073741824 + - updated.records[0].power_state == "stopped" + - updated.records[0].snapshot_schedule == snapshot_schedule.record[0].name + + - name: Set VMs params - idempotence + scale_computing.hypercore.vm_params: *Set-VMs-parameters + register: output + - ansible.builtin.assert: + that: + - output is not changed + + - name: Check VMs params aren't changed + scale_computing.hypercore.vm_info: + vm_name: XLAB-vm_params_CI_test + register: output + - ansible.builtin.assert: + that: + - output.records[0].description == updated.records[0].description + - output.records[0].tags == updated.records[0].tags + - output.records[0].vcpu == updated.records[0].vcpu + - output.records[0].memory == updated.records[0].memory + - output.records[0].power_state == updated.records[0].power_state + - output.records[0].snapshot_schedule == updated.records[0].snapshot_schedule + + # =================================================================== + # Rename VM + - name: Rename VM - reboot not needed + scale_computing.hypercore.vm_params: + vm_name: XLAB-vm_params_CI_test + vm_name_new: XLAB-vm_params_CI_test - updated + register: output + - ansible.builtin.assert: + that: + - output is changed + - output.vm_rebooted is false + + - name: Check VMs name changed + scale_computing.hypercore.vm_info: + vm_name: XLAB-vm_params_CI_test - updated + register: output + - ansible.builtin.assert: + that: + - output.records | length == 1 + + - name: Check that VM with old name doesn't exist + scale_computing.hypercore.vm_info: + vm_name: XLAB-vm_params_CI_test + register: output + - ansible.builtin.assert: + that: + - output.records | length == 0 + + # =================================================================== + # Delete snapshot schedule, description and tags + - name: Delete snapshot schedule, description and tags + scale_computing.hypercore.vm_params: &delete-schedule-description-tags + vm_name: XLAB-vm_params_CI_test - updated + snapshot_schedule: "" + tags: [""] + description: "" + register: output + - ansible.builtin.assert: + that: + - output is changed + - output.vm_rebooted is false + + - name: Check VMs params changed + scale_computing.hypercore.vm_info: + vm_name: XLAB-vm_params_CI_test - updated + register: output + - ansible.builtin.assert: + that: + - output.records[0].description == "" + - output.records[0].tags == [""] + - not output.records[0].snapshot_schedule + + - name: Delete snapshot schedule, description and tags - idempotence + scale_computing.hypercore.vm_params: *delete-schedule-description-tags + register: output + - ansible.builtin.assert: + that: + - output is not changed + - output.vm_rebooted is false + + - name: Check VMs params aren't changed + scale_computing.hypercore.vm_info: + vm_name: XLAB-vm_params_CI_test - updated + register: output + - ansible.builtin.assert: + that: + - output.records[0].description == "" + - output.records[0].tags == [""] + - not output.records[0].snapshot_schedule + + # =================================================================== + # Set nonexisting snapshot schedule + - name: Set VMs snapshot_schedule - not found + scale_computing.hypercore.vm_params: + vm_name: XLAB-vm_params_CI_test - updated + snapshot_schedule: not_existing + ignore_errors: True + register: output + - ansible.builtin.assert: + that: + - "'No records from endpoint /rest/v1/VirDomainSnapshotSchedule' in output.msg" + + - name: Check VMs snapshot schedule isn't changed + scale_computing.hypercore.vm_info: + vm_name: XLAB-vm_params_CI_test - updated + register: output + - ansible.builtin.assert: + that: + - not output.records[0].snapshot_schedule + + # =================================================================== + # Set nonexisting snapshot schedule + - name: Set VMs power_state - applied + scale_computing.hypercore.vm_params: + vm_name: XLAB-vm_params_CI_test - updated + power_state: reset + # ignore_errors: True + register: output + - ansible.builtin.assert: + that: + - output is succeeded + # changes since RESET power action was sent to VM. + # But no change is observable as HC3 did not boot the (stopped) VM. + - output is changed + - output.vm_rebooted is false + + - name: Check VMs power_state isn't changed + scale_computing.hypercore.vm_info: + vm_name: XLAB-vm_params_CI_test - updated + register: output + - ansible.builtin.assert: + that: + - output is succeeded + # HC3 9.2.22 - RESET is accepted, but VM remains stopped as it was stopped before. + - output.records[0].power_state == "stopped" + + # =================================================================== + # Cleanup + - name: Delete snapshot schedule + scale_computing.hypercore.snapshot_schedule: *delete-snapshot-schedule + + - name: Delete XLAB-vm_params_CI_test and XLAB-vm_params_CI_test - updated (if it exists from before) + scale_computing.hypercore.vm: *delete-XLAB-vm_params_CI_test + loop: + - XLAB-vm_params_CI_test + - XLAB-vm_params_CI_test - updated diff --git a/tests/integration/targets/i_vm_params/tasks/02_machine_type.yml b/tests/integration/targets/i_vm_params/tasks/02_machine_type.yml new file mode 100644 index 00000000..e4857c1e --- /dev/null +++ b/tests/integration/targets/i_vm_params/tasks/02_machine_type.yml @@ -0,0 +1,240 @@ +--- +- environment: + SC_HOST: "{{ sc_host }}" + SC_USERNAME: "{{ sc_config[sc_host].sc_username }}" + SC_PASSWORD: "{{ sc_config[sc_host].sc_password }}" + SC_TIMEOUT: "{{ sc_timeout }}" + vars: + vm_name: XLAB-vm_params_CI_test-UEFI-2 + + block: + # =================================================================== + # Prepare test VM with known initial state + - &delete-vm + name: Delete VM vm_params machine_type + scale_computing.hypercore.vm: + vm_name: "{{ vm_name }}" + state: absent + + - name: Create VM vm_params machine_type + scale_computing.hypercore.vm: + vm_name: "{{ vm_name }}" + description: VM for testing vm_params module machine_type + power_state: stop + machine_type: BIOS + state: present + tags: + - Xlab + memory: "{{ '512 MB' | human_to_bytes }}" + vcpu: 2 + disks: [] + nics: [] + register: initial + - ansible.builtin.assert: + that: + - initial is changed + + # =================================================================== + # Set VMs params, except for name + - &set-vm-to-uefi + name: Set VM to UEFI mode, missing NVRAM disk + scale_computing.hypercore.vm_params: + vm_name: "{{ vm_name }}" + description: VM for testing vm_params module machine_type - updated + tags: + - Xlab + - updated + vcpu: 3 + memory: "{{ '1024 MB' | human_to_bytes }}" + power_state: start + force_reboot: True + shutdown_timeout: 10 + machine_type: UEFI + register: output + ignore_errors: true + - ansible.builtin.assert: + that: + - output is failed + - output is not changed + - output.msg == "Changing machineType to UEFI requires nvram disk." + + - &check-vm-not-updated + block: + - name: Check VMs params are not changed + scale_computing.hypercore.vm_info: + vm_name: "{{ vm_name }}" + register: updated + - ansible.builtin.assert: + that: + - updated.records | length == 1 + - updated.records[0].description == "VM for testing vm_params module machine_type" + - updated.records[0].tags == ["Xlab"] + - updated.records[0].vcpu == 2 + - updated.records[0].memory == 536870912 + - updated.records[0].power_state == "stopped" + - updated.records[0].snapshot_schedule == "" + + # =================================================================== + # Add required NVRAM disk, then use vm_params to set UEFI mode + - name: Add NVRAM disk to VM + scale_computing.hypercore.vm: + vm_name: "{{ vm_name }}" + description: VM for testing vm_params module machine_type + power_state: stop + machine_type: BIOS + state: present + tags: + - Xlab + memory: "{{ '512 MB' | human_to_bytes }}" + vcpu: 2 + disks: + - type: nvram + disk_slot: -1 + size: 540672 + nics: [ ] + register: output + - assert: + that: + - output is changed + - output.vm_rebooted == false + - output.record[0].disks | length == 1 + - output.record[0].disks.0.type == "nvram" + - output.record[0].disks.0.disk_slot == -1 + # check with vm_info NVRAM disk was added + - name: Get vm_info + scale_computing.hypercore.vm_info: + vm_name: "{{ vm_name }}" + register: output + - assert: + that: + - output.records[0].disks | length == 1 + - output.records[0].disks.0.type == "nvram" + - output.records[0].disks.0.disk_slot == -1 + + # =================================================================== + # Change VM machine_type + - name: Set VM to UEFI mode, NVRAM disk present + <<: *set-vm-to-uefi + - ansible.builtin.assert: + that: + - output is changed + - output.vm_rebooted is false + + - &check-vm-updated + block: + - name: Check VMs params were changed + scale_computing.hypercore.vm_info: + vm_name: "{{ vm_name }}" + register: updated + - ansible.builtin.assert: + that: + - updated.records | length == 1 + - updated.records[0].description == "VM for testing vm_params module machine_type - updated" + - updated.records[0].tags == ["Xlab", "updated"] + - updated.records[0].vcpu == 3 + - updated.records[0].memory == 1073741824 + - updated.records[0].power_state == "started" + - updated.records[0].snapshot_schedule == "" + + # =================================================================== + # Change VM machine_type - idempotence + - name: Set VM to UEFI mode, NVRAM disk present, idempotence + <<: *set-vm-to-uefi + - ansible.builtin.assert: + that: + - output is not changed + - output.vm_rebooted is false + + - *check-vm-updated + + # =================================================================== + # Delete snapshot schedule, description and tags + - name: Delete snapshot schedule, description and tags from VM + scale_computing.hypercore.vm_params: &delete-schedule-description-tags + vm_name: "{{ vm_name }}" + snapshot_schedule: "" + tags: [""] + description: "" + register: output + - ansible.builtin.assert: + that: + - output is changed + - output.vm_rebooted is false + + - name: Check VMs params changed + scale_computing.hypercore.vm_info: + vm_name: "{{ vm_name }}" + register: output + - ansible.builtin.assert: + that: + - output.records[0].description == "" + - output.records[0].tags == [""] + - not output.records[0].snapshot_schedule + + - name: Delete snapshot schedule, description and tags - idempotence + scale_computing.hypercore.vm_params: *delete-schedule-description-tags + register: output + - ansible.builtin.assert: + that: + - output is not changed + - output.vm_rebooted is false + + - name: Check VMs params aren't changed + scale_computing.hypercore.vm_info: + vm_name: "{{ vm_name }}" + register: output + - ansible.builtin.assert: + that: + - output.records[0].description == "" + - output.records[0].tags == [""] + - not output.records[0].snapshot_schedule + + # =================================================================== + # Set nonexisting snapshot schedule + - name: Set VMs snapshot_schedule - not found + scale_computing.hypercore.vm_params: + vm_name: "{{ vm_name }}" + snapshot_schedule: not_existing + ignore_errors: True + register: output + - ansible.builtin.assert: + that: + - "'No records from endpoint /rest/v1/VirDomainSnapshotSchedule' in output.msg" + + - name: Check VMs snapshot schedule isn't changed + scale_computing.hypercore.vm_info: + vm_name: "{{ vm_name }}" + register: output + - ansible.builtin.assert: + that: + - not output.records[0].snapshot_schedule + + # =================================================================== + # Set nonexisting snapshot schedule + - name: Set VMs power_state - applied + scale_computing.hypercore.vm_params: + vm_name: "{{ vm_name }}" + power_state: reset + # ignore_errors: True + register: output + - ansible.builtin.assert: + that: + - output is succeeded + # changes since RESET power action was sent to VM. + # Change is observable as HC3 should reset a running VM. + - output is changed + - output.vm_rebooted is false # true # todo - is this reported? should we pretend reboot==reset - it is not same. + + - name: Check VMs power_state isn't changed + scale_computing.hypercore.vm_info: + vm_name: "{{ vm_name }}" + register: output + - ansible.builtin.assert: + that: + - output is succeeded + # HC3 9.2.22 - RESET is accepted, but VM remains stopped as it was stopped before. + - output.records[0].power_state == "started" + + # =================================================================== + # Cleanup + - *delete-vm diff --git a/tests/integration/targets/i_vm_params/tasks/main.yml b/tests/integration/targets/i_vm_params/tasks/main.yml index 8dab8616..7848482c 100644 --- a/tests/integration/targets/i_vm_params/tasks/main.yml +++ b/tests/integration/targets/i_vm_params/tasks/main.yml @@ -6,221 +6,5 @@ SC_TIMEOUT: "{{ sc_timeout }}" block: - # =================================================================== - # Prepare test VM with known initial state - - name: Delete XLAB-vm_params_CI_test and XLAB-vm_params_CI_test - updated (if it exists from before) - scale_computing.hypercore.vm: &delete-XLAB-vm_params_CI_test - vm_name: "{{ item }}" - state: absent - loop: - - XLAB-vm_params_CI_test - - XLAB-vm_params_CI_test - updated - - - name: Create XLAB-vm_params_CI_test - scale_computing.hypercore.vm: - vm_name: XLAB-vm_params_CI_test - description: VM for testing vm_params module - power_state: start - machine_type: BIOS - state: present - tags: - - Xlab - memory: "{{ '512 MB' | human_to_bytes }}" - vcpu: 2 - disks: [] - nics: [] - register: initial - - ansible.builtin.assert: - that: - - initial is changed - - - name: Delete snapshot schedule (if it exists from before) - scale_computing.hypercore.snapshot_schedule: &delete-snapshot-schedule - name: demo-snap-schedule-params - state: absent - - - name: Setup snapshot schedule - scale_computing.hypercore.snapshot_schedule: - name: demo-snap-schedule-params - state: present - recurrences: - - name: weekly-tuesday - frequency: "FREQ=WEEKLY;INTERVAL=1;BYDAY=TU" # RFC-2445 - start: "2010-01-01 00:00:00" - local_retention: "{{ 10 * 7*24*60*60 }}" # 10 days, unit seconds - register: snapshot_schedule - - # =================================================================== - # Set VMs params, except for name - - name: Set VMs params - scale_computing.hypercore.vm_params: &Set-VMs-parameters - vm_name: XLAB-vm_params_CI_test - description: VM for testing vm_params module - updated - tags: - - Xlab - - updated - vcpu: 3 - memory: "{{ '1024 MB' | human_to_bytes }}" - power_state: stop - snapshot_schedule: "{{ snapshot_schedule.record[0].name }}" - force_reboot: True - shutdown_timeout: 10 - register: output - - ansible.builtin.assert: - that: - - output is changed - - output.vm_rebooted is false - - - name: Check VMs params changed - scale_computing.hypercore.vm_info: - vm_name: XLAB-vm_params_CI_test - register: updated - - ansible.builtin.assert: - that: - - updated.records[0].description == "VM for testing vm_params module - updated" - - updated.records[0].tags == ["Xlab", "updated"] - - updated.records[0].vcpu == 3 - - updated.records[0].memory == 1073741824 - - updated.records[0].power_state == "stopped" - - updated.records[0].snapshot_schedule == snapshot_schedule.record[0].name - - - name: Set VMs params - idempotence - scale_computing.hypercore.vm_params: *Set-VMs-parameters - register: output - - ansible.builtin.assert: - that: - - output is not changed - - - name: Check VMs params aren't changed - scale_computing.hypercore.vm_info: - vm_name: XLAB-vm_params_CI_test - register: output - - ansible.builtin.assert: - that: - - output.records[0].description == updated.records[0].description - - output.records[0].tags == updated.records[0].tags - - output.records[0].vcpu == updated.records[0].vcpu - - output.records[0].memory == updated.records[0].memory - - output.records[0].power_state == updated.records[0].power_state - - output.records[0].snapshot_schedule == updated.records[0].snapshot_schedule - - # =================================================================== - # Rename VM - - name: Rename VM - reboot not needed - scale_computing.hypercore.vm_params: - vm_name: XLAB-vm_params_CI_test - vm_name_new: XLAB-vm_params_CI_test - updated - register: output - - ansible.builtin.assert: - that: - - output is changed - - output.vm_rebooted is false - - - name: Check VMs name changed - scale_computing.hypercore.vm_info: - vm_name: XLAB-vm_params_CI_test - updated - register: output - - ansible.builtin.assert: - that: - - output.records | length == 1 - - - name: Check that VM with old name dosen't exist - scale_computing.hypercore.vm_info: - vm_name: XLAB-vm_params_CI_test - register: output - - ansible.builtin.assert: - that: - - output.records | length == 0 - - # =================================================================== - # Delete snapshot schedule, description and tags - - name: Delete snapshot schedule, description and tags - scale_computing.hypercore.vm_params: &delete-schedule-description-tags - vm_name: XLAB-vm_params_CI_test - updated - snapshot_schedule: "" - tags: [""] - description: "" - register: output - - ansible.builtin.assert: - that: - - output is changed - - output.vm_rebooted is false - - - name: Check VMs params changed - scale_computing.hypercore.vm_info: - vm_name: XLAB-vm_params_CI_test - updated - register: output - - ansible.builtin.assert: - that: - - output.records[0].description == "" - - output.records[0].tags == [""] - - not output.records[0].snapshot_schedule - - - name: Delete snapshot schedule, description and tags - idempotence - scale_computing.hypercore.vm_params: *delete-schedule-description-tags - register: output - - ansible.builtin.assert: - that: - - output is not changed - - output.vm_rebooted is false - - - name: Check VMs params aren't changed - scale_computing.hypercore.vm_info: - vm_name: XLAB-vm_params_CI_test - updated - register: output - - ansible.builtin.assert: - that: - - output.records[0].description == "" - - output.records[0].tags == [""] - - not output.records[0].snapshot_schedule - - # =================================================================== - # Set nonexisting snapshot schedule - - name: Set VMs snapshot_schedule - not found - scale_computing.hypercore.vm_params: - vm_name: XLAB-vm_params_CI_test - updated - snapshot_schedule: not_existing - ignore_errors: True - register: output - - ansible.builtin.assert: - that: - - "'No records from endpoint /rest/v1/VirDomainSnapshotSchedule' in output.msg" - - - name: Check VMs snapshot schedule isn't changed - scale_computing.hypercore.vm_info: - vm_name: XLAB-vm_params_CI_test - updated - register: output - - ansible.builtin.assert: - that: - - not output.records[0].snapshot_schedule - - # =================================================================== - # Set nonexisting snapshot schedule - - name: Set VMs power_state - not applied - scale_computing.hypercore.vm_params: - vm_name: XLAB-vm_params_CI_test - updated - power_state: reset - ignore_errors: True - register: output - - ansible.builtin.assert: - that: - - "'Unexpected response - 500' in output.msg" - - - name: Check VMs power_state isn't changed - scale_computing.hypercore.vm_info: - vm_name: XLAB-vm_params_CI_test - updated - register: output - - ansible.builtin.assert: - that: - - output.records[0].power_state == "stopped" - - # =================================================================== - # Cleanup - - name: Delete snapshot schedule - scale_computing.hypercore.snapshot_schedule: *delete-snapshot-schedule - - - name: Delete XLAB-vm_params_CI_test and XLAB-vm_params_CI_test - updated (if it exists from before) - scale_computing.hypercore.vm: *delete-XLAB-vm_params_CI_test - loop: - - XLAB-vm_params_CI_test - - XLAB-vm_params_CI_test - updated + - include_tasks: 01_main.yml + - include_tasks: 02_machine_type.yml diff --git a/tests/unit/plugins/module_utils/test_disk.py b/tests/unit/plugins/module_utils/test_disk.py index b7086933..bbb4c959 100644 --- a/tests/unit/plugins/module_utils/test_disk.py +++ b/tests/unit/plugins/module_utils/test_disk.py @@ -270,6 +270,7 @@ def test_post_and_patch_payload__nvram_post(self): name="VM-name", memory=42, vcpu=2, + power_state="started", ) disk = Disk( @@ -302,6 +303,7 @@ def test_post_and_patch_payload__nvram_patch(self): name="VM-name", memory=42, vcpu=2, + power_state="started", ) disk = Disk( @@ -342,6 +344,7 @@ def test_post_and_patch_payload__vtpm_post(self): name="VM-name", memory=42, vcpu=2, + power_state="started", ) disk = Disk( @@ -374,6 +377,7 @@ def test_post_and_patch_payload__vtpm_patch(self): name="VM-name", memory=42, vcpu=2, + power_state="started", ) disk = Disk( diff --git a/tests/unit/plugins/module_utils/test_vm.py b/tests/unit/plugins/module_utils/test_vm.py index d6305e1c..128fa846 100644 --- a/tests/unit/plugins/module_utils/test_vm.py +++ b/tests/unit/plugins/module_utils/test_vm.py @@ -35,12 +35,13 @@ class TestVM: def test_vm_from_ansible(self): vm_dict = dict( + # this are module params uuid=None, # No uuid when creating object from ansible vm_name="VM-name", tags=["XLAB-test-tag1", "XLAB-test-tag2"], description="desc", memory=42, - power_state="started", + power_state="start", vcpu=2, nics=[], disks=[], @@ -55,7 +56,7 @@ def test_vm_from_ansible(self): tags=["XLAB-test-tag1", "XLAB-test-tag2"], description="desc", memory=42, - power_state="started", + power_action="start", vcpu=2, nics=[], disks=[], @@ -157,7 +158,7 @@ def test_vm_to_hypercore(self, hcversion): tags=["XLAB-test-tag1", "XLAB-test-tag2"], description="desc", memory=42, - power_state="started", + power_action="start", vcpu=2, nics=[], disks=[],