summaryrefslogtreecommitdiffstats
path: root/collections-debian-merged/ansible_collections/community/kubevirt/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'collections-debian-merged/ansible_collections/community/kubevirt/plugins')
-rw-r--r--collections-debian-merged/ansible_collections/community/kubevirt/plugins/README.md31
-rw-r--r--collections-debian-merged/ansible_collections/community/kubevirt/plugins/doc_fragments/kubevirt_common_options.py131
-rw-r--r--collections-debian-merged/ansible_collections/community/kubevirt/plugins/doc_fragments/kubevirt_vm_options.py102
-rw-r--r--collections-debian-merged/ansible_collections/community/kubevirt/plugins/inventory/kubevirt.py256
-rw-r--r--collections-debian-merged/ansible_collections/community/kubevirt/plugins/module_utils/kubevirt.py464
-rw-r--r--collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_cdi_upload.py184
-rw-r--r--collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_preset.py154
-rw-r--r--collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_pvc.py457
-rw-r--r--collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_rs.py211
-rw-r--r--collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_template.py385
-rw-r--r--collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_vm.py469
11 files changed, 2844 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/community/kubevirt/plugins/README.md b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/README.md
new file mode 100644
index 00000000..6541cf7c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/README.md
@@ -0,0 +1,31 @@
+# Collections Plugins Directory
+
+This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that
+is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that
+would contain module utils and modules respectively.
+
+Here is an example directory of the majority of plugins currently supported by Ansible:
+
+```
+└── plugins
+ ├── action
+ ├── become
+ ├── cache
+ ├── callback
+ ├── cliconf
+ ├── connection
+ ├── filter
+ ├── httpapi
+ ├── inventory
+ ├── lookup
+ ├── module_utils
+ ├── modules
+ ├── netconf
+ ├── shell
+ ├── strategy
+ ├── terminal
+ ├── test
+ └── vars
+```
+
+A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible/2.9/plugins/plugins.html). \ No newline at end of file
diff --git a/collections-debian-merged/ansible_collections/community/kubevirt/plugins/doc_fragments/kubevirt_common_options.py b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/doc_fragments/kubevirt_common_options.py
new file mode 100644
index 00000000..ed25b724
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/doc_fragments/kubevirt_common_options.py
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2018, KubeVirt Team <@kubevirt>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+ DOCUMENTATION = r'''
+options:
+ resource_definition:
+ description:
+ - "A partial YAML definition of the object being created/updated. Here you can define Kubernetes
+ resource parameters not covered by this module's parameters."
+ - "NOTE: I(resource_definition) has lower priority than module parameters. If you try to define e.g.
+ I(metadata.namespace) here, that value will be ignored and I(namespace) used instead."
+ aliases:
+ - definition
+ - inline
+ type: dict
+ wait:
+ description:
+ - "I(True) if the module should wait for the resource to get into desired state."
+ type: bool
+ default: yes
+ force:
+ description:
+ - If set to C(no), and I(state) is C(present), an existing object will be replaced.
+ type: bool
+ default: no
+ wait_timeout:
+ description:
+ - The amount of time in seconds the module should wait for the resource to get into desired state.
+ type: int
+ default: 120
+ wait_sleep:
+ description:
+ - Number of seconds to sleep between checks.
+ default: 5
+ memory:
+ description:
+ - The amount of memory to be requested by virtual machine.
+ - For example 1024Mi.
+ type: str
+ memory_limit:
+ description:
+ - The maximum memory to be used by virtual machine.
+ - For example 1024Mi.
+ type: str
+ machine_type:
+ description:
+ - QEMU machine type is the actual chipset of the virtual machine.
+ type: str
+ merge_type:
+ description:
+ - Whether to override the default patch merge approach with a specific type.
+ - If more than one merge type is given, the merge types will be tried in order.
+ - "Defaults to C(['strategic-merge', 'merge']), which is ideal for using the same parameters
+ on resource kinds that combine Custom Resources and built-in resources, as
+ Custom Resource Definitions typically aren't updatable by the usual strategic merge."
+ - "See U(https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment)"
+ type: list
+ choices: [ json, merge, strategic-merge ]
+ cpu_shares:
+ description:
+ - "Specify CPU shares."
+ type: int
+ cpu_limit:
+ description:
+ - "Is converted to its millicore value and multiplied by 100. The resulting value is the total amount of CPU time that a container can use
+ every 100ms. A virtual machine cannot use more than its share of CPU time during this interval."
+ type: int
+ cpu_cores:
+ description:
+ - "Number of CPU cores."
+ type: int
+ cpu_model:
+ description:
+ - "CPU model."
+ - "You can check list of available models here: U(https://github.com/libvirt/libvirt/blob/master/src/cpu_map/index.xml)."
+ - "I(Note:) User can define default CPU model via as I(default-cpu-model) in I(kubevirt-config) I(ConfigMap), if not set I(host-model) is used."
+ - "I(Note:) Be sure that node CPU model where you run a VM, has the same or higher CPU family."
+ - "I(Note:) If CPU model wasn't defined, the VM will have CPU model closest to one that used on the node where the VM is running."
+ type: str
+ bootloader:
+ description:
+ - "Specify the bootloader of the virtual machine."
+ - "All virtual machines use BIOS by default for booting."
+ type: str
+ smbios_uuid:
+ description:
+ - "In order to provide a consistent view on the virtualized hardware for the guest OS, the SMBIOS UUID can be set."
+ type: str
+ cpu_features:
+ description:
+ - "List of dictionary to fine-tune features provided by the selected CPU model."
+ - "I(Note): Policy attribute can either be omitted or contain one of the following policies: force, require, optional, disable, forbid."
+ - "I(Note): In case a policy is omitted for a feature, it will default to require."
+ - "More information about policies: U(https://libvirt.org/formatdomain.html#elementsCPU)"
+ type: list
+ headless:
+ description:
+ - "Specify if the virtual machine should have attached a minimal Video and Graphics device configuration."
+ - "By default a minimal Video and Graphics device configuration will be applied to the VirtualMachineInstance. The video device is vga
+ compatible and comes with a memory size of 16 MB."
+ hugepage_size:
+ description:
+ - "Specify huge page size."
+ type: str
+ tablets:
+ description:
+ - "Specify tablets to be used as input devices"
+ type: list
+ hostname:
+ description:
+ - "Specifies the hostname of the virtual machine. The hostname will be set either by dhcp, cloud-init if configured or virtual machine
+ name will be used."
+ subdomain:
+ description:
+ - "If specified, the fully qualified virtual machine hostname will be hostname.subdomain.namespace.svc.cluster_domain. If not specified,
+ the virtual machine will not have a domain name at all. The DNS entry will resolve to the virtual machine, no matter if the virtual machine
+ itself can pick up a hostname."
+requirements:
+ - python >= 2.7
+ - openshift >= 0.8.2
+notes:
+ - "In order to use this module you have to install Openshift Python SDK.
+ To ensure it's installed with correct version you can create the following task:
+ I(pip: name=openshift>=0.8.2)"
+'''
diff --git a/collections-debian-merged/ansible_collections/community/kubevirt/plugins/doc_fragments/kubevirt_vm_options.py b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/doc_fragments/kubevirt_vm_options.py
new file mode 100644
index 00000000..89d17a99
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/doc_fragments/kubevirt_vm_options.py
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2018, KubeVirt Team <@kubevirt>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ # Standard oVirt documentation fragment
+ DOCUMENTATION = r'''
+options:
+ disks:
+ description:
+ - List of dictionaries which specify disks of the virtual machine.
+ - "A disk can be made accessible via four different types: I(disk), I(lun), I(cdrom), I(floppy)."
+ - "All possible configuration options are available in U(https://kubevirt.io/api-reference/master/definitions.html#_v1_disk)"
+ - Each disk must have specified a I(volume) that declares which volume type of the disk
+ All possible configuration options of volume are available in U(https://kubevirt.io/api-reference/master/definitions.html#_v1_volume).
+ type: list
+ labels:
+ description:
+ - Labels are key/value pairs that are attached to virtual machines. Labels are intended to be used to
+ specify identifying attributes of virtual machines that are meaningful and relevant to users, but do not directly
+ imply semantics to the core system. Labels can be used to organize and to select subsets of virtual machines.
+ Labels can be attached to virtual machines at creation time and subsequently added and modified at any time.
+ - More on labels that are used for internal implementation U(https://kubevirt.io/user-guide/#/misc/annotations_and_labels)
+ type: dict
+ interfaces:
+ description:
+ - An interface defines a virtual network interface of a virtual machine (also called a frontend).
+ - All possible configuration options interfaces are available in U(https://kubevirt.io/api-reference/master/definitions.html#_v1_interface)
+ - Each interface must have specified a I(network) that declares which logical or physical device it is connected to (also called as backend).
+ All possible configuration options of network are available in U(https://kubevirt.io/api-reference/master/definitions.html#_v1_network).
+ type: list
+ cloud_init_nocloud:
+ description:
+ - "Represents a cloud-init NoCloud user-data source. The NoCloud data will be added
+ as a disk to the virtual machine. A proper cloud-init installation is required inside the guest.
+ More information U(https://kubevirt.io/api-reference/master/definitions.html#_v1_cloudinitnocloudsource)"
+ type: dict
+ affinity:
+ description:
+ - "Describes node affinity scheduling rules for the vm."
+ type: dict
+ suboptions:
+ soft:
+ description:
+ - "The scheduler will prefer to schedule vms to nodes that satisfy the affinity expressions specified by this field, but it may choose a
+ node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for
+ each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute
+ a sum by iterating through the elements of this field and adding C(weight) to the sum if the node has vms which matches the corresponding
+ C(term); the nodes with the highest sum are the most preferred."
+ type: dict
+ hard:
+ description:
+ - "If the affinity requirements specified by this field are not met at scheduling time, the vm will not be scheduled onto the node. If
+ the affinity requirements specified by this field cease to be met at some point during vm execution (e.g. due to a vm label update), the
+ system may or may not try to eventually evict the vm from its node. When there are multiple elements, the lists of nodes corresponding to
+ each C(term) are intersected, i.e. all terms must be satisfied."
+ type: dict
+ node_affinity:
+ description:
+ - "Describes vm affinity scheduling rules e.g. co-locate this vm in the same node, zone, etc. as some other vms"
+ type: dict
+ suboptions:
+ soft:
+ description:
+ - "The scheduler will prefer to schedule vms to nodes that satisfy the affinity expressions specified by this field, but it may choose
+ a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e.
+ for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.),
+ compute a sum by iterating through the elements of this field and adding C(weight) to the sum if the node matches the corresponding
+ match_expressions; the nodes with the highest sum are the most preferred."
+ type: dict
+ hard:
+ description:
+ - "If the affinity requirements specified by this field are not met at scheduling time, the vm will not be scheduled onto the node. If
+ the affinity requirements specified by this field cease to be met at some point during vm execution (e.g. due to an update), the system
+ may or may not try to eventually evict the vm from its node."
+ type: dict
+ anti_affinity:
+ description:
+ - "Describes vm anti-affinity scheduling rules e.g. avoid putting this vm in the same node, zone, etc. as some other vms."
+ type: dict
+ suboptions:
+ soft:
+ description:
+ - "The scheduler will prefer to schedule vms to nodes that satisfy the anti-affinity expressions specified by this field, but it may
+ choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights,
+ i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling anti-affinity expressions,
+ etc.), compute a sum by iterating through the elements of this field and adding C(weight) to the sum if the node has vms which matches
+ the corresponding C(term); the nodes with the highest sum are the most preferred."
+ type: dict
+ hard:
+ description:
+ - "If the anti-affinity requirements specified by this field are not met at scheduling time, the vm will not be scheduled onto the node.
+ If the anti-affinity requirements specified by this field cease to be met at some point during vm execution (e.g. due to a vm label
+ update), the system may or may not try to eventually evict the vm from its node. When there are multiple elements, the lists of nodes
+ corresponding to each C(term) are intersected, i.e. all terms must be satisfied."
+ type: dict
+'''
diff --git a/collections-debian-merged/ansible_collections/community/kubevirt/plugins/inventory/kubevirt.py b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/inventory/kubevirt.py
new file mode 100644
index 00000000..b2d06468
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/inventory/kubevirt.py
@@ -0,0 +1,256 @@
+# Copyright (c) 2018 Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+
+DOCUMENTATION = '''
+ name: kubevirt
+ plugin_type: inventory
+ author:
+ - KubeVirt Team (@kubevirt)
+
+ short_description: KubeVirt inventory source
+ extends_documentation_fragment:
+ - inventory_cache
+ - constructed
+ description:
+ - Fetch running VirtualMachines for one or more namespaces.
+ - Groups by namespace, namespace_vms and labels.
+ - Uses kubevirt.(yml|yaml) YAML configuration file to set parameter values.
+
+ options:
+ plugin:
+ description: token that ensures this is a source file for the 'kubevirt' plugin.
+ required: True
+ choices: ['kubevirt', 'community.general.kubevirt', 'community.kubevirt.kubevirt']
+ type: str
+ host_format:
+ description:
+ - Specify the format of the host in the inventory group.
+ default: "{namespace}-{name}-{uid}"
+ connections:
+ type: list
+ description:
+ - Optional list of cluster connection settings. If no connections are provided, the default
+ I(~/.kube/config) and active context will be used, and objects will be returned for all namespaces
+ the active user is authorized to access.
+ suboptions:
+ name:
+ description:
+ - Optional name to assign to the cluster. If not provided, a name is constructed from the server
+ and port.
+ type: str
+ kubeconfig:
+ description:
+ - Path to an existing Kubernetes config file. If not provided, and no other connection
+ options are provided, the OpenShift client will attempt to load the default
+ configuration file from I(~/.kube/config.json). Can also be specified via K8S_AUTH_KUBECONFIG
+ environment variable.
+ type: str
+ context:
+ description:
+ - The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment
+ variable.
+ type: str
+ host:
+ description:
+ - Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable.
+ type: str
+ api_key:
+ description:
+ - Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment
+ variable.
+ type: str
+ username:
+ description:
+ - Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME
+ environment variable.
+ type: str
+ password:
+ description:
+ - Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD
+ environment variable.
+ type: str
+ cert_file:
+ description:
+ - Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE
+ environment variable.
+ type: str
+ key_file:
+ description:
+ - Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_HOST
+ environment variable.
+ type: str
+ ssl_ca_cert:
+ description:
+ - Path to a CA certificate used to authenticate with the API. Can also be specified via
+ K8S_AUTH_SSL_CA_CERT environment variable.
+ type: str
+ verify_ssl:
+ description:
+ - "Whether or not to verify the API server's SSL certificates. Can also be specified via
+ K8S_AUTH_VERIFY_SSL environment variable."
+ type: bool
+ namespaces:
+ description:
+ - List of namespaces. If not specified, will fetch all virtual machines for all namespaces user is authorized
+ to access.
+ type: list
+ network_name:
+ description:
+ - In case of multiple network attached to virtual machine, define which interface should be returned as primary IP
+ address.
+ type: str
+ aliases: [ interface_name ]
+ api_version:
+ description:
+ - "Specify the KubeVirt API version."
+ type: str
+ annotation_variable:
+ description:
+ - "Specify the name of the annotation which provides data, which should be used as inventory host variables."
+ - "Note, that the value in ansible annotations should be json."
+ type: str
+ default: 'ansible'
+ requirements:
+ - "openshift >= 0.6"
+ - "PyYAML >= 3.11"
+'''
+
+EXAMPLES = '''
+# File must be named kubevirt.yaml or kubevirt.yml
+
+# Authenticate with token, and return all virtual machines for all namespaces
+plugin: community.kubevirt.kubevirt
+connections:
+ - host: https://kubevirt.io
+ token: xxxxxxxxxxxxxxxx
+ ssl_verify: false
+
+# Use default config (~/.kube/config) file and active context, and return vms with interfaces
+# connected to network myovsnetwork and from namespace vms
+plugin: community.kubevirt.kubevirt
+connections:
+ - namespaces:
+ - vms
+ network_name: myovsnetwork
+'''
+
+import json
+
+from ansible_collections.community.kubernetes.plugins.inventory.k8s import K8sInventoryException, InventoryModule as K8sInventoryModule, format_dynamic_api_exc
+
+try:
+ from openshift.dynamic.exceptions import DynamicApiError
+except ImportError:
+ pass
+
+
+API_VERSION = 'kubevirt.io/v1alpha3'
+
+
+class InventoryModule(K8sInventoryModule):
+ NAME = 'community.kubevirt.kubevirt'
+
+ def setup(self, config_data, cache, cache_key):
+ self.config_data = config_data
+ super(InventoryModule, self).setup(config_data, cache, cache_key)
+
+ def fetch_objects(self, connections):
+ client = self.get_api_client()
+ vm_format = self.config_data.get('host_format', '{namespace}-{name}-{uid}')
+
+ if connections:
+ for connection in connections:
+ client = self.get_api_client(**connection)
+ name = connection.get('name', self.get_default_host_name(client.configuration.host))
+ if connection.get('namespaces'):
+ namespaces = connection['namespaces']
+ else:
+ namespaces = self.get_available_namespaces(client)
+ interface_name = connection.get('network_name')
+ api_version = connection.get('api_version', API_VERSION)
+ annotation_variable = connection.get('annotation_variable', 'ansible')
+ for namespace in namespaces:
+ self.get_vms_for_namespace(client, name, namespace, vm_format, interface_name, api_version, annotation_variable)
+ else:
+ name = self.get_default_host_name(client.configuration.host)
+ namespaces = self.get_available_namespaces(client)
+ for namespace in namespaces:
+ self.get_vms_for_namespace(client, name, namespace, vm_format, None, api_version, annotation_variable)
+
+ def get_vms_for_namespace(self, client, name, namespace, name_format, interface_name=None, api_version=None, annotation_variable=None):
+ v1_vm = client.resources.get(api_version=api_version, kind='VirtualMachineInstance')
+ try:
+ obj = v1_vm.get(namespace=namespace)
+ except DynamicApiError as exc:
+ self.display.debug(exc)
+ raise K8sInventoryException('Error fetching Virtual Machines list: %s' % format_dynamic_api_exc(exc))
+
+ namespace_group = 'namespace_{0}'.format(namespace)
+ namespace_vms_group = '{0}_vms'.format(namespace_group)
+
+ name = self._sanitize_group_name(name)
+ namespace_group = self._sanitize_group_name(namespace_group)
+ namespace_vms_group = self._sanitize_group_name(namespace_vms_group)
+ self.inventory.add_group(name)
+ self.inventory.add_group(namespace_group)
+ self.inventory.add_child(name, namespace_group)
+ self.inventory.add_group(namespace_vms_group)
+ self.inventory.add_child(namespace_group, namespace_vms_group)
+ for vm in obj.items:
+ if not (vm.status and vm.status.interfaces):
+ continue
+
+ # Find interface by its name:
+ if interface_name is None:
+ interface = vm.status.interfaces[0]
+ else:
+ interface = next(
+ (i for i in vm.status.interfaces if i.name == interface_name),
+ None
+ )
+
+ # If interface is not found or IP address is not reported skip this VM:
+ if interface is None or interface.ipAddress is None:
+ continue
+
+ vm_name = name_format.format(namespace=vm.metadata.namespace, name=vm.metadata.name, uid=vm.metadata.uid)
+ vm_ip = interface.ipAddress
+ vm_annotations = {} if not vm.metadata.annotations else dict(vm.metadata.annotations)
+
+ self.inventory.add_host(vm_name)
+
+ if vm.metadata.labels:
+ # create a group for each label_value
+ for key, value in vm.metadata.labels:
+ group_name = 'label_{0}_{1}'.format(key, value)
+ group_name = self._sanitize_group_name(group_name)
+ self.inventory.add_group(group_name)
+ self.inventory.add_child(group_name, vm_name)
+ vm_labels = dict(vm.metadata.labels)
+ else:
+ vm_labels = {}
+
+ self.inventory.add_child(namespace_vms_group, vm_name)
+
+ # add hostvars
+ self.inventory.set_variable(vm_name, 'ansible_host', vm_ip)
+ self.inventory.set_variable(vm_name, 'labels', vm_labels)
+ self.inventory.set_variable(vm_name, 'annotations', vm_annotations)
+ self.inventory.set_variable(vm_name, 'object_type', 'vm')
+ self.inventory.set_variable(vm_name, 'resource_version', vm.metadata.resourceVersion)
+ self.inventory.set_variable(vm_name, 'uid', vm.metadata.uid)
+
+ # Add all variables which are listed in 'ansible' annotation:
+ annotations_data = json.loads(vm_annotations.get(annotation_variable, "{}"))
+ for k, v in annotations_data.items():
+ self.inventory.set_variable(vm_name, k, v)
+
+ def verify_file(self, path):
+ if super(InventoryModule, self).verify_file(path):
+ if path.endswith(('kubevirt.yml', 'kubevirt.yaml')):
+ return True
+ return False
diff --git a/collections-debian-merged/ansible_collections/community/kubevirt/plugins/module_utils/kubevirt.py b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/module_utils/kubevirt.py
new file mode 100644
index 00000000..e59e477a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/module_utils/kubevirt.py
@@ -0,0 +1,464 @@
+# -*- coding: utf-8 -*-
+#
+
+# Copyright (c) 2018, KubeVirt Team <@kubevirt>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from collections import defaultdict
+from distutils.version import Version
+
+from ansible.module_utils.common import dict_transformations
+from ansible.module_utils.common._collections_compat import Sequence
+from ansible_collections.community.kubernetes.plugins.module_utils.common import list_dict_str
+from ansible_collections.community.kubernetes.plugins.module_utils.raw import KubernetesRawModule
+
+import copy
+import re
+
+MAX_SUPPORTED_API_VERSION = 'v1alpha3'
+API_GROUP = 'kubevirt.io'
+
+
+# Put all args that (can) modify 'spec:' here:
+VM_SPEC_DEF_ARG_SPEC = {
+ 'resource_definition': {
+ 'type': 'dict',
+ 'aliases': ['definition', 'inline']
+ },
+ 'memory': {'type': 'str'},
+ 'memory_limit': {'type': 'str'},
+ 'cpu_cores': {'type': 'int'},
+ 'disks': {'type': 'list'},
+ 'labels': {'type': 'dict'},
+ 'interfaces': {'type': 'list'},
+ 'machine_type': {'type': 'str'},
+ 'cloud_init_nocloud': {'type': 'dict'},
+ 'bootloader': {'type': 'str'},
+ 'smbios_uuid': {'type': 'str'},
+ 'cpu_model': {'type': 'str'},
+ 'headless': {'type': 'str'},
+ 'hugepage_size': {'type': 'str'},
+ 'tablets': {'type': 'list'},
+ 'cpu_limit': {'type': 'int'},
+ 'cpu_shares': {'type': 'int'},
+ 'cpu_features': {'type': 'list'},
+ 'affinity': {'type': 'dict'},
+ 'anti_affinity': {'type': 'dict'},
+ 'node_affinity': {'type': 'dict'},
+}
+# And other common args go here:
+VM_COMMON_ARG_SPEC = {
+ 'name': {'required': True},
+ 'namespace': {'required': True},
+ 'hostname': {'type': 'str'},
+ 'subdomain': {'type': 'str'},
+ 'state': {
+ 'default': 'present',
+ 'choices': ['present', 'absent'],
+ },
+ 'force': {
+ 'type': 'bool',
+ 'default': False,
+ },
+ 'merge_type': {'type': 'list', 'choices': ['json', 'merge', 'strategic-merge']},
+ 'wait': {'type': 'bool', 'default': True},
+ 'wait_timeout': {'type': 'int', 'default': 120},
+ 'wait_sleep': {'type': 'int', 'default': 5},
+}
+VM_COMMON_ARG_SPEC.update(VM_SPEC_DEF_ARG_SPEC)
+
+
+def virtdict():
+ """
+ This function create dictionary, with defaults to dictionary.
+ """
+ return defaultdict(virtdict)
+
+
+class KubeAPIVersion(Version):
+ component_re = re.compile(r'(\d+ | [a-z]+)', re.VERBOSE)
+
+ def __init__(self, vstring=None):
+ if vstring:
+ self.parse(vstring)
+
+ def parse(self, vstring):
+ self.vstring = vstring
+ components = [x for x in self.component_re.split(vstring) if x]
+ for i, obj in enumerate(components):
+ try:
+ components[i] = int(obj)
+ except ValueError:
+ pass
+
+ errmsg = "version '{0}' does not conform to kubernetes api versioning guidelines".format(vstring)
+ c = components
+
+ if len(c) not in (2, 4) or c[0] != 'v' or not isinstance(c[1], int):
+ raise ValueError(errmsg)
+ if len(c) == 4 and (c[2] not in ('alpha', 'beta') or not isinstance(c[3], int)):
+ raise ValueError(errmsg)
+
+ self.version = components
+
+ def __str__(self):
+ return self.vstring
+
+ def __repr__(self):
+ return "KubeAPIVersion ('{0}')".format(str(self))
+
+ def _cmp(self, other):
+ if isinstance(other, str):
+ other = KubeAPIVersion(other)
+
+ myver = self.version
+ otherver = other.version
+
+ for ver in myver, otherver:
+ if len(ver) == 2:
+ ver.extend(['zeta', 9999])
+
+ if myver == otherver:
+ return 0
+ if myver < otherver:
+ return -1
+ if myver > otherver:
+ return 1
+
+ # python2 compatibility
+ def __cmp__(self, other):
+ return self._cmp(other)
+
+
+class KubeVirtRawModule(KubernetesRawModule):
+ def __init__(self, *args, **kwargs):
+ super(KubeVirtRawModule, self).__init__(*args, **kwargs)
+
+ @staticmethod
+ def merge_dicts(base_dict, merging_dicts):
+ """This function merges a base dictionary with one or more other dictionaries.
+ The base dictionary takes precedence when there is a key collision.
+ merging_dicts can be a dict or a list or tuple of dicts. In the latter case, the
+ dictionaries at the front of the list have higher precedence over the ones at the end.
+ """
+ if not merging_dicts:
+ merging_dicts = ({},)
+
+ if not isinstance(merging_dicts, Sequence):
+ merging_dicts = (merging_dicts,)
+
+ new_dict = {}
+ for d in reversed(merging_dicts):
+ new_dict = dict_transformations.dict_merge(new_dict, d)
+
+ new_dict = dict_transformations.dict_merge(new_dict, base_dict)
+
+ return new_dict
+
+ def get_resource(self, resource):
+ try:
+ existing = resource.get(name=self.name, namespace=self.namespace)
+ except Exception:
+ existing = None
+
+ return existing
+
+ def _define_datavolumes(self, datavolumes, spec):
+ """
+ Takes datavoulmes parameter of Ansible and create kubevirt API datavolumesTemplateSpec
+ structure from it
+ """
+ if not datavolumes:
+ return
+
+ spec['dataVolumeTemplates'] = []
+ for dv in datavolumes:
+ # Add datavolume to datavolumetemplates spec:
+ dvt = virtdict()
+ dvt['metadata']['name'] = dv.get('name')
+ dvt['spec']['pvc'] = {
+ 'accessModes': dv.get('pvc').get('accessModes'),
+ 'resources': {
+ 'requests': {
+ 'storage': dv.get('pvc').get('storage'),
+ }
+ }
+ }
+ dvt['spec']['source'] = dv.get('source')
+ spec['dataVolumeTemplates'].append(dvt)
+
+ # Add datavolume to disks spec:
+ if not spec['template']['spec']['domain']['devices']['disks']:
+ spec['template']['spec']['domain']['devices']['disks'] = []
+
+ spec['template']['spec']['domain']['devices']['disks'].append(
+ {
+ 'name': dv.get('name'),
+ 'disk': dv.get('disk', {'bus': 'virtio'}),
+ }
+ )
+
+ # Add datavolume to volumes spec:
+ if not spec['template']['spec']['volumes']:
+ spec['template']['spec']['volumes'] = []
+
+ spec['template']['spec']['volumes'].append(
+ {
+ 'dataVolume': {
+ 'name': dv.get('name')
+ },
+ 'name': dv.get('name'),
+ }
+ )
+
+ def _define_cloud_init(self, cloud_init_nocloud, template_spec):
+ """
+ Takes the user's cloud_init_nocloud parameter and fill it in kubevirt
+ API strucuture. The name for disk is hardcoded to ansiblecloudinitdisk.
+ """
+ if cloud_init_nocloud:
+ if not template_spec['volumes']:
+ template_spec['volumes'] = []
+ if not template_spec['domain']['devices']['disks']:
+ template_spec['domain']['devices']['disks'] = []
+
+ template_spec['volumes'].append({'name': 'ansiblecloudinitdisk', 'cloudInitNoCloud': cloud_init_nocloud})
+ template_spec['domain']['devices']['disks'].append({
+ 'name': 'ansiblecloudinitdisk',
+ 'disk': {'bus': 'virtio'},
+ })
+
+ def _define_interfaces(self, interfaces, template_spec, defaults):
+ """
+ Takes interfaces parameter of Ansible and create kubevirt API interfaces
+ and networks strucutre out from it.
+ """
+ if not interfaces and defaults and 'interfaces' in defaults:
+ interfaces = copy.deepcopy(defaults['interfaces'])
+ for d in interfaces:
+ d['network'] = defaults['networks'][0]
+
+ if interfaces:
+ # Extract interfaces k8s specification from interfaces list passed to Ansible:
+ spec_interfaces = []
+ for i in interfaces:
+ spec_interfaces.append(
+ self.merge_dicts(dict((k, v) for k, v in i.items() if k != 'network'), defaults['interfaces'])
+ )
+ if 'interfaces' not in template_spec['domain']['devices']:
+ template_spec['domain']['devices']['interfaces'] = []
+ template_spec['domain']['devices']['interfaces'].extend(spec_interfaces)
+
+ # Extract networks k8s specification from interfaces list passed to Ansible:
+ spec_networks = []
+ for i in interfaces:
+ net = i['network']
+ net['name'] = i['name']
+ spec_networks.append(self.merge_dicts(net, defaults['networks']))
+ if 'networks' not in template_spec:
+ template_spec['networks'] = []
+ template_spec['networks'].extend(spec_networks)
+
+ def _define_disks(self, disks, template_spec, defaults):
+ """
+ Takes disks parameter of Ansible and create kubevirt API disks and
+ volumes strucutre out from it.
+ """
+ if not disks and defaults and 'disks' in defaults:
+ disks = copy.deepcopy(defaults['disks'])
+ for d in disks:
+ d['volume'] = defaults['volumes'][0]
+
+ if disks:
+ # Extract k8s specification from disks list passed to Ansible:
+ spec_disks = []
+ for d in disks:
+ spec_disks.append(
+ self.merge_dicts(dict((k, v) for k, v in d.items() if k != 'volume'), defaults['disks'])
+ )
+ if 'disks' not in template_spec['domain']['devices']:
+ template_spec['domain']['devices']['disks'] = []
+ template_spec['domain']['devices']['disks'].extend(spec_disks)
+
+ # Extract volumes k8s specification from disks list passed to Ansible:
+ spec_volumes = []
+ for d in disks:
+ volume = d['volume']
+ volume['name'] = d['name']
+ spec_volumes.append(self.merge_dicts(volume, defaults['volumes']))
+ if 'volumes' not in template_spec:
+ template_spec['volumes'] = []
+ template_spec['volumes'].extend(spec_volumes)
+
+ def find_supported_resource(self, kind):
+ results = self.client.resources.search(kind=kind, group=API_GROUP)
+ if not results:
+ self.fail('Failed to find resource {0} in {1}'.format(kind, API_GROUP))
+ sr = sorted(results, key=lambda r: KubeAPIVersion(r.api_version), reverse=True)
+ for r in sr:
+ if KubeAPIVersion(r.api_version) <= KubeAPIVersion(MAX_SUPPORTED_API_VERSION):
+ return r
+ self.fail("API versions {0} are too recent. Max supported is {1}/{2}.".format(
+ str([r.api_version for r in sr]), API_GROUP, MAX_SUPPORTED_API_VERSION))
+
+ def _construct_vm_definition(self, kind, definition, template, params, defaults=None):
+ self.client = self.get_api_client()
+
+ disks = params.get('disks', [])
+ memory = params.get('memory')
+ memory_limit = params.get('memory_limit')
+ cpu_cores = params.get('cpu_cores')
+ cpu_model = params.get('cpu_model')
+ cpu_features = params.get('cpu_features')
+ labels = params.get('labels')
+ datavolumes = params.get('datavolumes')
+ interfaces = params.get('interfaces')
+ bootloader = params.get('bootloader')
+ cloud_init_nocloud = params.get('cloud_init_nocloud')
+ machine_type = params.get('machine_type')
+ headless = params.get('headless')
+ smbios_uuid = params.get('smbios_uuid')
+ hugepage_size = params.get('hugepage_size')
+ tablets = params.get('tablets')
+ cpu_shares = params.get('cpu_shares')
+ cpu_limit = params.get('cpu_limit')
+ node_affinity = params.get('node_affinity')
+ vm_affinity = params.get('affinity')
+ vm_anti_affinity = params.get('anti_affinity')
+ hostname = params.get('hostname')
+ subdomain = params.get('subdomain')
+ template_spec = template['spec']
+
+ # Merge additional flat parameters:
+ if memory:
+ template_spec['domain']['resources']['requests']['memory'] = memory
+
+ if cpu_shares:
+ template_spec['domain']['resources']['requests']['cpu'] = cpu_shares
+
+ if cpu_limit:
+ template_spec['domain']['resources']['limits']['cpu'] = cpu_limit
+
+ if tablets:
+ for tablet in tablets:
+ tablet['type'] = 'tablet'
+ template_spec['domain']['devices']['inputs'] = tablets
+
+ if memory_limit:
+ template_spec['domain']['resources']['limits']['memory'] = memory_limit
+
+ if hugepage_size is not None:
+ template_spec['domain']['memory']['hugepages']['pageSize'] = hugepage_size
+
+ if cpu_features is not None:
+ template_spec['domain']['cpu']['features'] = cpu_features
+
+ if cpu_cores is not None:
+ template_spec['domain']['cpu']['cores'] = cpu_cores
+
+ if cpu_model:
+ template_spec['domain']['cpu']['model'] = cpu_model
+
+ if labels:
+ template['metadata']['labels'] = self.merge_dicts(labels, template['metadata']['labels'])
+
+ if machine_type:
+ template_spec['domain']['machine']['type'] = machine_type
+
+ if bootloader:
+ template_spec['domain']['firmware']['bootloader'] = {bootloader: {}}
+
+ if smbios_uuid:
+ template_spec['domain']['firmware']['uuid'] = smbios_uuid
+
+ if headless is not None:
+ template_spec['domain']['devices']['autoattachGraphicsDevice'] = not headless
+
+ if vm_affinity or vm_anti_affinity:
+ vms_affinity = vm_affinity or vm_anti_affinity
+ affinity_name = 'podAffinity' if vm_affinity else 'podAntiAffinity'
+ for affinity in vms_affinity.get('soft', []):
+ if not template_spec['affinity'][affinity_name]['preferredDuringSchedulingIgnoredDuringExecution']:
+ template_spec['affinity'][affinity_name]['preferredDuringSchedulingIgnoredDuringExecution'] = []
+ template_spec['affinity'][affinity_name]['preferredDuringSchedulingIgnoredDuringExecution'].append({
+ 'weight': affinity.get('weight'),
+ 'podAffinityTerm': {
+ 'labelSelector': {
+ 'matchExpressions': affinity.get('term').get('match_expressions'),
+ },
+ 'topologyKey': affinity.get('topology_key'),
+ },
+ })
+ for affinity in vms_affinity.get('hard', []):
+ if not template_spec['affinity'][affinity_name]['requiredDuringSchedulingIgnoredDuringExecution']:
+ template_spec['affinity'][affinity_name]['requiredDuringSchedulingIgnoredDuringExecution'] = []
+ template_spec['affinity'][affinity_name]['requiredDuringSchedulingIgnoredDuringExecution'].append({
+ 'labelSelector': {
+ 'matchExpressions': affinity.get('term').get('match_expressions'),
+ },
+ 'topologyKey': affinity.get('topology_key'),
+ })
+
+ if node_affinity:
+ for affinity in node_affinity.get('soft', []):
+ if not template_spec['affinity']['nodeAffinity']['preferredDuringSchedulingIgnoredDuringExecution']:
+ template_spec['affinity']['nodeAffinity']['preferredDuringSchedulingIgnoredDuringExecution'] = []
+ template_spec['affinity']['nodeAffinity']['preferredDuringSchedulingIgnoredDuringExecution'].append({
+ 'weight': affinity.get('weight'),
+ 'preference': {
+ 'matchExpressions': affinity.get('term').get('match_expressions'),
+ }
+ })
+ for affinity in node_affinity.get('hard', []):
+ if not template_spec['affinity']['nodeAffinity']['requiredDuringSchedulingIgnoredDuringExecution']['nodeSelectorTerms']:
+ template_spec['affinity']['nodeAffinity']['requiredDuringSchedulingIgnoredDuringExecution']['nodeSelectorTerms'] = []
+ template_spec['affinity']['nodeAffinity']['requiredDuringSchedulingIgnoredDuringExecution']['nodeSelectorTerms'].append({
+ 'matchExpressions': affinity.get('term').get('match_expressions'),
+ })
+
+ if hostname:
+ template_spec['hostname'] = hostname
+
+ if subdomain:
+ template_spec['subdomain'] = subdomain
+
+ # Define disks
+ self._define_disks(disks, template_spec, defaults)
+
+ # Define cloud init disk if defined:
+ # Note, that this must be called after _define_disks, so the cloud_init
+ # is not first in order and it's not used as boot disk:
+ self._define_cloud_init(cloud_init_nocloud, template_spec)
+
+ # Define interfaces:
+ self._define_interfaces(interfaces, template_spec, defaults)
+
+ # Define datavolumes:
+ self._define_datavolumes(datavolumes, definition['spec'])
+
+ return self.merge_dicts(definition, self.resource_definitions[0])
+
+ def construct_vm_definition(self, kind, definition, template, defaults=None):
+ definition = self._construct_vm_definition(kind, definition, template, self.params, defaults)
+ resource = self.find_supported_resource(kind)
+ definition = self.set_defaults(resource, definition)
+ return resource, definition
+
+ def construct_vm_template_definition(self, kind, definition, template, params):
+ definition = self._construct_vm_definition(kind, definition, template, params)
+ resource = self.find_resource(kind, definition['apiVersion'], fail=True)
+
+ # Set defaults:
+ definition['kind'] = kind
+ definition['metadata']['name'] = params.get('name')
+ definition['metadata']['namespace'] = params.get('namespace')
+
+ return resource, definition
+
+ def execute_crud(self, kind, definition):
+ """ Module execution """
+ resource = self.find_supported_resource(kind)
+ definition = self.set_defaults(resource, definition)
+ return self.perform_action(resource, definition)
diff --git a/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_cdi_upload.py b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_cdi_upload.py
new file mode 100644
index 00000000..dfcf707e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_cdi_upload.py
@@ -0,0 +1,184 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2019, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+
+module: kubevirt_cdi_upload
+
+short_description: Upload local VM images to CDI Upload Proxy.
+
+
+author: KubeVirt Team (@kubevirt)
+
+
+description:
+ - Use Openshift Python SDK to create UploadTokenRequest objects.
+ - Transfer contents of local files to the CDI Upload Proxy.
+
+options:
+ pvc_name:
+ description:
+ - Use to specify the name of the target PersistentVolumeClaim.
+ required: true
+ pvc_namespace:
+ description:
+ - Use to specify the namespace of the target PersistentVolumeClaim.
+ required: true
+ upload_host:
+ description:
+ - URL containing the host and port on which the CDI Upload Proxy is available.
+ - "More info: U(https://github.com/kubevirt/containerized-data-importer/blob/master/doc/upload.md#expose-cdi-uploadproxy-service)"
+ upload_host_validate_certs:
+ description:
+ - Whether or not to verify the CDI Upload Proxy's SSL certificates against your system's CA trust store.
+ default: true
+ type: bool
+ aliases: [ upload_host_verify_ssl ]
+ path:
+ description:
+ - Path of local image file to transfer.
+ merge_type:
+ description:
+ - Whether to override the default patch merge approach with a specific type. By default, the strategic
+ merge will typically be used.
+ type: list
+ choices: [ json, merge, strategic-merge ]
+
+extends_documentation_fragment:
+- community.kubernetes.k8s_auth_options
+
+
+requirements:
+ - python >= 2.7
+ - openshift >= 0.8.2
+ - requests >= 2.0.0
+'''
+
+EXAMPLES = '''
+- name: Upload local image to pvc-vm1
+ community.kubevirt.kubevirt_cdi_upload:
+ pvc_namespace: default
+ pvc_name: pvc-vm1
+ upload_host: https://localhost:8443
+ upload_host_validate_certs: false
+ path: /tmp/cirros-0.4.0-x86_64-disk.img
+'''
+
+RETURN = '''# '''
+
+import copy
+import traceback
+
+from collections import defaultdict
+
+from ansible_collections.community.kubernetes.plugins.module_utils.common import AUTH_ARG_SPEC
+from ansible_collections.community.kubernetes.plugins.module_utils.raw import KubernetesRawModule
+
+# 3rd party imports
+try:
+ import requests
+ HAS_REQUESTS = True
+except ImportError:
+ HAS_REQUESTS = False
+
+
+SERVICE_ARG_SPEC = {
+ 'pvc_name': {'required': True},
+ 'pvc_namespace': {'required': True},
+ 'upload_host': {'required': True},
+ 'upload_host_validate_certs': {
+ 'type': 'bool',
+ 'default': True,
+ 'aliases': ['upload_host_verify_ssl']
+ },
+ 'path': {'required': True},
+ 'merge_type': {
+ 'type': 'list',
+ 'choices': ['json', 'merge', 'strategic-merge']
+ },
+}
+
+
+class KubeVirtCDIUpload(KubernetesRawModule):
+ def __init__(self, *args, **kwargs):
+ super(KubeVirtCDIUpload, self).__init__(*args, k8s_kind='UploadTokenRequest', **kwargs)
+
+ if not HAS_REQUESTS:
+ self.fail("This module requires the python 'requests' package. Try `pip install requests`.")
+
+ @property
+ def argspec(self):
+ """ argspec property builder """
+ argument_spec = copy.deepcopy(AUTH_ARG_SPEC)
+ argument_spec.update(SERVICE_ARG_SPEC)
+ return argument_spec
+
+ def execute_module(self):
+ """ Module execution """
+
+ API = 'v1alpha1'
+ KIND = 'UploadTokenRequest'
+
+ self.client = self.get_api_client()
+
+ api_version = 'upload.cdi.kubevirt.io/{0}'.format(API)
+ pvc_name = self.params.get('pvc_name')
+ pvc_namespace = self.params.get('pvc_namespace')
+ upload_host = self.params.get('upload_host')
+ upload_host_verify_ssl = self.params.get('upload_host_validate_certs')
+ path = self.params.get('path')
+
+ definition = defaultdict(defaultdict)
+
+ definition['kind'] = KIND
+ definition['apiVersion'] = api_version
+
+ def_meta = definition['metadata']
+ def_meta['name'] = pvc_name
+ def_meta['namespace'] = pvc_namespace
+
+ def_spec = definition['spec']
+ def_spec['pvcName'] = pvc_name
+
+ # Let's check the file's there before we do anything else
+ imgfile = open(path, 'rb')
+
+ resource = self.find_resource(KIND, api_version, fail=True)
+ definition = self.set_defaults(resource, definition)
+ result = self.perform_action(resource, definition)
+
+ headers = {'Authorization': "Bearer {0}".format(result['result']['status']['token'])}
+ url = "{0}/{1}/upload".format(upload_host, API)
+ ret = requests.post(url, data=imgfile, headers=headers, verify=upload_host_verify_ssl)
+
+ if ret.status_code != 200:
+ self.fail_request("Something went wrong while uploading data", method='POST', url=url,
+ reason=ret.reason, status_code=ret.status_code)
+
+ self.exit_json(changed=True)
+
+ def fail_request(self, msg, **kwargs):
+ req_info = {}
+ for k, v in kwargs.items():
+ req_info['req_' + k] = v
+ self.fail_json(msg=msg, **req_info)
+
+
+def main():
+ module = KubeVirtCDIUpload()
+ try:
+ module.execute_module()
+ except Exception as e:
+ module.fail_json(msg=str(e), exception=traceback.format_exc())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_preset.py b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_preset.py
new file mode 100644
index 00000000..ab6063c0
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_preset.py
@@ -0,0 +1,154 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2019, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = '''
+---
+module: kubevirt_preset
+
+short_description: Manage KubeVirt virtual machine presets
+
+description:
+ - Use Openshift Python SDK to manage the state of KubeVirt virtual machine presets.
+
+
+author: KubeVirt Team (@kubevirt)
+
+options:
+ state:
+ description:
+ - Create or delete virtual machine presets.
+ default: "present"
+ choices:
+ - present
+ - absent
+ type: str
+ name:
+ description:
+ - Name of the virtual machine preset.
+ required: true
+ type: str
+ namespace:
+ description:
+ - Namespace where the virtual machine preset exists.
+ required: true
+ type: str
+ selector:
+ description:
+ - "Selector is a label query over a set of virtual machine preset."
+ type: dict
+
+extends_documentation_fragment:
+- community.kubernetes.k8s_auth_options
+- community.kubevirt.kubevirt_vm_options
+- community.kubevirt.kubevirt_common_options
+
+
+requirements:
+ - python >= 2.7
+ - openshift >= 0.8.2
+'''
+
+EXAMPLES = '''
+- name: Create virtual machine preset 'vmi-preset-small'
+ community.kubevirt.kubevirt_preset:
+ state: present
+ name: vmi-preset-small
+ namespace: vms
+ memory: 64M
+ selector:
+ matchLabels:
+ kubevirt.io/vmPreset: vmi-preset-small
+
+- name: Remove virtual machine preset 'vmi-preset-small'
+ community.kubevirt.kubevirt_preset:
+ state: absent
+ name: vmi-preset-small
+ namespace: vms
+'''
+
+RETURN = '''
+kubevirt_preset:
+ description:
+ - The virtual machine preset managed by the user.
+ - "This dictionary contains all values returned by the KubeVirt API all options
+ are described here U(https://kubevirt.io/api-reference/master/definitions.html#_v1_virtualmachineinstancepreset)"
+ returned: success
+ type: complex
+ contains: {}
+'''
+
+import copy
+import traceback
+
+
+from ansible_collections.community.kubernetes.plugins.module_utils.common import AUTH_ARG_SPEC
+
+from ansible_collections.community.kubevirt.plugins.module_utils.kubevirt import (
+ virtdict,
+ KubeVirtRawModule,
+ VM_COMMON_ARG_SPEC
+)
+
+
+KIND = 'VirtualMachineInstancePreset'
+VMP_ARG_SPEC = {
+ 'selector': {'type': 'dict'},
+}
+
+
+class KubeVirtVMPreset(KubeVirtRawModule):
+
+ @property
+ def argspec(self):
+ """ argspec property builder """
+ argument_spec = copy.deepcopy(AUTH_ARG_SPEC)
+ argument_spec.update(VM_COMMON_ARG_SPEC)
+ argument_spec.update(VMP_ARG_SPEC)
+ return argument_spec
+
+ def execute_module(self):
+ # Parse parameters specific for this module:
+ definition = virtdict()
+ selector = self.params.get('selector')
+
+ if selector:
+ definition['spec']['selector'] = selector
+
+ # FIXME: Devices must be set, but we don't yet support any
+ # attributes there, remove when we do:
+ definition['spec']['domain']['devices'] = dict()
+
+ # defaults for template
+ defaults = {'disks': [], 'volumes': [], 'interfaces': [], 'networks': []}
+
+ # Execute the CURD of VM:
+ dummy, definition = self.construct_vm_definition(KIND, definition, definition, defaults)
+ result_crud = self.execute_crud(KIND, definition)
+ changed = result_crud['changed']
+ result = result_crud.pop('result')
+
+ # Return from the module:
+ self.exit_json(**{
+ 'changed': changed,
+ 'kubevirt_preset': result,
+ 'result': result_crud,
+ })
+
+
+def main():
+ module = KubeVirtVMPreset()
+ try:
+ module.execute_module()
+ except Exception as e:
+ module.fail_json(msg=str(e), exception=traceback.format_exc())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_pvc.py b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_pvc.py
new file mode 100644
index 00000000..e21d05b8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_pvc.py
@@ -0,0 +1,457 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2019, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = '''
+
+module: kubevirt_pvc
+
+short_description: Manage PVCs on Kubernetes
+
+
+author: KubeVirt Team (@kubevirt)
+
+description:
+ - Use Openshift Python SDK to manage PVCs on Kubernetes
+ - Support Containerized Data Importer out of the box
+
+options:
+ resource_definition:
+ description:
+ - "A partial YAML definition of the PVC object being created/updated. Here you can define Kubernetes
+ PVC Resource parameters not covered by this module's parameters."
+ - "NOTE: I(resource_definition) has lower priority than module parameters. If you try to define e.g.
+ I(metadata.namespace) here, that value will be ignored and I(namespace) used instead."
+ aliases:
+ - definition
+ - inline
+ type: dict
+ state:
+ description:
+ - "Determines if an object should be created, patched, or deleted. When set to C(present), an object will be
+ created, if it does not already exist. If set to C(absent), an existing object will be deleted. If set to
+ C(present), an existing object will be patched, if its attributes differ from those specified using
+ module options and I(resource_definition)."
+ default: present
+ choices:
+ - present
+ - absent
+ force:
+ description:
+ - If set to C(True), and I(state) is C(present), an existing object will be replaced.
+ default: false
+ type: bool
+ merge_type:
+ description:
+ - Whether to override the default patch merge approach with a specific type.
+ - "This defaults to C(['strategic-merge', 'merge']), which is ideal for using the same parameters
+ on resource kinds that combine Custom Resources and built-in resources."
+ - See U(https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/#use-a-json-merge-patch-to-update-a-deployment)
+ - If more than one merge_type is given, the merge_types will be tried in order
+ choices:
+ - json
+ - merge
+ - strategic-merge
+ type: list
+ name:
+ description:
+ - Use to specify a PVC object name.
+ required: true
+ type: str
+ namespace:
+ description:
+ - Use to specify a PVC object namespace.
+ required: true
+ type: str
+ annotations:
+ description:
+ - Annotations attached to this object.
+ - U(https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)
+ type: dict
+ labels:
+ description:
+ - Labels attached to this object.
+ - U(https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
+ type: dict
+ selector:
+ description:
+ - A label query over volumes to consider for binding.
+ - U(https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)
+ type: dict
+ access_modes:
+ description:
+ - Contains the desired access modes the volume should have.
+ - "More info: U(https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes)"
+ type: list
+ size:
+ description:
+ - How much storage to allocate to the PVC.
+ type: str
+ aliases:
+ - storage
+ storage_class_name:
+ description:
+ - Name of the StorageClass required by the claim.
+ - "More info: U(https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1)"
+ type: str
+ volume_mode:
+ description:
+ - "This defines what type of volume is required by the claim. Value of Filesystem is implied when not
+ included in claim spec. This is an alpha feature of kubernetes and may change in the future."
+ type: str
+ volume_name:
+ description:
+ - This is the binding reference to the PersistentVolume backing this claim.
+ type: str
+ cdi_source:
+ description:
+ - "If data is to be copied onto the PVC using the Containerized Data Importer you can specify the source of
+ the data (along with any additional configuration) as well as it's format."
+ - "Valid source types are: blank, http, s3, registry, pvc and upload. The last one requires using the
+ M(community.kubevirt.kubevirt_cdi_upload) module to actually perform an upload."
+ - "Source data format is specified using the optional I(content_type). Valid options are C(kubevirt)
+ (default; raw image) and C(archive) (tar.gz)."
+ - "This uses the DataVolume source syntax:
+ U(https://github.com/kubevirt/containerized-data-importer/blob/master/doc/datavolumes.md#https3registry-source)"
+ type: dict
+ wait:
+ description:
+ - "If set, this module will wait for the PVC to become bound and CDI (if enabled) to finish its operation
+ before returning."
+ - "Used only if I(state) set to C(present)."
+ - "Unless used in conjunction with I(cdi_source), this might result in a timeout, as clusters may be configured
+ to not bind PVCs until first usage."
+ default: false
+ type: bool
+ wait_timeout:
+ description:
+ - Specifies how much time in seconds to wait for PVC creation to complete if I(wait) option is enabled.
+ - Default value is reasonably high due to an expectation that CDI might take a while to finish its operation.
+ type: int
+ default: 300
+
+extends_documentation_fragment:
+- community.kubernetes.k8s_auth_options
+
+
+requirements:
+ - python >= 2.7
+ - openshift >= 0.8.2
+'''
+
+EXAMPLES = '''
+- name: Create a PVC and import data from an external source
+ community.kubevirt.kubevirt_pvc:
+ name: pvc1
+ namespace: default
+ size: 100Mi
+ access_modes:
+ - ReadWriteOnce
+ cdi_source:
+ http:
+ url: https://www.source.example/path/of/data/vm.img
+ # If the URL points to a tar.gz containing the disk image, uncomment the line below:
+ #content_type: archive
+
+- name: Create a PVC as a clone from a different PVC
+ community.kubevirt.kubevirt_pvc:
+ name: pvc2
+ namespace: default
+ size: 100Mi
+ access_modes:
+ - ReadWriteOnce
+ cdi_source:
+ pvc:
+ namespace: source-ns
+ name: source-pvc
+
+- name: Create a PVC ready for data upload
+ community.kubevirt.kubevirt_pvc:
+ name: pvc3
+ namespace: default
+ size: 100Mi
+ access_modes:
+ - ReadWriteOnce
+ cdi_source:
+ upload: yes
+ # You need the kubevirt_cdi_upload module to actually upload something
+
+- name: Create a PVC with a blank raw image
+ community.kubevirt.kubevirt_pvc:
+ name: pvc4
+ namespace: default
+ size: 100Mi
+ access_modes:
+ - ReadWriteOnce
+ cdi_source:
+ blank: yes
+
+- name: Create a PVC and fill it with data from a container
+ community.kubevirt.kubevirt_pvc:
+ name: pvc5
+ namespace: default
+ size: 100Mi
+ access_modes:
+ - ReadWriteOnce
+ cdi_source:
+ registry:
+ url: "docker://kubevirt/fedora-cloud-registry-disk-demo"
+
+'''
+
+RETURN = '''
+result:
+ description:
+ - The created, patched, or otherwise present object. Will be empty in the case of a deletion.
+ 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 REST resource this object represents.
+ returned: success
+ type: str
+ metadata:
+ description: Standard object metadata. Includes name, namespace, annotations, labels, etc.
+ returned: success
+ type: complex
+ spec:
+ description: Specific attributes of the object. Will vary based on the I(api_version) and I(kind).
+ returned: success
+ type: complex
+ status:
+ description: Current status details for the object.
+ returned: success
+ type: complex
+ items:
+ description: Returned only when multiple yaml documents are passed to src or resource_definition
+ returned: when resource_definition or src contains list of objects
+ type: list
+ duration:
+ description: elapsed time of task in seconds
+ returned: when C(wait) is true
+ type: int
+ sample: 48
+'''
+
+
+import copy
+import traceback
+
+from collections import defaultdict
+
+from ansible_collections.community.kubernetes.plugins.module_utils.common import AUTH_ARG_SPEC
+from ansible_collections.community.kubernetes.plugins.module_utils.raw import KubernetesRawModule
+from ansible_collections.community.kubevirt.plugins.module_utils.kubevirt import virtdict, KubeVirtRawModule
+
+
+PVC_ARG_SPEC = {
+ 'name': {'required': True},
+ 'namespace': {'required': True},
+ 'state': {
+ 'type': 'str',
+ 'choices': [
+ 'present', 'absent'
+ ],
+ 'default': 'present'
+ },
+ 'force': {
+ 'type': 'bool',
+ 'default': False,
+ },
+ 'merge_type': {
+ 'type': 'list',
+ 'choices': ['json', 'merge', 'strategic-merge']
+ },
+ 'resource_definition': {
+ 'type': 'dict',
+ 'aliases': ['definition', 'inline']
+ },
+ 'labels': {'type': 'dict'},
+ 'annotations': {'type': 'dict'},
+ 'selector': {'type': 'dict'},
+ 'access_modes': {'type': 'list'},
+ 'size': {
+ 'type': 'str',
+ 'aliases': ['storage']
+ },
+ 'storage_class_name': {'type': 'str'},
+ 'volume_mode': {'type': 'str'},
+ 'volume_name': {'type': 'str'},
+ 'cdi_source': {'type': 'dict'},
+ 'wait': {
+ 'type': 'bool',
+ 'default': False
+ },
+ 'wait_timeout': {
+ 'type': 'int',
+ 'default': 300
+ }
+}
+
+
+class CreatePVCFailed(Exception):
+ pass
+
+
+class KubevirtPVC(KubernetesRawModule):
+ def __init__(self):
+ super(KubevirtPVC, self).__init__()
+
+ @property
+ def argspec(self):
+ argument_spec = copy.deepcopy(AUTH_ARG_SPEC)
+ argument_spec.update(PVC_ARG_SPEC)
+ return argument_spec
+
+ @staticmethod
+ def fix_serialization(obj):
+ if obj and hasattr(obj, 'to_dict'):
+ return obj.to_dict()
+ return obj
+
+ def _parse_cdi_source(self, _cdi_src, metadata):
+ cdi_src = copy.deepcopy(_cdi_src)
+ annotations = metadata['annotations']
+ labels = metadata['labels']
+
+ valid_content_types = ('kubevirt', 'archive')
+ valid_sources = ('http', 's3', 'pvc', 'upload', 'blank', 'registry')
+
+ if 'content_type' in cdi_src:
+ content_type = cdi_src.pop('content_type')
+ if content_type not in valid_content_types:
+ raise ValueError("cdi_source.content_type must be one of {0}, not: '{1}'".format(
+ valid_content_types, content_type))
+ annotations['cdi.kubevirt.io/storage.contentType'] = content_type
+
+ if len(cdi_src) != 1:
+ raise ValueError("You must specify exactly one valid CDI source, not {0}: {1}".format(len(cdi_src), tuple(cdi_src.keys())))
+
+ src_type = tuple(cdi_src.keys())[0]
+ src_spec = cdi_src[src_type]
+
+ if src_type not in valid_sources:
+ raise ValueError("Got an invalid CDI source type: '{0}', must be one of {1}".format(src_type, valid_sources))
+
+ # True for all cases save one
+ labels['app'] = 'containerized-data-importer'
+
+ if src_type == 'upload':
+ annotations['cdi.kubevirt.io/storage.upload.target'] = ''
+ elif src_type == 'blank':
+ annotations['cdi.kubevirt.io/storage.import.source'] = 'none'
+ elif src_type == 'pvc':
+ if not isinstance(src_spec, dict) or sorted(src_spec.keys()) != ['name', 'namespace']:
+ raise ValueError("CDI Source 'pvc' requires specifying 'name' and 'namespace' (and nothing else)")
+ labels['app'] = 'host-assisted-cloning'
+ annotations['k8s.io/CloneRequest'] = '{0}/{1}'.format(src_spec['namespace'], src_spec['name'])
+ elif src_type in ('http', 's3', 'registry'):
+ if not isinstance(src_spec, dict) or 'url' not in src_spec:
+ raise ValueError("CDI Source '{0}' requires specifying 'url'".format(src_type))
+ unknown_params = set(src_spec.keys()).difference(set(('url', 'secretRef', 'certConfigMap')))
+ if unknown_params:
+ raise ValueError("CDI Source '{0}' does not know recognize params: {1}".format(src_type, tuple(unknown_params)))
+ annotations['cdi.kubevirt.io/storage.import.source'] = src_type
+ annotations['cdi.kubevirt.io/storage.import.endpoint'] = src_spec['url']
+ if 'secretRef' in src_spec:
+ annotations['cdi.kubevirt.io/storage.import.secretName'] = src_spec['secretRef']
+ if 'certConfigMap' in src_spec:
+ annotations['cdi.kubevirt.io/storage.import.certConfigMap'] = src_spec['certConfigMap']
+
+ def _wait_for_creation(self, resource, uid):
+ return_obj = None
+ desired_cdi_status = 'Succeeded'
+ use_cdi = True if self.params.get('cdi_source') else False
+ if use_cdi and 'upload' in self.params['cdi_source']:
+ desired_cdi_status = 'Running'
+
+ for event in resource.watch(namespace=self.namespace, timeout=self.params.get('wait_timeout')):
+ entity = event['object']
+ metadata = entity.metadata
+ if not hasattr(metadata, 'uid') or metadata.uid != uid:
+ continue
+ if entity.status.phase == 'Bound':
+ if use_cdi and hasattr(metadata, 'annotations'):
+ import_status = metadata.annotations.get('cdi.kubevirt.io/storage.pod.phase')
+ if import_status == desired_cdi_status:
+ return_obj = entity
+ break
+ elif import_status == 'Failed':
+ raise CreatePVCFailed("PVC creation incomplete; importing data failed")
+ else:
+ return_obj = entity
+ break
+ elif entity.status.phase == 'Failed':
+ raise CreatePVCFailed("PVC creation failed")
+
+ if not return_obj:
+ raise CreatePVCFailed("PVC creation timed out")
+
+ return self.fix_serialization(return_obj)
+
+ def execute_module(self):
+ KIND = 'PersistentVolumeClaim'
+ API = 'v1'
+
+ definition = virtdict()
+ definition['kind'] = KIND
+ definition['apiVersion'] = API
+
+ metadata = definition['metadata']
+ metadata['name'] = self.params.get('name')
+ metadata['namespace'] = self.params.get('namespace')
+ if self.params.get('annotations'):
+ metadata['annotations'] = self.params.get('annotations')
+ if self.params.get('labels'):
+ metadata['labels'] = self.params.get('labels')
+ if self.params.get('cdi_source'):
+ self._parse_cdi_source(self.params.get('cdi_source'), metadata)
+
+ spec = definition['spec']
+ if self.params.get('access_modes'):
+ spec['accessModes'] = self.params.get('access_modes')
+ if self.params.get('size'):
+ spec['resources']['requests']['storage'] = self.params.get('size')
+ if self.params.get('storage_class_name'):
+ spec['storageClassName'] = self.params.get('storage_class_name')
+ if self.params.get('selector'):
+ spec['selector'] = self.params.get('selector')
+ if self.params.get('volume_mode'):
+ spec['volumeMode'] = self.params.get('volume_mode')
+ if self.params.get('volume_name'):
+ spec['volumeName'] = self.params.get('volume_name')
+
+ # 'resource_definition:' has lower priority than module parameters
+ definition = dict(KubeVirtRawModule.merge_dicts(definition, self.resource_definitions[0]))
+
+ self.client = self.get_api_client()
+ resource = self.find_resource(KIND, API, fail=True)
+ definition = self.set_defaults(resource, definition)
+ result = self.perform_action(resource, definition)
+ if self.params.get('wait') and self.params.get('state') == 'present':
+ result['result'] = self._wait_for_creation(resource, result['result']['metadata']['uid'])
+
+ self.exit_json(**result)
+
+
+def main():
+ module = KubevirtPVC()
+ try:
+ module.execute_module()
+ except Exception as e:
+ module.fail_json(msg=str(e), exception=traceback.format_exc())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_rs.py b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_rs.py
new file mode 100644
index 00000000..1e86dbda
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_rs.py
@@ -0,0 +1,211 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2019, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = '''
+---
+module: kubevirt_rs
+
+short_description: Manage KubeVirt virtual machine replica sets
+
+description:
+ - Use Openshift Python SDK to manage the state of KubeVirt virtual machine replica sets.
+
+
+author: KubeVirt Team (@kubevirt)
+
+options:
+ state:
+ description:
+ - Create or delete virtual machine replica sets.
+ default: "present"
+ choices:
+ - present
+ - absent
+ type: str
+ name:
+ description:
+ - Name of the virtual machine replica set.
+ required: true
+ type: str
+ namespace:
+ description:
+ - Namespace where the virtual machine replica set exists.
+ required: true
+ type: str
+ selector:
+ description:
+ - "Selector is a label query over a set of virtual machine."
+ required: true
+ type: dict
+ replicas:
+ description:
+ - Number of desired pods. This is a pointer to distinguish between explicit zero and not specified.
+ - Replicas defaults to 1 if newly created replica set.
+ type: int
+
+extends_documentation_fragment:
+- community.kubernetes.k8s_auth_options
+- community.kubevirt.kubevirt_vm_options
+- community.kubevirt.kubevirt_common_options
+
+
+requirements:
+ - python >= 2.7
+ - openshift >= 0.8.2
+'''
+
+EXAMPLES = '''
+- name: Create virtual machine replica set 'myvmir'
+ community.kubevirt.kubevirt_rs:
+ state: present
+ name: myvmir
+ namespace: vms
+ wait: true
+ replicas: 3
+ memory: 64M
+ labels:
+ myvmi: myvmi
+ selector:
+ matchLabels:
+ myvmi: myvmi
+ disks:
+ - name: containerdisk
+ volume:
+ containerDisk:
+ image: kubevirt/cirros-container-disk-demo:latest
+ path: /custom-disk/cirros.img
+ disk:
+ bus: virtio
+
+- name: Remove virtual machine replica set 'myvmir'
+ community.kubevirt.kubevirt_rs:
+ state: absent
+ name: myvmir
+ namespace: vms
+ wait: true
+'''
+
+RETURN = '''
+kubevirt_rs:
+ description:
+ - The virtual machine virtual machine replica set managed by the user.
+ - "This dictionary contains all values returned by the KubeVirt API all options
+ are described here U(https://kubevirt.io/api-reference/master/definitions.html#_v1_virtualmachineinstance)"
+ returned: success
+ type: complex
+ contains: {}
+'''
+
+import copy
+import traceback
+
+
+from ansible_collections.community.kubernetes.plugins.module_utils.common import AUTH_ARG_SPEC
+
+from ansible_collections.community.kubevirt.plugins.module_utils.kubevirt import (
+ virtdict,
+ KubeVirtRawModule,
+ VM_COMMON_ARG_SPEC,
+)
+
+
+KIND = 'VirtualMachineInstanceReplicaSet'
+VMIR_ARG_SPEC = {
+ 'replicas': {'type': 'int'},
+ 'selector': {'type': 'dict'},
+}
+
+
+class KubeVirtVMIRS(KubeVirtRawModule):
+
+ @property
+ def argspec(self):
+ """ argspec property builder """
+ argument_spec = copy.deepcopy(AUTH_ARG_SPEC)
+ argument_spec.update(copy.deepcopy(VM_COMMON_ARG_SPEC))
+ argument_spec.update(copy.deepcopy(VMIR_ARG_SPEC))
+ return argument_spec
+
+ def wait_for_replicas(self, replicas):
+ """ Wait for ready_replicas to equal the requested number of replicas. """
+ resource = self.find_supported_resource(KIND)
+ return_obj = None
+
+ for event in resource.watch(namespace=self.namespace, timeout=self.params.get('wait_timeout')):
+ entity = event['object']
+ if entity.metadata.name != self.name:
+ continue
+ status = entity.get('status', {})
+ readyReplicas = status.get('readyReplicas', 0)
+ if readyReplicas == replicas:
+ return_obj = entity
+ break
+
+ if not return_obj:
+ self.fail_json(msg="Error fetching the patched object. Try a higher wait_timeout value.")
+ if replicas and return_obj.status.readyReplicas is None:
+ self.fail_json(msg="Failed to fetch the number of ready replicas. Try a higher wait_timeout value.")
+ if replicas and return_obj.status.readyReplicas != replicas:
+ self.fail_json(msg="Number of ready replicas is {0}. Failed to reach {1} ready replicas within "
+ "the wait_timeout period.".format(return_obj.status.ready_replicas, replicas))
+ return return_obj.to_dict()
+
+ def execute_module(self):
+ # Parse parameters specific for this module:
+ definition = virtdict()
+ selector = self.params.get('selector')
+ replicas = self.params.get('replicas')
+
+ if selector:
+ definition['spec']['selector'] = selector
+
+ if replicas is not None:
+ definition['spec']['replicas'] = replicas
+
+ # defaults for template
+ defaults = {'disks': [], 'volumes': [], 'interfaces': [], 'networks': []}
+
+ # Execute the CURD of VM:
+ template = definition['spec']['template']
+ dummy, definition = self.construct_vm_definition(KIND, definition, template, defaults)
+ result_crud = self.execute_crud(KIND, definition)
+ changed = result_crud['changed']
+ result = result_crud.pop('result')
+
+ # When creating a new VMIRS object without specifying `replicas`, assume it's '1' to make the
+ # wait logic work correctly
+ if changed and result_crud['method'] == 'create' and replicas is None:
+ replicas = 1
+
+ # Wait for the new number of ready replicas after a CRUD update
+ # Note1: doesn't work correctly when reducing number of replicas due to how VMIRS works (as of kubevirt 1.5.0)
+ # Note2: not the place to wait for the VMIs to get deleted when deleting the VMIRS object; that *might* be
+ # achievable in execute_crud(); keywords: orphanDependents, propagationPolicy, DeleteOptions
+ if self.params.get('wait') and replicas is not None and self.params.get('state') == 'present':
+ result = self.wait_for_replicas(replicas)
+
+ # Return from the module:
+ self.exit_json(**{
+ 'changed': changed,
+ 'kubevirt_rs': result,
+ 'result': result_crud,
+ })
+
+
+def main():
+ module = KubeVirtVMIRS()
+ try:
+ module.execute_module()
+ except Exception as e:
+ module.fail_json(msg=str(e), exception=traceback.format_exc())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_template.py b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_template.py
new file mode 100644
index 00000000..ee478b8d
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_template.py
@@ -0,0 +1,385 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2019, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = '''
+---
+module: kubevirt_template
+
+short_description: Manage KubeVirt templates
+
+description:
+ - Use Openshift Python SDK to manage the state of KubeVirt templates.
+
+
+author: KubeVirt Team (@kubevirt)
+
+options:
+ name:
+ description:
+ - Name of the Template object.
+ required: true
+ type: str
+ namespace:
+ description:
+ - Namespace where the Template object exists.
+ required: true
+ type: str
+ objects:
+ description:
+ - List of any valid API objects, such as a I(DeploymentConfig), I(Service), etc. The object
+ will be created exactly as defined here, with any parameter values substituted in prior to creation.
+ The definition of these objects can reference parameters defined earlier.
+ - As part of the list user can pass also I(VirtualMachine) kind. When passing I(VirtualMachine)
+ user must use Ansible structure of the parameters not the Kubernetes API structure. For more information
+ please take a look at M(community.kubevirt.kubevirt_vm) module and at EXAMPLES section, where you can see example.
+ type: list
+ merge_type:
+ description:
+ - Whether to override the default patch merge approach with a specific type. By default, the strategic
+ merge will typically be used.
+ type: list
+ choices: [ json, merge, strategic-merge ]
+ display_name:
+ description:
+ - "A brief, user-friendly name, which can be employed by user interfaces."
+ type: str
+ description:
+ description:
+ - A description of the template.
+ - Include enough detail that the user will understand what is being deployed...
+ and any caveats they need to know before deploying. It should also provide links to additional information,
+ such as a README file."
+ type: str
+ long_description:
+ description:
+ - "Additional template description. This may be displayed by the service catalog, for example."
+ type: str
+ provider_display_name:
+ description:
+ - "The name of the person or organization providing the template."
+ type: str
+ documentation_url:
+ description:
+ - "A URL referencing further documentation for the template."
+ type: str
+ support_url:
+ description:
+ - "A URL where support can be obtained for the template."
+ type: str
+ editable:
+ description:
+ - "Extension for hinting at which elements should be considered editable.
+ List of jsonpath selectors. The jsonpath root is the objects: element of the template."
+ - This is parameter can be used only when kubevirt addon is installed on your openshift cluster.
+ type: list
+ default_disk:
+ description:
+ - "The goal of default disk is to define what kind of disk is supported by the OS mainly in
+ terms of bus (ide, scsi, sata, virtio, ...)"
+ - The C(default_disk) parameter define configuration overlay for disks that will be applied on top of disks
+ during virtual machine creation to define global compatibility and/or performance defaults defined here.
+ - This is parameter can be used only when kubevirt addon is installed on your openshift cluster.
+ type: dict
+ default_volume:
+ description:
+ - "The goal of default volume is to be able to configure mostly performance parameters like
+ caches if those are exposed by the underlying volume implementation."
+ - The C(default_volume) parameter define configuration overlay for volumes that will be applied on top of volumes
+ during virtual machine creation to define global compatibility and/or performance defaults defined here.
+ - This is parameter can be used only when kubevirt addon is installed on your openshift cluster.
+ type: dict
+ default_nic:
+ description:
+ - "The goal of default network is similar to I(default_disk) and should be used as a template
+ to ensure OS compatibility and performance."
+ - The C(default_nic) parameter define configuration overlay for nic that will be applied on top of nics
+ during virtual machine creation to define global compatibility and/or performance defaults defined here.
+ - This is parameter can be used only when kubevirt addon is installed on your openshift cluster.
+ type: dict
+ default_network:
+ description:
+ - "The goal of default network is similar to I(default_volume) and should be used as a template
+ that specifies performance and connection parameters (L2 bridge for example)"
+ - The C(default_network) parameter define configuration overlay for networks that will be applied on top of networks
+ during virtual machine creation to define global compatibility and/or performance defaults defined here.
+ - This is parameter can be used only when kubevirt addon is installed on your openshift cluster.
+ type: dict
+ icon_class:
+ description:
+ - "An icon to be displayed with your template in the web console. Choose from our existing logo
+ icons when possible. You can also use icons from FontAwesome. Alternatively, provide icons through
+ CSS customizations that can be added to an OpenShift Container Platform cluster that uses your template.
+ You must specify an icon class that exists, or it will prevent falling back to the generic icon."
+ type: str
+ parameters:
+ description:
+ - "Parameters allow a value to be supplied by the user or generated when the template is instantiated.
+ Then, that value is substituted wherever the parameter is referenced. References can be defined in any
+ field in the objects list field. This is useful for generating random passwords or allowing the user to
+ supply a host name or other user-specific value that is required to customize the template."
+ - "More information can be found at: U(https://docs.openshift.com/container-platform/3.6/dev_guide/templates.html#writing-parameters)"
+ type: list
+ version:
+ description:
+ - Template structure version.
+ - This is parameter can be used only when kubevirt addon is installed on your openshift cluster.
+ type: str
+
+extends_documentation_fragment:
+- community.kubernetes.k8s_auth_options
+- community.kubernetes.k8s_state_options
+
+
+requirements:
+ - python >= 2.7
+ - openshift >= 0.8.2
+'''
+
+EXAMPLES = '''
+- name: Create template 'mytemplate'
+ community.kubevirt.kubevirt_template:
+ state: present
+ name: myvmtemplate
+ namespace: templates
+ display_name: Generic cirros template
+ description: Basic cirros template
+ long_description: Verbose description of cirros template
+ provider_display_name: Just Be Cool, Inc.
+ documentation_url: http://theverycoolcompany.com
+ support_url: http://support.theverycoolcompany.com
+ icon_class: icon-linux
+ default_disk:
+ disk:
+ bus: virtio
+ default_nic:
+ model: virtio
+ default_network:
+ resource:
+ resourceName: bridge.network.kubevirt.io/cnvmgmt
+ default_volume:
+ containerDisk:
+ image: kubevirt/cirros-container-disk-demo:latest
+ objects:
+ - name: ${NAME}
+ kind: VirtualMachine
+ memory: ${MEMORY_SIZE}
+ state: present
+ namespace: vms
+ parameters:
+ - name: NAME
+ description: VM name
+ generate: expression
+ from: 'vm-[A-Za-z0-9]{8}'
+ - name: MEMORY_SIZE
+ description: Memory size
+ value: 1Gi
+
+- name: Remove template 'myvmtemplate'
+ community.kubevirt.kubevirt_template:
+ state: absent
+ name: myvmtemplate
+ namespace: templates
+'''
+
+RETURN = '''
+kubevirt_template:
+ description:
+ - The template dictionary specification returned by the API.
+ returned: success
+ type: complex
+ contains: {}
+'''
+
+
+import copy
+import traceback
+
+from ansible_collections.community.kubernetes.plugins.module_utils.common import AUTH_ARG_SPEC
+
+from ansible_collections.community.kubevirt.plugins.module_utils.kubevirt import (
+ virtdict,
+ KubeVirtRawModule,
+ API_GROUP,
+ MAX_SUPPORTED_API_VERSION
+)
+
+
+TEMPLATE_ARG_SPEC = {
+ 'name': {'required': True},
+ 'namespace': {'required': True},
+ 'state': {
+ 'default': 'present',
+ 'choices': ['present', 'absent'],
+ },
+ 'force': {
+ 'type': 'bool',
+ 'default': False,
+ },
+ 'merge_type': {
+ 'type': 'list',
+ 'choices': ['json', 'merge', 'strategic-merge']
+ },
+ 'objects': {
+ 'type': 'list',
+ },
+ 'display_name': {
+ 'type': 'str',
+ },
+ 'description': {
+ 'type': 'str',
+ },
+ 'long_description': {
+ 'type': 'str',
+ },
+ 'provider_display_name': {
+ 'type': 'str',
+ },
+ 'documentation_url': {
+ 'type': 'str',
+ },
+ 'support_url': {
+ 'type': 'str',
+ },
+ 'icon_class': {
+ 'type': 'str',
+ },
+ 'version': {
+ 'type': 'str',
+ },
+ 'editable': {
+ 'type': 'list',
+ },
+ 'default_disk': {
+ 'type': 'dict',
+ },
+ 'default_volume': {
+ 'type': 'dict',
+ },
+ 'default_network': {
+ 'type': 'dict',
+ },
+ 'default_nic': {
+ 'type': 'dict',
+ },
+ 'parameters': {
+ 'type': 'list',
+ },
+}
+
+
+class KubeVirtVMTemplate(KubeVirtRawModule):
+
+ @property
+ def argspec(self):
+ """ argspec property builder """
+ argument_spec = copy.deepcopy(AUTH_ARG_SPEC)
+ argument_spec.update(TEMPLATE_ARG_SPEC)
+ return argument_spec
+
+ def execute_module(self):
+ # Parse parameters specific for this module:
+ definition = virtdict()
+
+ # Execute the CRUD of VM template:
+ kind = 'Template'
+ template_api_version = 'template.openshift.io/v1'
+
+ # Fill in template parameters:
+ definition['parameters'] = self.params.get('parameters')
+
+ # Fill in the default Label
+ labels = definition['metadata']['labels']
+ labels['template.cnv.io/type'] = 'vm'
+
+ # Fill in Openshift/Kubevirt template annotations:
+ annotations = definition['metadata']['annotations']
+ if self.params.get('display_name'):
+ annotations['openshift.io/display-name'] = self.params.get('display_name')
+ if self.params.get('description'):
+ annotations['description'] = self.params.get('description')
+ if self.params.get('long_description'):
+ annotations['openshift.io/long-description'] = self.params.get('long_description')
+ if self.params.get('provider_display_name'):
+ annotations['openshift.io/provider-display-name'] = self.params.get('provider_display_name')
+ if self.params.get('documentation_url'):
+ annotations['openshift.io/documentation-url'] = self.params.get('documentation_url')
+ if self.params.get('support_url'):
+ annotations['openshift.io/support-url'] = self.params.get('support_url')
+ if self.params.get('icon_class'):
+ annotations['iconClass'] = self.params.get('icon_class')
+ if self.params.get('version'):
+ annotations['template.cnv.io/version'] = self.params.get('version')
+
+ # TODO: Make it more Ansiblish, so user don't have to specify API JSON path, but rather Ansible params:
+ if self.params.get('editable'):
+ annotations['template.cnv.io/editable'] = self.params.get('editable')
+
+ # Set defaults annotations:
+ if self.params.get('default_disk'):
+ annotations['defaults.template.cnv.io/disk'] = self.params.get('default_disk').get('name')
+ if self.params.get('default_volume'):
+ annotations['defaults.template.cnv.io/volume'] = self.params.get('default_volume').get('name')
+ if self.params.get('default_nic'):
+ annotations['defaults.template.cnv.io/nic'] = self.params.get('default_nic').get('name')
+ if self.params.get('default_network'):
+ annotations['defaults.template.cnv.io/network'] = self.params.get('default_network').get('name')
+
+ # Process objects:
+ self.client = self.get_api_client()
+ definition['objects'] = []
+ objects = self.params.get('objects') or []
+ for obj in objects:
+ if obj['kind'] != 'VirtualMachine':
+ definition['objects'].append(obj)
+ else:
+ vm_definition = virtdict()
+
+ # Set VM defaults:
+ if self.params.get('default_disk'):
+ vm_definition['spec']['template']['spec']['domain']['devices']['disks'] = [self.params.get('default_disk')]
+ if self.params.get('default_volume'):
+ vm_definition['spec']['template']['spec']['volumes'] = [self.params.get('default_volume')]
+ if self.params.get('default_nic'):
+ vm_definition['spec']['template']['spec']['domain']['devices']['interfaces'] = [self.params.get('default_nic')]
+ if self.params.get('default_network'):
+ vm_definition['spec']['template']['spec']['networks'] = [self.params.get('default_network')]
+
+ # Set kubevirt API version:
+ vm_definition['apiVersion'] = '%s/%s' % (API_GROUP, MAX_SUPPORTED_API_VERSION)
+
+ # Construct k8s vm API object:
+ vm_template = vm_definition['spec']['template']
+ dummy, vm_def = self.construct_vm_template_definition('VirtualMachine', vm_definition, vm_template, obj)
+
+ definition['objects'].append(vm_def)
+
+ # Create template:
+ resource = self.client.resources.get(api_version=template_api_version, kind=kind, name='templates')
+ definition = self.set_defaults(resource, definition)
+ result = self.perform_action(resource, definition)
+
+ # Return from the module:
+ self.exit_json(**{
+ 'changed': result['changed'],
+ 'kubevirt_template': result.pop('result'),
+ 'result': result,
+ })
+
+
+def main():
+ module = KubeVirtVMTemplate()
+ try:
+ module.execute_module()
+ except Exception as e:
+ module.fail_json(msg=str(e), exception=traceback.format_exc())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_vm.py b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_vm.py
new file mode 100644
index 00000000..5526dc12
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/kubevirt/plugins/modules/kubevirt_vm.py
@@ -0,0 +1,469 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2019, Ansible Project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+DOCUMENTATION = '''
+---
+module: kubevirt_vm
+
+short_description: Manage KubeVirt virtual machine
+
+description:
+ - Use Openshift Python SDK to manage the state of KubeVirt virtual machines.
+
+
+author: KubeVirt Team (@kubevirt)
+
+options:
+ state:
+ description:
+ - Set the virtual machine to either I(present), I(absent), I(running) or I(stopped).
+ - "I(present) - Create or update a virtual machine. (And run it if it's ephemeral.)"
+ - "I(absent) - Remove a virtual machine."
+ - "I(running) - Create or update a virtual machine and run it."
+ - "I(stopped) - Stop a virtual machine. (This deletes ephemeral VMs.)"
+ default: "present"
+ choices:
+ - present
+ - absent
+ - running
+ - stopped
+ type: str
+ name:
+ description:
+ - Name of the virtual machine.
+ required: true
+ type: str
+ namespace:
+ description:
+ - Namespace where the virtual machine exists.
+ required: true
+ type: str
+ ephemeral:
+ description:
+ - If (true) ephemeral virtual machine will be created. When destroyed it won't be accessible again.
+ - Works only with C(state) I(present) and I(absent).
+ type: bool
+ default: false
+ datavolumes:
+ description:
+ - "DataVolumes are a way to automate importing virtual machine disks onto pvcs during the virtual machine's
+ launch flow. Without using a DataVolume, users have to prepare a pvc with a disk image before assigning
+ it to a VM or VMI manifest. With a DataVolume, both the pvc creation and import is automated on behalf of the user."
+ type: list
+ template:
+ description:
+ - "Name of Template to be used in creation of a virtual machine."
+ type: str
+ template_parameters:
+ description:
+ - "New values of parameters from Template."
+ type: dict
+
+extends_documentation_fragment:
+- community.kubernetes.k8s_auth_options
+- community.kubevirt.kubevirt_vm_options
+- community.kubevirt.kubevirt_common_options
+
+
+requirements:
+ - python >= 2.7
+ - openshift >= 0.8.2
+'''
+
+EXAMPLES = '''
+- name: Start virtual machine 'myvm'
+ community.kubevirt.kubevirt_vm:
+ state: running
+ name: myvm
+ namespace: vms
+
+- name: Create virtual machine 'myvm' and start it
+ community.kubevirt.kubevirt_vm:
+ state: running
+ name: myvm
+ namespace: vms
+ memory: 64Mi
+ cpu_cores: 1
+ bootloader: efi
+ smbios_uuid: 5d307ca9-b3ef-428c-8861-06e72d69f223
+ cpu_model: Conroe
+ headless: true
+ hugepage_size: 2Mi
+ tablets:
+ - bus: virtio
+ name: tablet1
+ cpu_limit: 3
+ cpu_shares: 2
+ disks:
+ - name: containerdisk
+ volume:
+ containerDisk:
+ image: kubevirt/cirros-container-disk-demo:latest
+ path: /custom-disk/cirros.img
+ disk:
+ bus: virtio
+
+- name: Create virtual machine 'myvm' with multus network interface
+ community.kubevirt.kubevirt_vm:
+ name: myvm
+ namespace: vms
+ memory: 512M
+ interfaces:
+ - name: default
+ bridge: {}
+ network:
+ pod: {}
+ - name: mynet
+ bridge: {}
+ network:
+ multus:
+ networkName: mynetconf
+
+- name: Combine inline definition with Ansible parameters
+ community.kubevirt.kubevirt_vm:
+ # Kubernetes specification:
+ definition:
+ metadata:
+ labels:
+ app: galaxy
+ service: web
+ origin: vmware
+
+ # Ansible parameters:
+ state: running
+ name: myvm
+ namespace: vms
+ memory: 64M
+ disks:
+ - name: containerdisk
+ volume:
+ containerDisk:
+ image: kubevirt/cirros-container-disk-demo:latest
+ path: /custom-disk/cirros.img
+ disk:
+ bus: virtio
+
+- name: Start ephemeral virtual machine 'myvm' and wait to be running
+ community.kubevirt.kubevirt_vm:
+ ephemeral: true
+ state: running
+ wait: true
+ wait_timeout: 180
+ name: myvm
+ namespace: vms
+ memory: 64M
+ labels:
+ kubevirt.io/vm: myvm
+ disks:
+ - name: containerdisk
+ volume:
+ containerDisk:
+ image: kubevirt/cirros-container-disk-demo:latest
+ path: /custom-disk/cirros.img
+ disk:
+ bus: virtio
+
+- name: Start fedora vm with cloud init
+ community.kubevirt.kubevirt_vm:
+ state: running
+ wait: true
+ name: myvm
+ namespace: vms
+ memory: 1024M
+ cloud_init_nocloud:
+ userData: |-
+ #cloud-config
+ password: fedora
+ chpasswd: { expire: False }
+ disks:
+ - name: containerdisk
+ volume:
+ containerDisk:
+ image: kubevirt/fedora-cloud-container-disk-demo:latest
+ path: /disk/fedora.qcow2
+ disk:
+ bus: virtio
+ node_affinity:
+ soft:
+ - weight: 1
+ term:
+ match_expressions:
+ - key: security
+ operator: In
+ values:
+ - S2
+
+- name: Create virtual machine with datavolume and specify node affinity
+ community.kubevirt.kubevirt_vm:
+ name: myvm
+ namespace: default
+ memory: 1024Mi
+ datavolumes:
+ - name: mydv
+ source:
+ http:
+ url: https://url/disk.qcow2
+ pvc:
+ accessModes:
+ - ReadWriteOnce
+ storage: 5Gi
+ node_affinity:
+ hard:
+ - term:
+ match_expressions:
+ - key: security
+ operator: In
+ values:
+ - S1
+
+- name: Remove virtual machine 'myvm'
+ community.kubevirt.kubevirt_vm:
+ state: absent
+ name: myvm
+ namespace: vms
+'''
+
+RETURN = '''
+kubevirt_vm:
+ description:
+ - The virtual machine dictionary specification returned by the API.
+ - "This dictionary contains all values returned by the KubeVirt API all options
+ are described here U(https://kubevirt.io/api-reference/master/definitions.html#_v1_virtualmachine)"
+ returned: success
+ type: complex
+ contains: {}
+'''
+
+
+import copy
+import traceback
+
+from ansible_collections.community.kubernetes.plugins.module_utils.common import AUTH_ARG_SPEC
+from ansible_collections.community.kubevirt.plugins.module_utils.kubevirt import (
+ virtdict,
+ KubeVirtRawModule,
+ VM_COMMON_ARG_SPEC,
+ VM_SPEC_DEF_ARG_SPEC
+)
+
+VM_ARG_SPEC = {
+ 'ephemeral': {'type': 'bool', 'default': False},
+ 'state': {
+ 'type': 'str',
+ 'choices': [
+ 'present', 'absent', 'running', 'stopped'
+ ],
+ 'default': 'present'
+ },
+ 'datavolumes': {'type': 'list'},
+ 'template': {'type': 'str'},
+ 'template_parameters': {'type': 'dict'},
+}
+
+# Which params (can) modify 'spec:' contents of a VM:
+VM_SPEC_PARAMS = list(VM_SPEC_DEF_ARG_SPEC.keys()) + ['datavolumes', 'template', 'template_parameters']
+
+
+class KubeVirtVM(KubeVirtRawModule):
+
+ @property
+ def argspec(self):
+ """ argspec property builder """
+ argument_spec = copy.deepcopy(AUTH_ARG_SPEC)
+ argument_spec.update(VM_COMMON_ARG_SPEC)
+ argument_spec.update(VM_ARG_SPEC)
+ return argument_spec
+
+ @staticmethod
+ def fix_serialization(obj):
+ if obj and hasattr(obj, 'to_dict'):
+ return obj.to_dict()
+ return obj
+
+ def _wait_for_vmi_running(self):
+ for event in self._kind_resource.watch(namespace=self.namespace, timeout=self.params.get('wait_timeout')):
+ entity = event['object']
+ if entity.metadata.name != self.name:
+ continue
+ status = entity.get('status', {})
+ phase = status.get('phase', None)
+ if phase == 'Running':
+ return entity
+
+ self.fail("Timeout occurred while waiting for virtual machine to start. Maybe try a higher wait_timeout value?")
+
+ def _wait_for_vm_state(self, new_state):
+ if new_state == 'running':
+ want_created = want_ready = True
+ else:
+ want_created = want_ready = False
+
+ for event in self._kind_resource.watch(namespace=self.namespace, timeout=self.params.get('wait_timeout')):
+ entity = event['object']
+ if entity.metadata.name != self.name:
+ continue
+ status = entity.get('status', {})
+ created = status.get('created', False)
+ ready = status.get('ready', False)
+ if (created, ready) == (want_created, want_ready):
+ return entity
+
+ self.fail("Timeout occurred while waiting for virtual machine to achieve '{0}' state. "
+ "Maybe try a higher wait_timeout value?".format(new_state))
+
+ def manage_vm_state(self, new_state, already_changed):
+ new_running = True if new_state == 'running' else False
+ changed = False
+ k8s_obj = {}
+
+ if not already_changed:
+ k8s_obj = self.get_resource(self._kind_resource)
+ if not k8s_obj:
+ self.fail("VirtualMachine object disappeared during module operation, aborting.")
+ if k8s_obj.spec.get('running', False) == new_running:
+ return False, k8s_obj
+
+ newdef = dict(metadata=dict(name=self.name, namespace=self.namespace), spec=dict(running=new_running))
+ k8s_obj, err = self.patch_resource(self._kind_resource, newdef, k8s_obj,
+ self.name, self.namespace, merge_type='merge')
+ if err:
+ self.fail_json(**err)
+ else:
+ changed = True
+
+ if self.params.get('wait'):
+ k8s_obj = self._wait_for_vm_state(new_state)
+
+ return changed, k8s_obj
+
+ def _process_template_defaults(self, proccess_template, processedtemplate, defaults):
+ def set_template_default(default_name, default_name_index, definition_spec):
+ default_value = proccess_template['metadata']['annotations'][default_name]
+ if default_value:
+ values = definition_spec[default_name_index]
+ default_values = [d for d in values if d.get('name') == default_value]
+ defaults[default_name_index] = default_values
+ if definition_spec[default_name_index] is None:
+ definition_spec[default_name_index] = []
+ definition_spec[default_name_index].extend([d for d in values if d.get('name') != default_value])
+
+ devices = processedtemplate['spec']['template']['spec']['domain']['devices']
+ spec = processedtemplate['spec']['template']['spec']
+
+ set_template_default('defaults.template.cnv.io/disk', 'disks', devices)
+ set_template_default('defaults.template.cnv.io/volume', 'volumes', spec)
+ set_template_default('defaults.template.cnv.io/nic', 'interfaces', devices)
+ set_template_default('defaults.template.cnv.io/network', 'networks', spec)
+
+ def construct_definition(self, kind, our_state, ephemeral):
+ definition = virtdict()
+ processedtemplate = {}
+
+ # Construct the API object definition:
+ defaults = {'disks': [], 'volumes': [], 'interfaces': [], 'networks': []}
+ vm_template = self.params.get('template')
+ if vm_template:
+ # Find the template the VM should be created from:
+ template_resource = self.client.resources.get(api_version='template.openshift.io/v1', kind='Template', name='templates')
+ proccess_template = template_resource.get(name=vm_template, namespace=self.params.get('namespace'))
+
+ # Set proper template values taken from module option 'template_parameters':
+ for k, v in self.params.get('template_parameters', {}).items():
+ for parameter in proccess_template.parameters:
+ if parameter.name == k:
+ parameter.value = v
+
+ # Proccess the template:
+ processedtemplates_res = self.client.resources.get(api_version='template.openshift.io/v1', kind='Template', name='processedtemplates')
+ processedtemplate = processedtemplates_res.create(proccess_template.to_dict()).to_dict()['objects'][0]
+
+ # Process defaults of the template:
+ self._process_template_defaults(proccess_template, processedtemplate, defaults)
+
+ if not ephemeral:
+ definition['spec']['running'] = our_state == 'running'
+ template = definition if ephemeral else definition['spec']['template']
+ template['metadata']['labels']['vm.cnv.io/name'] = self.params.get('name')
+ dummy, definition = self.construct_vm_definition(kind, definition, template, defaults)
+
+ return self.merge_dicts(definition, processedtemplate)
+
+ def execute_module(self):
+ # Parse parameters specific to this module:
+ ephemeral = self.params.get('ephemeral')
+ k8s_state = our_state = self.params.get('state')
+ kind = 'VirtualMachineInstance' if ephemeral else 'VirtualMachine'
+ _used_params = [name for name in self.params if self.params[name] is not None]
+ # Is 'spec:' getting changed?
+ vm_spec_change = True if set(VM_SPEC_PARAMS).intersection(_used_params) else False
+ changed = False
+ crud_executed = False
+ method = ''
+
+ # Underlying module_utils/k8s/* code knows only of state == present/absent; let's make sure not to confuse it
+ if ephemeral:
+ # Ephemerals don't actually support running/stopped; we treat those as aliases for present/absent instead
+ if our_state == 'running':
+ self.params['state'] = k8s_state = 'present'
+ elif our_state == 'stopped':
+ self.params['state'] = k8s_state = 'absent'
+ else:
+ if our_state != 'absent':
+ self.params['state'] = k8s_state = 'present'
+
+ # Start with fetching the current object to make sure it exists
+ # If it does, but we end up not performing any operations on it, at least we'll be able to return
+ # its current contents as part of the final json
+ self.client = self.get_api_client()
+ self._kind_resource = self.find_supported_resource(kind)
+ k8s_obj = self.get_resource(self._kind_resource)
+ if not self.check_mode and not vm_spec_change and k8s_state != 'absent' and not k8s_obj:
+ self.fail("It's impossible to create an empty VM or change state of a non-existent VM.")
+
+ # If there are (potential) changes to `spec:` or we want to delete the object, that warrants a full CRUD
+ # Also check_mode always warrants a CRUD, as that'll produce a sane result
+ if vm_spec_change or k8s_state == 'absent' or self.check_mode:
+ definition = self.construct_definition(kind, our_state, ephemeral)
+ result = self.execute_crud(kind, definition)
+ changed = result['changed']
+ k8s_obj = result['result']
+ method = result['method']
+ crud_executed = True
+
+ if ephemeral and self.params.get('wait') and k8s_state == 'present' and not self.check_mode:
+ # Waiting for k8s_state==absent is handled inside execute_crud()
+ k8s_obj = self._wait_for_vmi_running()
+
+ if not ephemeral and our_state in ['running', 'stopped'] and not self.check_mode:
+ # State==present/absent doesn't involve any additional VMI state management and is fully
+ # handled inside execute_crud() (including wait logic)
+ patched, k8s_obj = self.manage_vm_state(our_state, crud_executed)
+ changed = changed or patched
+ if changed:
+ method = method or 'patch'
+
+ # Return from the module:
+ self.exit_json(**{
+ 'changed': changed,
+ 'kubevirt_vm': self.fix_serialization(k8s_obj),
+ 'method': method
+ })
+
+
+def main():
+ module = KubeVirtVM()
+ try:
+ module.execute_module()
+ except Exception as e:
+ module.fail_json(msg=str(e), exception=traceback.format_exc())
+
+
+if __name__ == '__main__':
+ main()