mirror of
https://github.com/kubevirt/kubevirt.core.git
synced 2026-03-26 19:03:16 +00:00
feat(kubevirt_vm): Add support for RunStrategy
This change adds support for setting the RunStrategy of a VM. Depending on the value set the wait condition for the VM is adjusted. For the values Always, RerunOnFailure or Once the wait condition will wait for the VM to run and be ready. For the value Halted the wait condition will wait for the VM to not exist. For the value Manual the wait condition is not set. Signed-off-by: Felix Matouschek <fmatouschek@redhat.com>
This commit is contained in:
42
examples/play-create-run-strategy.yml
Normal file
42
examples/play-create-run-strategy.yml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
- name: Playbook creating a virtual machine with multus network
|
||||||
|
hosts: localhost
|
||||||
|
tasks:
|
||||||
|
- name: Create VM
|
||||||
|
kubevirt.core.kubevirt_vm:
|
||||||
|
state: present
|
||||||
|
name: testvm
|
||||||
|
namespace: default
|
||||||
|
labels:
|
||||||
|
app: test
|
||||||
|
instancetype:
|
||||||
|
name: u1.medium
|
||||||
|
preference:
|
||||||
|
name: fedora
|
||||||
|
run_strategy: Manual
|
||||||
|
spec:
|
||||||
|
domain:
|
||||||
|
devices:
|
||||||
|
interfaces:
|
||||||
|
- name: default
|
||||||
|
masquerade: {}
|
||||||
|
- name: bridge-network
|
||||||
|
bridge: {}
|
||||||
|
networks:
|
||||||
|
- name: default
|
||||||
|
pod: {}
|
||||||
|
- name: bridge-network
|
||||||
|
multus:
|
||||||
|
networkName: kindexgw
|
||||||
|
volumes:
|
||||||
|
- containerDisk:
|
||||||
|
image: quay.io/containerdisks/fedora:latest
|
||||||
|
name: containerdisk
|
||||||
|
- cloudInitNoCloud:
|
||||||
|
userData: |-
|
||||||
|
#cloud-config
|
||||||
|
# The default username is: fedora
|
||||||
|
ssh_authorized_keys:
|
||||||
|
- ssh-ed25519 AAAA...
|
||||||
|
name: cloudinit
|
||||||
|
wait: true
|
||||||
@@ -61,8 +61,21 @@ options:
|
|||||||
running:
|
running:
|
||||||
description:
|
description:
|
||||||
- Specify whether the C(VirtualMachine) should be running or not.
|
- Specify whether the C(VirtualMachine) should be running or not.
|
||||||
|
- Mutually exclusive with O(run_strategy).
|
||||||
|
- Defaults to O(running=yes) when O(running) and O(run_strategy) are not set.
|
||||||
type: bool
|
type: bool
|
||||||
default: yes
|
run_strategy:
|
||||||
|
description:
|
||||||
|
- Specify the C(RunStrategy) of the C(VirtualMachine).
|
||||||
|
- Mutually exclusive with O(running).
|
||||||
|
type: str
|
||||||
|
choices:
|
||||||
|
- Always
|
||||||
|
- Halted
|
||||||
|
- Manual
|
||||||
|
- RerunOnFailure
|
||||||
|
- Once
|
||||||
|
version_added: 2.0.0
|
||||||
instancetype:
|
instancetype:
|
||||||
description:
|
description:
|
||||||
- Specify the C(Instancetype) matcher of the C(VirtualMachine).
|
- Specify the C(Instancetype) matcher of the C(VirtualMachine).
|
||||||
@@ -280,6 +293,13 @@ from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions imp
|
|||||||
CoreException,
|
CoreException,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
WAIT_CONDITION_READY = {"type": "Ready", "status": True}
|
||||||
|
WAIT_CONDITION_VMI_NOT_EXISTS = {
|
||||||
|
"type": "Ready",
|
||||||
|
"status": False,
|
||||||
|
"reason": "VMINotExists",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def create_vm(params: Dict) -> Dict:
|
def create_vm(params: Dict) -> Dict:
|
||||||
"""
|
"""
|
||||||
@@ -292,7 +312,6 @@ def create_vm(params: Dict) -> Dict:
|
|||||||
"namespace": params["namespace"],
|
"namespace": params["namespace"],
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
"running": params["running"],
|
|
||||||
"template": {"spec": {"domain": {"devices": {}}}},
|
"template": {"spec": {"domain": {"devices": {}}}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -312,6 +331,12 @@ def create_vm(params: Dict) -> Dict:
|
|||||||
if template_metadata:
|
if template_metadata:
|
||||||
vm["spec"]["template"]["metadata"] = template_metadata
|
vm["spec"]["template"]["metadata"] = template_metadata
|
||||||
|
|
||||||
|
if (run_strategy := params.get("run_strategy")) is not None:
|
||||||
|
vm["spec"]["runStrategy"] = run_strategy
|
||||||
|
else:
|
||||||
|
vm["spec"]["running"] = (
|
||||||
|
running if (running := params.get("running")) is not None else True
|
||||||
|
)
|
||||||
if (instancetype := params.get("instancetype")) is not None:
|
if (instancetype := params.get("instancetype")) is not None:
|
||||||
vm["spec"]["instancetype"] = instancetype
|
vm["spec"]["instancetype"] = instancetype
|
||||||
if (preference := params.get("preference")) is not None:
|
if (preference := params.get("preference")) is not None:
|
||||||
@@ -324,6 +349,21 @@ def create_vm(params: Dict) -> Dict:
|
|||||||
return vm
|
return vm
|
||||||
|
|
||||||
|
|
||||||
|
def set_wait_condition(module: AnsibleK8SModule) -> None:
|
||||||
|
"""
|
||||||
|
set_wait_condition sets the wait_condition to allow waiting for the ready
|
||||||
|
state of the VirtualMachine depending on the module parameters running
|
||||||
|
and run_strategy.
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
module.params["running"] is False
|
||||||
|
or (run_strategy := module.params["run_strategy"]) == "Halted"
|
||||||
|
):
|
||||||
|
module.params["wait_condition"] = WAIT_CONDITION_VMI_NOT_EXISTS
|
||||||
|
elif run_strategy != "Manual":
|
||||||
|
module.params["wait_condition"] = WAIT_CONDITION_READY
|
||||||
|
|
||||||
|
|
||||||
def arg_spec() -> Dict:
|
def arg_spec() -> Dict:
|
||||||
"""
|
"""
|
||||||
arg_spec defines the argument spec of this module.
|
arg_spec defines the argument spec of this module.
|
||||||
@@ -335,7 +375,10 @@ def arg_spec() -> Dict:
|
|||||||
"namespace": {"required": True},
|
"namespace": {"required": True},
|
||||||
"annotations": {"type": "dict"},
|
"annotations": {"type": "dict"},
|
||||||
"labels": {"type": "dict"},
|
"labels": {"type": "dict"},
|
||||||
"running": {"type": "bool", "default": True},
|
"running": {"type": "bool"},
|
||||||
|
"run_strategy": {
|
||||||
|
"choices": ["Always", "Halted", "Manual", "RerunOnFailure", "Once"]
|
||||||
|
},
|
||||||
"instancetype": {"type": "dict"},
|
"instancetype": {"type": "dict"},
|
||||||
"preference": {"type": "dict"},
|
"preference": {"type": "dict"},
|
||||||
"data_volume_templates": {"type": "list", "elements": "dict"},
|
"data_volume_templates": {"type": "list", "elements": "dict"},
|
||||||
@@ -376,6 +419,7 @@ def main() -> None:
|
|||||||
argument_spec=arg_spec(),
|
argument_spec=arg_spec(),
|
||||||
mutually_exclusive=[
|
mutually_exclusive=[
|
||||||
("name", "generate_name"),
|
("name", "generate_name"),
|
||||||
|
("running", "run_strategy"),
|
||||||
],
|
],
|
||||||
required_one_of=[
|
required_one_of=[
|
||||||
("name", "generate_name"),
|
("name", "generate_name"),
|
||||||
@@ -387,14 +431,7 @@ def main() -> None:
|
|||||||
module.params["resource_definition"] = create_vm(module.params)
|
module.params["resource_definition"] = create_vm(module.params)
|
||||||
|
|
||||||
# Set wait_condition to allow waiting for the ready state of the VirtualMachine
|
# Set wait_condition to allow waiting for the ready state of the VirtualMachine
|
||||||
if module.params["running"]:
|
set_wait_condition(module)
|
||||||
module.params["wait_condition"] = {"type": "Ready", "status": True}
|
|
||||||
else:
|
|
||||||
module.params["wait_condition"] = {
|
|
||||||
"type": "Ready",
|
|
||||||
"status": False,
|
|
||||||
"reason": "VMINotExists",
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
runner.run_module(module)
|
runner.run_module(module)
|
||||||
|
|||||||
@@ -101,11 +101,27 @@ VM_DEFINITION_STOPPED = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VM_DEFINITION_HALTED = {
|
||||||
|
"apiVersion": "kubevirt.io/v1",
|
||||||
|
"kind": "VirtualMachine",
|
||||||
|
"metadata": {
|
||||||
|
"name": "testvm",
|
||||||
|
"namespace": "default",
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"runStrategy": "Halted",
|
||||||
|
"template": {
|
||||||
|
"spec": {
|
||||||
|
"domain": {"devices": {}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
MODULE_PARAMS_DEFAULT = {
|
MODULE_PARAMS_DEFAULT = {
|
||||||
"api_version": "kubevirt.io/v1",
|
"api_version": "kubevirt.io/v1",
|
||||||
"annotations": None,
|
"annotations": None,
|
||||||
"labels": None,
|
"labels": None,
|
||||||
"running": True,
|
|
||||||
"instancetype": None,
|
"instancetype": None,
|
||||||
"preference": None,
|
"preference": None,
|
||||||
"data_volume_templates": None,
|
"data_volume_templates": None,
|
||||||
@@ -174,6 +190,12 @@ MODULE_PARAMS_STOPPED = MODULE_PARAMS_DEFAULT | {
|
|||||||
"running": False,
|
"running": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MODULE_PARAMS_HALTED = MODULE_PARAMS_DEFAULT | {
|
||||||
|
"name": "testvm",
|
||||||
|
"namespace": "default",
|
||||||
|
"run_strategy": "Halted",
|
||||||
|
}
|
||||||
|
|
||||||
MODULE_PARAMS_DELETE = MODULE_PARAMS_DEFAULT | {
|
MODULE_PARAMS_DELETE = MODULE_PARAMS_DEFAULT | {
|
||||||
"name": "testvm",
|
"name": "testvm",
|
||||||
"namespace": "default",
|
"namespace": "default",
|
||||||
@@ -183,24 +205,37 @@ MODULE_PARAMS_DELETE = MODULE_PARAMS_DEFAULT | {
|
|||||||
|
|
||||||
K8S_MODULE_PARAMS_CREATE = MODULE_PARAMS_CREATE | {
|
K8S_MODULE_PARAMS_CREATE = MODULE_PARAMS_CREATE | {
|
||||||
"generate_name": None,
|
"generate_name": None,
|
||||||
|
"running": None,
|
||||||
|
"run_strategy": None,
|
||||||
"resource_definition": VM_DEFINITION_CREATE,
|
"resource_definition": VM_DEFINITION_CREATE,
|
||||||
"wait_condition": {"type": "Ready", "status": True},
|
"wait_condition": {"type": "Ready", "status": True},
|
||||||
}
|
}
|
||||||
|
|
||||||
K8S_MODULE_PARAMS_RUNNING = MODULE_PARAMS_RUNNING | {
|
K8S_MODULE_PARAMS_RUNNING = MODULE_PARAMS_RUNNING | {
|
||||||
"generate_name": None,
|
"generate_name": None,
|
||||||
|
"run_strategy": None,
|
||||||
"resource_definition": VM_DEFINITION_RUNNING,
|
"resource_definition": VM_DEFINITION_RUNNING,
|
||||||
"wait_condition": {"type": "Ready", "status": True},
|
"wait_condition": {"type": "Ready", "status": True},
|
||||||
}
|
}
|
||||||
|
|
||||||
K8S_MODULE_PARAMS_STOPPED = MODULE_PARAMS_STOPPED | {
|
K8S_MODULE_PARAMS_STOPPED = MODULE_PARAMS_STOPPED | {
|
||||||
"generate_name": None,
|
"generate_name": None,
|
||||||
|
"run_strategy": None,
|
||||||
"resource_definition": VM_DEFINITION_STOPPED,
|
"resource_definition": VM_DEFINITION_STOPPED,
|
||||||
"wait_condition": {"type": "Ready", "status": False, "reason": "VMINotExists"},
|
"wait_condition": {"type": "Ready", "status": False, "reason": "VMINotExists"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
K8S_MODULE_PARAMS_HALTED = MODULE_PARAMS_HALTED | {
|
||||||
|
"generate_name": None,
|
||||||
|
"running": None,
|
||||||
|
"resource_definition": VM_DEFINITION_HALTED,
|
||||||
|
"wait_condition": {"type": "Ready", "status": False, "reason": "VMINotExists"},
|
||||||
|
}
|
||||||
|
|
||||||
K8S_MODULE_PARAMS_DELETE = MODULE_PARAMS_DELETE | {
|
K8S_MODULE_PARAMS_DELETE = MODULE_PARAMS_DELETE | {
|
||||||
"generate_name": None,
|
"generate_name": None,
|
||||||
|
"running": None,
|
||||||
|
"run_strategy": None,
|
||||||
"resource_definition": VM_DEFINITION_RUNNING,
|
"resource_definition": VM_DEFINITION_RUNNING,
|
||||||
"wait_condition": {"type": "Ready", "status": True},
|
"wait_condition": {"type": "Ready", "status": True},
|
||||||
}
|
}
|
||||||
@@ -227,6 +262,12 @@ K8S_MODULE_PARAMS_DELETE = MODULE_PARAMS_DELETE | {
|
|||||||
VM_DEFINITION_STOPPED,
|
VM_DEFINITION_STOPPED,
|
||||||
"update",
|
"update",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
MODULE_PARAMS_HALTED,
|
||||||
|
K8S_MODULE_PARAMS_HALTED,
|
||||||
|
VM_DEFINITION_HALTED,
|
||||||
|
"update",
|
||||||
|
),
|
||||||
(
|
(
|
||||||
MODULE_PARAMS_DELETE,
|
MODULE_PARAMS_DELETE,
|
||||||
K8S_MODULE_PARAMS_DELETE,
|
K8S_MODULE_PARAMS_DELETE,
|
||||||
@@ -262,10 +303,15 @@ def test_module(mocker, module_params, k8s_module_params, vm_definition, method)
|
|||||||
|
|
||||||
CREATE_VM_PARAMS = {
|
CREATE_VM_PARAMS = {
|
||||||
"api_version": "kubevirt.io/v1",
|
"api_version": "kubevirt.io/v1",
|
||||||
"running": True,
|
|
||||||
"namespace": "default",
|
"namespace": "default",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CREATE_VM_PARAMS_RUN_STRATEGY = {
|
||||||
|
"api_version": "kubevirt.io/v1",
|
||||||
|
"namespace": "default",
|
||||||
|
"run_strategy": "Manual",
|
||||||
|
}
|
||||||
|
|
||||||
CREATE_VM_PARAMS_ANNOTATIONS = CREATE_VM_PARAMS | {
|
CREATE_VM_PARAMS_ANNOTATIONS = CREATE_VM_PARAMS | {
|
||||||
"annotations": {"test": "test"},
|
"annotations": {"test": "test"},
|
||||||
}
|
}
|
||||||
@@ -341,6 +387,24 @@ CREATED_VM = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CREATED_VM_RUN_STRATEGY = {
|
||||||
|
"apiVersion": "kubevirt.io/v1",
|
||||||
|
"kind": "VirtualMachine",
|
||||||
|
"metadata": {
|
||||||
|
"namespace": "default",
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"runStrategy": "Manual",
|
||||||
|
"template": {
|
||||||
|
"spec": {
|
||||||
|
"domain": {
|
||||||
|
"devices": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
CREATED_VM_LABELS = {
|
CREATED_VM_LABELS = {
|
||||||
"apiVersion": "kubevirt.io/v1",
|
"apiVersion": "kubevirt.io/v1",
|
||||||
"kind": "VirtualMachine",
|
"kind": "VirtualMachine",
|
||||||
@@ -528,6 +592,7 @@ CREATED_VM_SPECS = {
|
|||||||
"params,expected",
|
"params,expected",
|
||||||
[
|
[
|
||||||
(CREATE_VM_PARAMS, CREATED_VM),
|
(CREATE_VM_PARAMS, CREATED_VM),
|
||||||
|
(CREATE_VM_PARAMS_RUN_STRATEGY, CREATED_VM_RUN_STRATEGY),
|
||||||
(CREATE_VM_PARAMS_ANNOTATIONS, CREATED_VM_ANNOTATIONS),
|
(CREATE_VM_PARAMS_ANNOTATIONS, CREATED_VM_ANNOTATIONS),
|
||||||
(CREATE_VM_PARAMS_LABELS, CREATED_VM_LABELS),
|
(CREATE_VM_PARAMS_LABELS, CREATED_VM_LABELS),
|
||||||
(CREATE_VM_PARAMS_INSTANCETYPE, CREATED_VM_INSTANCETYPE),
|
(CREATE_VM_PARAMS_INSTANCETYPE, CREATED_VM_INSTANCETYPE),
|
||||||
@@ -540,3 +605,46 @@ CREATED_VM_SPECS = {
|
|||||||
)
|
)
|
||||||
def test_create_vm(params, expected):
|
def test_create_vm(params, expected):
|
||||||
assert kubevirt_vm.create_vm(params) == expected
|
assert kubevirt_vm.create_vm(params) == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"params,expected",
|
||||||
|
[
|
||||||
|
({"running": None, "run_strategy": "Manual"}, {}),
|
||||||
|
(
|
||||||
|
{"running": None, "run_strategy": None},
|
||||||
|
{"wait_condition": kubevirt_vm.WAIT_CONDITION_READY},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"running": True, "run_strategy": None},
|
||||||
|
{"wait_condition": kubevirt_vm.WAIT_CONDITION_READY},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"running": None, "run_strategy": "Always"},
|
||||||
|
{"wait_condition": kubevirt_vm.WAIT_CONDITION_READY},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"running": None, "run_strategy": "RerunOnFailure"},
|
||||||
|
{"wait_condition": kubevirt_vm.WAIT_CONDITION_READY},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"running": None, "run_strategy": "Once"},
|
||||||
|
{"wait_condition": kubevirt_vm.WAIT_CONDITION_READY},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"running": False, "run_strategy": None},
|
||||||
|
{"wait_condition": kubevirt_vm.WAIT_CONDITION_VMI_NOT_EXISTS},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"running": None, "run_strategy": "Halted"},
|
||||||
|
{"wait_condition": kubevirt_vm.WAIT_CONDITION_VMI_NOT_EXISTS},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_set_wait_condition(mocker, params, expected):
|
||||||
|
module = mocker.Mock()
|
||||||
|
module.params = params
|
||||||
|
|
||||||
|
kubevirt_vm.set_wait_condition(module)
|
||||||
|
|
||||||
|
assert module.params == params | expected
|
||||||
|
|||||||
Reference in New Issue
Block a user