forked from ansible/ansible
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
kubevirt: Add new kubevirt_vm module (ansible#50768)
This module is managing virtual machines using KubeVirt. Signed-off-by: Ondra Machacek <[email protected]>
- Loading branch information
1 parent
6e409a9
commit 797a521
Showing
9 changed files
with
792 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
|
||
# Copyright (c) 2018, KubeVirt Team <@kubevirt> | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
from collections import defaultdict | ||
|
||
from ansible.module_utils.k8s.common import list_dict_str | ||
from ansible.module_utils.k8s.raw import KubernetesRawModule | ||
|
||
try: | ||
from openshift import watch | ||
from openshift.helper.exceptions import KubernetesException | ||
except ImportError: | ||
# Handled in k8s common: | ||
pass | ||
|
||
|
||
API_VERSION = 'kubevirt.io/v1alpha3' | ||
|
||
|
||
VM_COMMON_ARG_SPEC = { | ||
'name': {}, | ||
'force': { | ||
'type': 'bool', | ||
'default': False, | ||
}, | ||
'resource_definition': { | ||
'type': list_dict_str, | ||
'aliases': ['definition', 'inline'] | ||
}, | ||
'src': { | ||
'type': 'path', | ||
}, | ||
'namespace': {}, | ||
'api_version': {'type': 'str', 'default': API_VERSION, 'aliases': ['api', 'version']}, | ||
'merge_type': {'type': 'list', 'choices': ['json', 'merge', 'strategic-merge']}, | ||
'wait': {'type': 'bool', 'default': True}, | ||
'wait_timeout': {'type': 'int', 'default': 120}, | ||
'memory': {'type': 'str'}, | ||
'disks': {'type': 'list'}, | ||
'labels': {'type': 'dict'}, | ||
'interfaces': {'type': 'list'}, | ||
'machine_type': {'type': 'str'}, | ||
'cloud_init_nocloud': {'type': 'dict'}, | ||
} | ||
|
||
|
||
def virtdict(): | ||
""" | ||
This function create dictionary, with defaults to dictionary. | ||
""" | ||
return defaultdict(virtdict) | ||
|
||
|
||
class KubeVirtRawModule(KubernetesRawModule): | ||
def __init__(self, *args, **kwargs): | ||
self.api_version = API_VERSION | ||
super(KubeVirtRawModule, self).__init__(*args, **kwargs) | ||
|
||
@staticmethod | ||
def merge_dicts(x, y): | ||
""" | ||
This function merge two dictionaries, where the first dict has | ||
higher priority in merging two same keys. | ||
""" | ||
for k in set(x.keys()).union(y.keys()): | ||
if k in x and k in y: | ||
if isinstance(x[k], dict) and isinstance(y[k], dict): | ||
yield (k, dict(KubeVirtRawModule.merge_dicts(x[k], y[k]))) | ||
else: | ||
yield (k, y[k]) | ||
elif k in x: | ||
yield (k, x[k]) | ||
else: | ||
yield (k, y[k]) | ||
|
||
def _create_stream(self, resource, namespace, wait_timeout): | ||
""" Create a stream of events for the object """ | ||
w = None | ||
stream = None | ||
try: | ||
w = watch.Watch() | ||
w._api_client = self.client.client | ||
stream = w.stream(resource.get, serialize=False, namespace=namespace, timeout_seconds=wait_timeout) | ||
except KubernetesException as exc: | ||
self.fail_json(msg='Failed to initialize watch: {0}'.format(exc.message)) | ||
return w, stream | ||
|
||
def get_resource(self, resource): | ||
try: | ||
existing = resource.get(name=self.name, namespace=self.namespace) | ||
except Exception: | ||
existing = None | ||
|
||
return existing | ||
|
||
def _define_cloud_init(self, cloud_init_nocloud, template_spec): | ||
""" | ||
Takes the user's cloud_init_nocloud parameter and fill it in kubevirt | ||
API strucuture. The name of the volume is hardcoded to ansiblecloudinitvolume | ||
and the name for disk is hardcoded to ansiblecloudinitdisk. | ||
""" | ||
if cloud_init_nocloud: | ||
if not template_spec['volumes']: | ||
template_spec['volumes'] = [] | ||
if not template_spec['domain']['devices']['disks']: | ||
template_spec['domain']['devices']['disks'] = [] | ||
|
||
template_spec['volumes'].append({'name': 'ansiblecloudinitvolume', 'cloudInitNoCloud': cloud_init_nocloud}) | ||
template_spec['domain']['devices']['disks'].append({ | ||
'name': 'ansiblecloudinitdisk', | ||
'volumeName': 'ansiblecloudinitvolume', | ||
'disk': {'bus': 'virtio'}, | ||
}) | ||
|
||
def _define_interfaces(self, interfaces, template_spec): | ||
""" | ||
Takes interfaces parameter of Ansible and create kubevirt API interfaces | ||
and networks strucutre out from it. | ||
""" | ||
if interfaces: | ||
# Extract interfaces k8s specification from interfaces list passed to Ansible: | ||
spec_interfaces = [] | ||
for i in interfaces: | ||
spec_interfaces.append(dict((k, v) for k, v in i.items() if k != 'network')) | ||
template_spec['domain']['devices']['interfaces'] = spec_interfaces | ||
|
||
# Extract networks k8s specification from interfaces list passed to Ansible: | ||
spec_networks = [] | ||
for i in interfaces: | ||
net = i['network'] | ||
net['name'] = i['name'] | ||
spec_networks.append(net) | ||
template_spec['networks'] = spec_networks | ||
|
||
def _define_disks(self, disks, template_spec): | ||
""" | ||
Takes disks parameter of Ansible and create kubevirt API disks and | ||
volumes strucutre out from it. | ||
""" | ||
if disks: | ||
# Extract k8s specification from disks list passed to Ansible: | ||
spec_disks = [] | ||
for d in disks: | ||
spec_disks.append(dict((k, v) for k, v in d.items() if k != 'volume')) | ||
template_spec['domain']['devices']['disks'] = spec_disks | ||
|
||
# Extract volumes k8s specification from disks list passed to Ansible: | ||
spec_volumes = [] | ||
for d in disks: | ||
volume = d['volume'] | ||
volume['name'] = d['name'] | ||
spec_volumes.append(volume) | ||
template_spec['volumes'] = spec_volumes | ||
|
||
def execute_crud(self, kind, definition, template): | ||
""" Module execution """ | ||
self.client = self.get_api_client() | ||
|
||
disks = self.params.get('disks', []) | ||
memory = self.params.get('memory') | ||
labels = self.params.get('labels') | ||
interfaces = self.params.get('interfaces') | ||
cloud_init_nocloud = self.params.get('cloud_init_nocloud') | ||
machine_type = self.params.get('machine_type') | ||
template_spec = template['spec'] | ||
|
||
# Merge additional flat parameters: | ||
if memory: | ||
template_spec['domain']['resources']['requests']['memory'] = memory | ||
|
||
if labels: | ||
template['metadata']['labels'] = labels | ||
|
||
if machine_type: | ||
template_spec['domain']['machine']['type'] = machine_type | ||
|
||
# Define cloud init disk if defined: | ||
self._define_cloud_init(cloud_init_nocloud, template_spec) | ||
|
||
# Define disks | ||
self._define_disks(disks, template_spec) | ||
|
||
# Define interfaces: | ||
self._define_interfaces(interfaces, template_spec) | ||
|
||
# Perform create/absent action: | ||
definition = dict(self.merge_dicts(self.resource_definitions[0], definition)) | ||
resource = self.find_resource(kind, self.api_version, fail=True) | ||
definition = self.set_defaults(resource, definition) | ||
return self.perform_action(resource, definition) |
Empty file.
Oops, something went wrong.