diff --git a/lib/ansible/modules/cloud/ovirt/ovirt_vmpool.py b/lib/ansible/modules/cloud/ovirt/ovirt_vmpool.py index a2f2adfdc13..df4bad78ddd 100644 --- a/lib/ansible/modules/cloud/ovirt/ovirt_vmpool.py +++ b/lib/ansible/modules/cloud/ovirt/ovirt_vmpool.py @@ -81,6 +81,71 @@ options: description: - "Number of VMs in the pool." - "Default value is set by engine." + vm: + description: + - "For creating vm pool without editing template." + - "Note: You can use C(vm) only for creating vm pool." + type: dict + suboptions: + comment: + description: + - Comment of the Virtual Machine. + timezone: + description: + - Sets time zone offset of the guest hardware clock. + - For example C(Etc/GMT) + memory: + description: + - Amount of memory of the Virtual Machine. Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB). + - Default value is set by engine. + memory_guaranteed: + description: + - Amount of minimal guaranteed memory of the Virtual Machine. + Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB). + - C(memory_guaranteed) parameter can't be lower than C(memory) parameter. + - Default value is set by engine. + memory_max: + description: + - Upper bound of virtual machine memory up to which memory hot-plug can be performed. + Prefix uses IEC 60027-2 standard (for example 1GiB, 1024MiB). + - Default value is set by engine. + cloud_init: + description: + - Dictionary with values for Unix-like Virtual Machine initialization using cloud init. + - C(host_name) - Hostname to be set to Virtual Machine when deployed. + - C(timezone) - Timezone to be set to Virtual Machine when deployed. + - C(user_name) - Username to be used to set password to Virtual Machine when deployed. + - C(root_password) - Password to be set for user specified by C(user_name) parameter. + - C(authorized_ssh_keys) - Use this SSH keys to login to Virtual Machine. + - C(regenerate_ssh_keys) - If I(True) SSH keys will be regenerated on Virtual Machine. + - C(custom_script) - Cloud-init script which will be executed on Virtual Machine when deployed. This is appended to the end of the + cloud-init script generated by any other options. + - C(dns_servers) - DNS servers to be configured on Virtual Machine. + - C(dns_search) - DNS search domains to be configured on Virtual Machine. + - C(nic_boot_protocol) - Set boot protocol of the network interface of Virtual Machine. Can be one of C(none), C(dhcp) or C(static). + - C(nic_ip_address) - If boot protocol is static, set this IP address to network interface of Virtual Machine. + - C(nic_netmask) - If boot protocol is static, set this netmask to network interface of Virtual Machine. + - C(nic_gateway) - If boot protocol is static, set this gateway to network interface of Virtual Machine. + - C(nic_name) - Set name to network interface of Virtual Machine. + - C(nic_on_boot) - If I(True) network interface will be set to start on boot. + sso: + description: + - "I(True) enable Single Sign On by Guest Agent, I(False) to disable it. By default is chosen by oVirt/RHV engine." + type: bool + smartcard_enabled: + description: + - "If I(true), use smart card authentication." + type: bool + nics: + description: + - List of NICs, which should be attached to Virtual Machine. NIC is described by following dictionary. + - C(name) - Name of the NIC. + - C(profile_name) - Profile name where NIC should be attached. + - C(interface) - Type of the network interface. One of following I(virtio), I(e1000), I(rtl8139), default is I(virtio). + - C(mac_address) - Custom MAC address of the network interface, by default it's obtained from MAC pool. + - NOTE - This parameter is used only when C(state) is I(running) or I(present) and is able to only create NICs. + - To manage NICs of the VM in more depth please use M(ovirt_nics) module instead. + version_added: "2.9" extends_documentation_fragment: ovirt ''' @@ -88,8 +153,8 @@ EXAMPLES = ''' # Examples don't contain auth parameter for simplicity, # look at ovirt_auth module to see how to reuse authentication: -# Create VM pool from template -- ovirt_vmpool: +- name: Create VM pool from template + ovirt_vmpool: cluster: mycluster name: myvmpool template: rhel7 @@ -97,15 +162,50 @@ EXAMPLES = ''' prestarted: 2 vm_per_user: 1 -# Remove vmpool, note that all VMs in pool will be stopped and removed: -- ovirt_vmpool: +- name: Remove vmpool, note that all VMs in pool will be stopped and removed + ovirt_vmpool: state: absent name: myvmpool -# Change Pool Name -- ovirt_vmpool: +- name: Change Pool Name + ovirt_vmpool: id: 00000000-0000-0000-0000-000000000000 name: "new_pool_name" + +- name: Create vm pool and override the pool values + ovirt_vmpool: + cluster: mycluster + name: vmpool + template: blank + vm_count: 2 + prestarted: 1 + vm_per_user: 1 + vm: + memory: 4GiB + memory_guaranteed: 4GiB + memory_max: 10GiB + comment: vncomment + cloud_init: + nic_boot_protocol: static + nic_ip_address: 10.34.60.86 + nic_netmask: 255.255.252.0 + nic_gateway: 10.34.63.254 + nic_name: eth1 + nic_on_boot: true + host_name: example.com + custom_script: | + write_files: + - content: | + Hello, world! + path: /tmp/greeting.txt + permissions: '0644' + user_name: root + root_password: super_password + nics: + - name: nicname + interface: virtio + profile_name: network + ''' RETURN = ''' @@ -138,12 +238,18 @@ from ansible.module_utils.ovirt import ( get_link_name, ovirt_full_argument_spec, wait, + convert_to_bytes, + search_by_name, ) class VmPoolsModule(BaseModule): + def __init__(self, *args, **kwargs): + super(VmPoolsModule, self).__init__(*args, **kwargs) + self._initialization = None def build_entity(self): + vm = self.param('vm') return otypes.VmPool( id=self._module.params['id'], name=self._module.params['name'], @@ -161,8 +267,148 @@ class VmPoolsModule(BaseModule): type=otypes.VmPoolType( self._module.params['type'] ) if self._module.params['type'] else None, + vm=self.build_vm(vm) ) + def build_vm(self, vm): + return otypes.Vm( + comment=vm.get('comment'), + memory=convert_to_bytes( + vm.get('memory') + ) if vm.get('memory') else None, + memory_policy=otypes.MemoryPolicy( + guaranteed=convert_to_bytes(vm.get('memory_guaranteed')), + max=convert_to_bytes(vm.get('memory_max')), + ) if any(( + vm.get('memory_guaranteed'), + vm.get('memory_max') + )) else None, + initialization=self.get_initialization(vm), + display=otypes.Display( + smartcard_enabled=vm.get('smartcard_enabled') + ) if vm.get('smartcard_enabled') is not None else None, + sso=( + otypes.Sso( + methods=[otypes.Method(id=otypes.SsoMethod.GUEST_AGENT)] if vm.get('sso') else [] + ) + ) if vm.get('sso') is not None else None, + time_zone=otypes.TimeZone( + name=vm.get('timezone'), + ) if vm.get('timezone') else None, + ) + + def get_initialization(self, vm): + if self._initialization is not None: + return self._initialization + + sysprep = vm.get('sysprep') + cloud_init = vm.get('cloud_init') + cloud_init_nics = vm.get('cloud_init_nics') or [] + if cloud_init is not None: + cloud_init_nics.append(cloud_init) + + if cloud_init or cloud_init_nics: + self._initialization = otypes.Initialization( + nic_configurations=[ + otypes.NicConfiguration( + boot_protocol=otypes.BootProtocol( + nic.pop('nic_boot_protocol').lower() + ) if nic.get('nic_boot_protocol') else None, + name=nic.pop('nic_name', None), + on_boot=nic.pop('nic_on_boot', None), + ip=otypes.Ip( + address=nic.pop('nic_ip_address', None), + netmask=nic.pop('nic_netmask', None), + gateway=nic.pop('nic_gateway', None), + ) if ( + nic.get('nic_gateway') is not None or + nic.get('nic_netmask') is not None or + nic.get('nic_ip_address') is not None + ) else None, + ) + for nic in cloud_init_nics + if ( + nic.get('nic_gateway') is not None or + nic.get('nic_netmask') is not None or + nic.get('nic_ip_address') is not None or + nic.get('nic_boot_protocol') is not None or + nic.get('nic_on_boot') is not None + ) + ] if cloud_init_nics else None, + **cloud_init + ) + elif sysprep: + self._initialization = otypes.Initialization( + **sysprep + ) + return self._initialization + + def get_vms(self, entity): + vms = self._connection.system_service().vms_service().list() + resp = [] + for vm in vms: + if vm.vm_pool is not None and vm.vm_pool.id == entity.id: + resp.append(vm) + return resp + + def post_create(self, entity): + vm_param = self.param('vm') + if vm_param is not None and vm_param.get('nics') is not None: + vms = self.get_vms(entity) + for vm in vms: + self.__attach_nics(vm, vm_param) + + def __attach_nics(self, entity, vm_param): + # Attach NICs to VM, if specified: + vms_service = self._connection.system_service().vms_service() + nics_service = vms_service.service(entity.id).nics_service() + for nic in vm_param.get('nics'): + if search_by_name(nics_service, nic.get('name')) is None: + if not self._module.check_mode: + nics_service.add( + otypes.Nic( + name=nic.get('name'), + interface=otypes.NicInterface( + nic.get('interface', 'virtio') + ), + vnic_profile=otypes.VnicProfile( + id=self.__get_vnic_profile_id(nic), + ) if nic.get('profile_name') else None, + mac=otypes.Mac( + address=nic.get('mac_address') + ) if nic.get('mac_address') else None, + ) + ) + self.changed = True + + def __get_vnic_profile_id(self, nic): + """ + Return VNIC profile ID looked up by it's name, because there can be + more VNIC profiles with same name, other criteria of filter is cluster. + """ + vnics_service = self._connection.system_service().vnic_profiles_service() + clusters_service = self._connection.system_service().clusters_service() + cluster = search_by_name(clusters_service, self.param('cluster')) + profiles = [ + profile for profile in vnics_service.list() + if profile.name == nic.get('profile_name') + ] + cluster_networks = [ + net.id for net in self._connection.follow_link(cluster.networks) + ] + try: + return next( + profile.id for profile in profiles + if profile.network.id in cluster_networks + ) + except StopIteration: + raise Exception( + "Profile '%s' was not found in cluster '%s'" % ( + nic.get('profile_name'), + self.param('cluster') + ) + ) + def update_check(self, entity): return ( equal(self._module.params.get('name'), entity.name) and @@ -186,6 +432,7 @@ def main(): template=dict(default=None), cluster=dict(default=None), description=dict(default=None), + vm=dict(default=None, type='dict'), comment=dict(default=None), vm_per_user=dict(default=None, type='int'), prestarted=dict(default=None, type='int'),