Remove kubevirt and set up redirects to community.kubevirt (#1317)

* Remove kubevirt and set up redirects to community.kubevirt

This also removes the dependency on community.kubernetes which fixes
https://github.com/ansible-collections/community.general/issues/354.

* Update changelogs/fragments/1317-kubevirt-migration-removal.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update changelogs/fragments/1317-kubevirt-migration-removal.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Add missed redirects

Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
David Moreau Simard 2021-01-05 15:35:22 -05:00 committed by GitHub
commit e53f153e30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 35 additions and 3591 deletions

View file

@ -1,184 +0,0 @@
#!/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.general.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()

View file

@ -1,154 +0,0 @@
#!/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.general.kubevirt_vm_options
- community.general.kubevirt_common_options
requirements:
- python >= 2.7
- openshift >= 0.8.2
'''
EXAMPLES = '''
- name: Create virtual machine preset 'vmi-preset-small'
community.general.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.general.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.general.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()

View file

@ -1,457 +0,0 @@
#!/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.general.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.general.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.general.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.general.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.general.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.general.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.general.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()

View file

@ -1,211 +0,0 @@
#!/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.general.kubevirt_vm_options
- community.general.kubevirt_common_options
requirements:
- python >= 2.7
- openshift >= 0.8.2
'''
EXAMPLES = '''
- name: Create virtual machine replica set 'myvmir'
community.general.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.general.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.general.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()

View file

@ -1,385 +0,0 @@
#!/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.general.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.general.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.general.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.general.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()

View file

@ -1,469 +0,0 @@
#!/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.general.kubevirt_vm_options
- community.general.kubevirt_common_options
requirements:
- python >= 2.7
- openshift >= 0.8.2
'''
EXAMPLES = '''
- name: Start virtual machine 'myvm'
community.general.kubevirt_vm:
state: running
name: myvm
namespace: vms
- name: Create virtual machine 'myvm' and start it
community.general.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.general.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.general.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.general.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.general.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.general.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.general.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.general.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()