kubevirt_vm: Improve create VM from template (#55927)

pull/56700/head
Ondra Machacek 6 years ago committed by Martin Krizek
parent 3b4b2e5021
commit a4144e15c0

@ -10,6 +10,7 @@ from distutils.version import Version
from ansible.module_utils.k8s.common import list_dict_str from ansible.module_utils.k8s.common import list_dict_str
from ansible.module_utils.k8s.raw import KubernetesRawModule from ansible.module_utils.k8s.raw import KubernetesRawModule
import copy
import re import re
MAX_SUPPORTED_API_VERSION = 'v1alpha3' MAX_SUPPORTED_API_VERSION = 'v1alpha3'
@ -126,17 +127,24 @@ class KubeVirtRawModule(KubernetesRawModule):
super(KubeVirtRawModule, self).__init__(*args, **kwargs) super(KubeVirtRawModule, self).__init__(*args, **kwargs)
@staticmethod @staticmethod
def merge_dicts(x, y): def merge_dicts(x, yy):
""" """
This function merge two dictionaries, where the first dict has This function merge two dictionaries, where the first dict has
higher priority in merging two same keys. higher priority in merging two same keys.
""" """
if not yy:
yy = {}
if not isinstance(yy, list):
yy = [yy]
for y in yy:
for k in set(x.keys()).union(y.keys()): for k in set(x.keys()).union(y.keys()):
if k in x and k in y: if k in x and k in y:
if isinstance(x[k], dict) and isinstance(y[k], dict): if isinstance(x[k], dict) and isinstance(y[k], dict):
yield (k, dict(KubeVirtRawModule.merge_dicts(x[k], y[k]))) yield (k, dict(KubeVirtRawModule.merge_dicts(x[k], y[k])))
else: else:
yield (k, y[k]) yield (k, x[k])
elif k in x: elif k in x:
yield (k, x[k]) yield (k, x[k])
else: else:
@ -215,16 +223,23 @@ class KubeVirtRawModule(KubernetesRawModule):
'disk': {'bus': 'virtio'}, 'disk': {'bus': 'virtio'},
}) })
def _define_interfaces(self, interfaces, template_spec): def _define_interfaces(self, interfaces, template_spec, defaults):
""" """
Takes interfaces parameter of Ansible and create kubevirt API interfaces Takes interfaces parameter of Ansible and create kubevirt API interfaces
and networks strucutre out from it. and networks strucutre out from it.
""" """
if not interfaces and defaults and 'interfaces' in defaults:
interfaces = copy.deepcopy(defaults['interfaces'])
for d in interfaces:
d['network'] = defaults['networks'][0]
if interfaces: if interfaces:
# Extract interfaces k8s specification from interfaces list passed to Ansible: # Extract interfaces k8s specification from interfaces list passed to Ansible:
spec_interfaces = [] spec_interfaces = []
for i in interfaces: for i in interfaces:
spec_interfaces.append(dict((k, v) for k, v in i.items() if k != 'network')) spec_interfaces.append(
dict(self.merge_dicts(dict((k, v) for k, v in i.items() if k != 'network'), defaults['interfaces']))
)
if 'interfaces' not in template_spec['domain']['devices']: if 'interfaces' not in template_spec['domain']['devices']:
template_spec['domain']['devices']['interfaces'] = [] template_spec['domain']['devices']['interfaces'] = []
template_spec['domain']['devices']['interfaces'].extend(spec_interfaces) template_spec['domain']['devices']['interfaces'].extend(spec_interfaces)
@ -234,21 +249,28 @@ class KubeVirtRawModule(KubernetesRawModule):
for i in interfaces: for i in interfaces:
net = i['network'] net = i['network']
net['name'] = i['name'] net['name'] = i['name']
spec_networks.append(net) spec_networks.append(dict(self.merge_dicts(net, defaults['networks'])))
if 'networks' not in template_spec: if 'networks' not in template_spec:
template_spec['networks'] = [] template_spec['networks'] = []
template_spec['networks'].extend(spec_networks) template_spec['networks'].extend(spec_networks)
def _define_disks(self, disks, template_spec): def _define_disks(self, disks, template_spec, defaults):
""" """
Takes disks parameter of Ansible and create kubevirt API disks and Takes disks parameter of Ansible and create kubevirt API disks and
volumes strucutre out from it. volumes strucutre out from it.
""" """
if not disks and defaults and 'disks' in defaults:
disks = copy.deepcopy(defaults['disks'])
for d in disks:
d['volume'] = defaults['volumes'][0]
if disks: if disks:
# Extract k8s specification from disks list passed to Ansible: # Extract k8s specification from disks list passed to Ansible:
spec_disks = [] spec_disks = []
for d in disks: for d in disks:
spec_disks.append(dict((k, v) for k, v in d.items() if k != 'volume')) spec_disks.append(
dict(self.merge_dicts(dict((k, v) for k, v in d.items() if k != 'volume'), defaults['disks']))
)
if 'disks' not in template_spec['domain']['devices']: if 'disks' not in template_spec['domain']['devices']:
template_spec['domain']['devices']['disks'] = [] template_spec['domain']['devices']['disks'] = []
template_spec['domain']['devices']['disks'].extend(spec_disks) template_spec['domain']['devices']['disks'].extend(spec_disks)
@ -258,7 +280,7 @@ class KubeVirtRawModule(KubernetesRawModule):
for d in disks: for d in disks:
volume = d['volume'] volume = d['volume']
volume['name'] = d['name'] volume['name'] = d['name']
spec_volumes.append(volume) spec_volumes.append(dict(self.merge_dicts(volume, defaults['volumes'])))
if 'volumes' not in template_spec: if 'volumes' not in template_spec:
template_spec['volumes'] = [] template_spec['volumes'] = []
template_spec['volumes'].extend(spec_volumes) template_spec['volumes'].extend(spec_volumes)
@ -274,7 +296,7 @@ class KubeVirtRawModule(KubernetesRawModule):
self.fail("API versions {0} are too recent. Max supported is {1}/{2}.".format( self.fail("API versions {0} are too recent. Max supported is {1}/{2}.".format(
str([r.api_version for r in sr]), API_GROUP, MAX_SUPPORTED_API_VERSION)) str([r.api_version for r in sr]), API_GROUP, MAX_SUPPORTED_API_VERSION))
def _construct_vm_definition(self, kind, definition, template, params): def _construct_vm_definition(self, kind, definition, template, params, defaults=None):
self.client = self.get_api_client() self.client = self.get_api_client()
disks = params.get('disks', []) disks = params.get('disks', [])
@ -343,7 +365,7 @@ class KubeVirtRawModule(KubernetesRawModule):
template_spec['domain']['devices']['autoattachGraphicsDevice'] = not headless template_spec['domain']['devices']['autoattachGraphicsDevice'] = not headless
# Define disks # Define disks
self._define_disks(disks, template_spec) self._define_disks(disks, template_spec, defaults)
# Define cloud init disk if defined: # Define cloud init disk if defined:
# Note, that this must be called after _define_disks, so the cloud_init # Note, that this must be called after _define_disks, so the cloud_init
@ -351,18 +373,15 @@ class KubeVirtRawModule(KubernetesRawModule):
self._define_cloud_init(cloud_init_nocloud, template_spec) self._define_cloud_init(cloud_init_nocloud, template_spec)
# Define interfaces: # Define interfaces:
self._define_interfaces(interfaces, template_spec) self._define_interfaces(interfaces, template_spec, defaults)
# Define datavolumes: # Define datavolumes:
self._define_datavolumes(datavolumes, definition['spec']) self._define_datavolumes(datavolumes, definition['spec'])
# Perform create/absent action: return dict(self.merge_dicts(definition, self.resource_definitions[0]))
definition = dict(self.merge_dicts(self.resource_definitions[0], definition))
resource = self.find_supported_resource(kind)
return dict(self.merge_dicts(self.resource_definitions[0], definition))
def construct_vm_definition(self, kind, definition, template): def construct_vm_definition(self, kind, definition, template, defaults=None):
definition = self._construct_vm_definition(kind, definition, template, self.params) definition = self._construct_vm_definition(kind, definition, template, self.params, defaults)
resource = self.find_supported_resource(kind) resource = self.find_supported_resource(kind)
definition = self.set_defaults(resource, definition) definition = self.set_defaults(resource, definition)
return resource, definition return resource, definition

@ -232,8 +232,6 @@ kubevirt_vm:
import copy import copy
import traceback import traceback
from ansible.module_utils.k8s.common import AUTH_ARG_SPEC
from ansible.module_utils.k8s.common import AUTH_ARG_SPEC from ansible.module_utils.k8s.common import AUTH_ARG_SPEC
from ansible.module_utils.kubevirt import ( from ansible.module_utils.kubevirt import (
virtdict, virtdict,
@ -253,7 +251,7 @@ VM_ARG_SPEC = {
}, },
'datavolumes': {'type': 'list'}, 'datavolumes': {'type': 'list'},
'template': {'type': 'str'}, 'template': {'type': 'str'},
'template_parameters': {'type': 'dict'}, 'template_parameters': {'type': 'dict', 'default': {}},
} }
# Which params (can) modify 'spec:' contents of a VM: # Which params (can) modify 'spec:' contents of a VM:
@ -332,11 +330,31 @@ class KubeVirtVM(KubeVirtRawModule):
return changed, k8s_obj 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): def construct_definition(self, kind, our_state, ephemeral):
definition = virtdict() definition = virtdict()
processedtemplate = {} processedtemplate = {}
# Construct the API object definition: # Construct the API object definition:
defaults = {'disks': [], 'volumes': [], 'interfaces': [], 'networks': []}
vm_template = self.params.get('template') vm_template = self.params.get('template')
if vm_template: if vm_template:
# Find the template the VM should be created from: # Find the template the VM should be created from:
@ -353,14 +371,16 @@ class KubeVirtVM(KubeVirtRawModule):
processedtemplates_res = self.client.resources.get(api_version='template.openshift.io/v1', kind='Template', name='processedtemplates') 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] 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: if not ephemeral:
definition['spec']['running'] = our_state == 'running' definition['spec']['running'] = our_state == 'running'
template = definition if ephemeral else definition['spec']['template'] template = definition if ephemeral else definition['spec']['template']
template['metadata']['labels']['vm.cnv.io/name'] = self.params.get('name') template['metadata']['labels']['vm.cnv.io/name'] = self.params.get('name')
dummy, definition = self.construct_vm_definition(kind, definition, template) dummy, definition = self.construct_vm_definition(kind, definition, template, defaults)
definition = dict(self.merge_dicts(processedtemplate, definition))
return definition return dict(self.merge_dicts(definition, processedtemplate))
def execute_module(self): def execute_module(self):
# Parse parameters specific to this module: # Parse parameters specific to this module:

Loading…
Cancel
Save