mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-22 04:40:22 -07:00
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:
parent
ddaad1e650
commit
e53f153e30
34 changed files with 35 additions and 3591 deletions
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
Loading…
Add table
Add a link
Reference in a new issue