Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add kubevirt_vmi_info module #129

Merged
merged 2 commits into from
Aug 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -172,5 +172,6 @@ jobs:
[
"kubevirt_vm",
"kubevirt_vm_info",
"kubevirt_vmi_info",
"inventory_kubevirt"
]
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -163,4 +163,5 @@ kubevirt.core
/tests/integration/targets/kubevirt_vm/wait_for_vm.yml
/tests/integration/targets/kubevirt_vm/files
/tests/integration/targets/kubevirt_vm_info/playbook.yml
/tests/integration/targets/kubevirt_vmi_info/playbook.yml
kubevirt-cache
50 changes: 50 additions & 0 deletions plugins/module_utils/info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
# Copyright 2024 Red Hat, Inc.
# Apache License 2.0 (see LICENSE or http://www.apache.org/licenses/LICENSE-2.0)


from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
)


INFO_ARG_SPEC = {
"api_version": {"default": "kubevirt.io/v1"},
"name": {},
"namespace": {},
"label_selectors": {"type": "list", "elements": "str", "default": []},
"field_selectors": {"type": "list", "elements": "str", "default": []},
"wait": {"type": "bool"},
"wait_sleep": {"type": "int", "default": 5},
"wait_timeout": {"type": "int", "default": 120},
}


def execute_info_module(module, kind, wait_condition):
"""
execute_info_module runs the lookup of resources.
"""
try:
client = get_api_client(module)
svc = K8sService(client, module)
facts = svc.find(
kind=kind,
api_version=module.params["api_version"],
name=module.params["name"],
namespace=module.params["namespace"],
label_selectors=module.params["label_selectors"],
field_selectors=module.params["field_selectors"],
wait=module.params["wait"],
wait_sleep=module.params["wait_sleep"],
wait_timeout=module.params["wait_timeout"],
condition=wait_condition,
)
module.exit_json(changed=False, **facts)
except CoreException as exc:
module.fail_from_exception(exc)
69 changes: 16 additions & 53 deletions plugins/modules/kubevirt_vm_info.py
Original file line number Diff line number Diff line change
@@ -161,63 +161,21 @@
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import (
get_api_client,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.exceptions import (
CoreException,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
)


def execute_module(module, svc):
"""
execute_module defines the kind and wait_condition and runs the lookup
of resources.
"""
# Set kind to query for VirtualMachines
KIND = "VirtualMachine"

# Set wait_condition to allow waiting for the ready state of the
# VirtualMachine based on the running parameter.
if module.params["running"] is None or module.params["running"]:
wait_condition = {"type": "Ready", "status": True}
else:
wait_condition = {"type": "Ready", "status": False, "reason": "VMINotExists"}

facts = svc.find(
kind=KIND,
api_version=module.params["api_version"],
name=module.params["name"],
namespace=module.params["namespace"],
label_selectors=module.params["label_selectors"],
field_selectors=module.params["field_selectors"],
wait=module.params["wait"],
wait_sleep=module.params["wait_sleep"],
wait_timeout=module.params["wait_timeout"],
condition=wait_condition,
)

module.exit_json(changed=False, **facts)
from ansible_collections.kubevirt.core.plugins.module_utils.info import (
INFO_ARG_SPEC,
execute_info_module,
)


def arg_spec():
"""
arg_spec defines the argument spec of this module.
"""
spec = {
"api_version": {"default": "kubevirt.io/v1"},
"name": {},
"namespace": {},
"label_selectors": {"type": "list", "elements": "str", "default": []},
"field_selectors": {"type": "list", "elements": "str", "default": []},
"running": {"type": "bool"},
"wait": {"type": "bool"},
"wait_sleep": {"type": "int", "default": 5},
"wait_timeout": {"type": "int", "default": 120},
}
spec.update(deepcopy(INFO_ARG_SPEC))
spec.update(deepcopy(AUTH_ARG_SPEC))

return spec
@@ -234,12 +192,17 @@ def main():
supports_check_mode=True,
)

try:
client = get_api_client(module)
svc = K8sService(client, module)
execute_module(module, svc)
except CoreException as exc:
module.fail_from_exception(exc)
# Set kind to query for VirtualMachines
kind = "VirtualMachine"

# Set wait_condition to allow waiting for the ready state of the
# VirtualMachine based on the running parameter.
if module.params["running"] is None or module.params["running"]:
wait_condition = {"type": "Ready", "status": True}
else:
wait_condition = {"type": "Ready", "status": False, "reason": "VMINotExists"}

execute_info_module(module, kind, wait_condition)


if __name__ == "__main__":
187 changes: 187 additions & 0 deletions plugins/modules/kubevirt_vmi_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2023 Red Hat, Inc.
# Based on the kubernetes.core.k8s_info module
# Apache License 2.0 (see LICENSE or http://www.apache.org/licenses/LICENSE-2.0)

from __future__ import absolute_import, division, print_function

__metaclass__ = type

DOCUMENTATION = """
module: kubevirt_vmi_info
short_description: Describe KubeVirt VirtualMachineInstances
author:
- "KubeVirt.io Project (!UNKNOWN)"
description:
- Use the Kubernetes Python client to perform read operations on KubeVirt C(VirtualMachineInstances).
- Pass options to find C(VirtualMachineInstances) as module arguments.
- Authenticate using either a config file, certificates, password or token.
- Supports check mode.
extends_documentation_fragment:
- kubevirt.core.kubevirt_auth_options
options:
api_version:
description:
- Use this to set the API version of KubeVirt.
type: str
default: kubevirt.io/v1
name:
description:
- Specify the name of the C(VirtualMachineInstance).
type: str
namespace:
description:
- Specify the namespace of C(VirtualMachineInstances).
type: str
label_selectors:
description: List of label selectors to use to filter results.
type: list
elements: str
default: []
field_selectors:
description: List of field selectors to use to filter results.
type: list
elements: str
default: []
wait:
description:
- Whether to wait for the C(VirtualMachineInstance) to end up in the ready state.
- By default this is waiting for the C(VirtualMachineInstance) to be up and running.
type: bool
wait_sleep:
description:
- Number of seconds to sleep between checks.
- Ignored if O(wait) is not set.
default: 5
type: int
wait_timeout:
description:
- How long in seconds to wait for the resource to end up in the ready state.
- Ignored if O(wait) is not set.
default: 120
type: int
requirements:
- "python >= 3.9"
- "kubernetes >= 28.1.0"
- "PyYAML >= 3.11"
"""

EXAMPLES = """
- name: Get an existing VirtualMachineInstance
kubevirt.core.kubevirt_vmi_info:
name: testvmi
namespace: default
register: default_testvmi
- name: Get a list of all VirtualMachinesInstances
kubevirt.core.kubevirt_vmi_info:
namespace: default
register: vmi_list
- name: Get a list of all VirtualMachineInstances from any namespace
kubevirt.core.kubevirt_vmi_info:
register: vmi_list
- name: Search for all VirtualMachineInstances labelled app=test
kubevirt.core.kubevirt_vmi_info:
label_selectors:
- app=test
- name: Wait until the VirtualMachineInstace is ready
kubevirt.core.kubevirt_vm_info:
name: testvm
namespace: default
wait: true
"""

RETURN = """
api_found:
description:
- Whether the specified O(api_version) and C(VirtualMachineInstance) C(Kind) were successfully mapped to an existing API on the target cluster.
returned: always
type: bool
resources:
description:
- The C(VirtualMachineInstances) that exist.
returned: success
type: complex
contains:
api_version:
description: The versioned schema of this representation of an object.
returned: success
type: str
kind:
description: Represents the C(REST) resource this object represents.
returned: success
type: str
metadata:
description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
returned: success
type: dict
spec:
description: Specific attributes of the C(VirtualMachineInstance). Can vary based on the O(api_version).
returned: success
type: dict
status:
description: Current status details for the C(VirtualMachineInstance).
returned: success
type: dict
"""

from copy import deepcopy

from ansible_collections.kubernetes.core.plugins.module_utils.ansiblemodule import (
AnsibleModule,
)
from ansible_collections.kubernetes.core.plugins.module_utils.args_common import (
AUTH_ARG_SPEC,
)
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.core import (
AnsibleK8SModule,
)

from ansible_collections.kubevirt.core.plugins.module_utils.info import (
INFO_ARG_SPEC,
execute_info_module,
)


def arg_spec():
"""
arg_spec defines the argument spec of this module.
"""
spec = {}
spec.update(deepcopy(INFO_ARG_SPEC))
spec.update(deepcopy(AUTH_ARG_SPEC))

return spec


def main():
"""
main instantiates the AnsibleK8SModule and runs the module.
"""
module = AnsibleK8SModule(
module_class=AnsibleModule,
argument_spec=arg_spec(),
supports_check_mode=True,
)

# Set kind to query for VirtualMachineInstances
kind = "VirtualMachineInstance"

# Set wait_condition to allow waiting for the ready state of the VirtualMachineInstance
wait_condition = {"type": "Ready", "status": True}

execute_info_module(module, kind, wait_condition)


if __name__ == "__main__":
main()
13 changes: 13 additions & 0 deletions tests/integration/targets/kubevirt_vmi_info/generate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
- name: Generate test files
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Generate test files
ansible.builtin.template:
src: "{{ item }}.yml.j2"
dest: "{{ item }}.yml"
mode: "0644"
with_items:
- playbook
67 changes: 67 additions & 0 deletions tests/integration/targets/kubevirt_vmi_info/playbook.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
- name: Create VM
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Create a VM
kubevirt.core.kubevirt_vm:
name: testvm
namespace: {{ NAMESPACE }}
instancetype:
name: u1.small
preference:
name: centos.stream9
spec:
domain:
devices: {}
volumes:
- containerDisk:
image: quay.io/containerdisks/centos-stream:9
name: containerdisk
wait: true
wait_timeout: 600

- name: Describe created VMI
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Describe a VMI
kubevirt.core.kubevirt_vmi_info:
name: testvm
namespace: {{ NAMESPACE }}
register: describe
- name: Assert module reported no changes
ansible.builtin.assert:
that:
- not describe.changed
- describe.resources | length == 1

- name: Delete VM
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Delete a VM
kubevirt.core.kubevirt_vm:
name: testvm
namespace: {{ NAMESPACE }}
state: absent
wait: true

- name: Verify VM deletion
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Delete a VM
kubevirt.core.kubevirt_vm:
name: testvm
namespace: {{ NAMESPACE }}
state: absent
register: delete
- name: Assert module reported no changes
ansible.builtin.assert:
that:
- not delete.changed
19 changes: 19 additions & 0 deletions tests/integration/targets/kubevirt_vmi_info/runme.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -eux

export ANSIBLE_CALLBACKS_ENABLED=ansible.posix.profile_tasks

NAMESPACE="test-kubevirt-vmi-info-$(tr -dc '[:lower:]' < /dev/urandom | head -c 5)"

cleanup() {
ansible localhost -m kubernetes.core.k8s -a "name=${NAMESPACE} api_version=v1 kind=Namespace state=absent"
rm -rf playbook.yml
}
trap cleanup EXIT

# Prepare the test environment
ansible localhost -m kubernetes.core.k8s -a "name=${NAMESPACE} api_version=v1 kind=Namespace state=present"
ansible-playbook -e "NAMESPACE=${NAMESPACE}" generate.yml

# Run the tests
ansible-playbook playbook.yml "$@"
1 change: 1 addition & 0 deletions tests/sanity/ignore-2.14.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
plugins/inventory/kubevirt.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vm.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vm_info.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vmi_info.py validate-modules:missing-gplv3-license
1 change: 1 addition & 0 deletions tests/sanity/ignore-2.15.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
plugins/inventory/kubevirt.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vm.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vm_info.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vmi_info.py validate-modules:missing-gplv3-license
1 change: 1 addition & 0 deletions tests/sanity/ignore-2.16.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
plugins/inventory/kubevirt.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vm.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vm_info.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vmi_info.py validate-modules:missing-gplv3-license
1 change: 1 addition & 0 deletions tests/sanity/ignore-2.17.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
plugins/inventory/kubevirt.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vm.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vm_info.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vmi_info.py validate-modules:missing-gplv3-license
1 change: 1 addition & 0 deletions tests/sanity/ignore-2.18.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
plugins/inventory/kubevirt.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vm.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vm_info.py validate-modules:missing-gplv3-license
plugins/modules/kubevirt_vmi_info.py validate-modules:missing-gplv3-license
5 changes: 4 additions & 1 deletion tests/unit/plugins/modules/test_kubevirt_vm_info.py
Original file line number Diff line number Diff line change
@@ -15,6 +15,9 @@
from ansible_collections.kubevirt.core.plugins.modules import (
kubevirt_vm_info,
)
from ansible_collections.kubevirt.core.plugins.module_utils import (
info,
)
from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock import (
AnsibleExitJson,
AnsibleFailJson,
@@ -81,7 +84,7 @@ def test_module_fails_when_required_args_missing(mocker):
)
def test_module(mocker, module_args, find_args):
mocker.patch.object(AnsibleModule, "exit_json", exit_json)
mocker.patch.object(kubevirt_vm_info, "get_api_client")
mocker.patch.object(info, "get_api_client")

find = mocker.patch.object(
K8sService,
81 changes: 81 additions & 0 deletions tests/unit/plugins/modules/test_kubevirt_vmi_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
# Copyright 2024 Red Hat, Inc.
# Apache License 2.0 (see LICENSE or http://www.apache.org/licenses/LICENSE-2.0)

from __future__ import absolute_import, division, print_function

__metaclass__ = type

import pytest

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
)
from ansible_collections.kubevirt.core.plugins.modules import (
kubevirt_vmi_info,
)
from ansible_collections.kubevirt.core.plugins.module_utils import (
info,
)
from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock import (
AnsibleExitJson,
exit_json,
set_module_args,
)

FIND_ARGS_DEFAULT = {
"kind": "VirtualMachineInstance",
"api_version": "kubevirt.io/v1",
"name": None,
"namespace": None,
"label_selectors": [],
"field_selectors": [],
"wait": None,
"wait_sleep": 5,
"wait_timeout": 120,
"condition": {"type": "Ready", "status": True},
}

FIND_ARGS_NAME_NAMESPACE = FIND_ARGS_DEFAULT | {
"name": "testvm",
"namespace": "default",
}

FIND_ARGS_LABEL_SELECTOR = FIND_ARGS_DEFAULT | {
"label_selectors": ["app=test"],
}

FIND_ARGS_FIELD_SELECTOR = FIND_ARGS_DEFAULT | {
"field_selectors": ["app=test"],
}


@pytest.mark.parametrize(
"module_args,find_args",
[
({}, FIND_ARGS_DEFAULT),
({"name": "testvm", "namespace": "default"}, FIND_ARGS_NAME_NAMESPACE),
({"label_selectors": "app=test"}, FIND_ARGS_LABEL_SELECTOR),
({"field_selectors": "app=test"}, FIND_ARGS_FIELD_SELECTOR),
],
)
def test_module(mocker, module_args, find_args):
mocker.patch.object(AnsibleModule, "exit_json", exit_json)
mocker.patch.object(info, "get_api_client")

find = mocker.patch.object(
K8sService,
"find",
return_value={
"api_found": True,
"failed": False,
"resources": [],
},
)

with pytest.raises(AnsibleExitJson):
set_module_args(module_args)
kubevirt_vmi_info.main()

find.assert_called_once_with(**find_args)

Unchanged files with check annotations Beta

# -*- coding: utf-8 -*-

Check warning on line 1 in tests/unit/plugins/inventory/blackbox/test_kubevirt_ansible_connection_winrm.py

GitHub Actions / linter

fqcn[deep]

Deep plugins directory is discouraged. Move 'tests/unit/plugins/inventory/blackbox/test_kubevirt_ansible_connection_winrm.py' directly under 'plugins/inventory' folder.
# Copyright 2024 Red Hat, Inc.
# Apache License 2.0 (see LICENSE or http://www.apache.org/licenses/LICENSE-2.0)
# -*- coding: utf-8 -*-

Check warning on line 1 in tests/unit/plugins/inventory/blackbox/test_kubevirt_set_composable_vars.py

GitHub Actions / linter

fqcn[deep]

Deep plugins directory is discouraged. Move 'tests/unit/plugins/inventory/blackbox/test_kubevirt_set_composable_vars.py' directly under 'plugins/inventory' folder.
# Copyright 2024 Red Hat, Inc.
# Apache License 2.0 (see LICENSE or http://www.apache.org/licenses/LICENSE-2.0)
# -*- coding: utf-8 -*-

Check warning on line 1 in tests/unit/plugins/inventory/blackbox/test_kubevirt_stopped_vm.py

GitHub Actions / linter

fqcn[deep]

Deep plugins directory is discouraged. Move 'tests/unit/plugins/inventory/blackbox/test_kubevirt_stopped_vm.py' directly under 'plugins/inventory' folder.
# Copyright 2024 Red Hat, Inc.
# Apache License 2.0 (see LICENSE or http://www.apache.org/licenses/LICENSE-2.0)