1#!/usr/bin/python 2 3# Copyright: (c) 2013, Vincent Van der Kussen <vincent at vanderkussen.org> 4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 6from __future__ import absolute_import, division, print_function 7__metaclass__ = type 8 9ANSIBLE_METADATA = {'metadata_version': '1.1', 10 'status': ['preview'], 11 'supported_by': 'community'} 12 13DOCUMENTATION = ''' 14--- 15module: ovirt 16author: 17- Vincent Van der Kussen (@vincentvdk) 18short_description: oVirt/RHEV platform management 19description: 20 - This module only supports oVirt/RHEV version 3. A newer module M(ovirt_vm) supports oVirt/RHV version 4. 21 - Allows you to create new instances, either from scratch or an image, in addition to deleting or stopping instances on the oVirt/RHEV platform. 22version_added: "1.4" 23options: 24 user: 25 description: 26 - The user to authenticate with. 27 required: true 28 url: 29 description: 30 - The url of the oVirt instance. 31 required: true 32 instance_name: 33 description: 34 - The name of the instance to use. 35 required: true 36 aliases: [ vmname ] 37 password: 38 description: 39 - Password of the user to authenticate with. 40 required: true 41 image: 42 description: 43 - The template to use for the instance. 44 resource_type: 45 description: 46 - Whether you want to deploy an image or create an instance from scratch. 47 choices: [ new, template ] 48 zone: 49 description: 50 - Deploy the image to this oVirt cluster. 51 instance_disksize: 52 description: 53 - Size of the instance's disk in GB. 54 aliases: [ vm_disksize] 55 instance_cpus: 56 description: 57 - The instance's number of CPUs. 58 default: 1 59 aliases: [ vmcpus ] 60 instance_nic: 61 description: 62 - The name of the network interface in oVirt/RHEV. 63 aliases: [ vmnic ] 64 instance_network: 65 description: 66 - The logical network the machine should belong to. 67 default: rhevm 68 aliases: [ vmnetwork ] 69 instance_mem: 70 description: 71 - The instance's amount of memory in MB. 72 aliases: [ vmmem ] 73 instance_type: 74 description: 75 - Define whether the instance is a server, desktop or high_performance. 76 - I(high_performance) is supported since Ansible 2.5 and oVirt/RHV 4.2. 77 choices: [ desktop, server, high_performance ] 78 default: server 79 aliases: [ vmtype ] 80 disk_alloc: 81 description: 82 - Define whether disk is thin or preallocated. 83 choices: [ preallocated, thin ] 84 default: thin 85 disk_int: 86 description: 87 - Interface type of the disk. 88 choices: [ ide, virtio ] 89 default: virtio 90 instance_os: 91 description: 92 - Type of Operating System. 93 aliases: [ vmos ] 94 instance_cores: 95 description: 96 - Define the instance's number of cores. 97 default: 1 98 aliases: [ vmcores ] 99 sdomain: 100 description: 101 - The Storage Domain where you want to create the instance's disk on. 102 region: 103 description: 104 - The oVirt/RHEV datacenter where you want to deploy to. 105 instance_dns: 106 description: 107 - Define the instance's Primary DNS server. 108 aliases: [ dns ] 109 version_added: "2.1" 110 instance_domain: 111 description: 112 - Define the instance's Domain. 113 aliases: [ domain ] 114 version_added: "2.1" 115 instance_hostname: 116 description: 117 - Define the instance's Hostname. 118 aliases: [ hostname ] 119 version_added: "2.1" 120 instance_ip: 121 description: 122 - Define the instance's IP. 123 aliases: [ ip ] 124 version_added: "2.1" 125 instance_netmask: 126 description: 127 - Define the instance's Netmask. 128 aliases: [ netmask ] 129 version_added: "2.1" 130 instance_rootpw: 131 description: 132 - Define the instance's Root password. 133 aliases: [ rootpw ] 134 version_added: "2.1" 135 instance_key: 136 description: 137 - Define the instance's Authorized key. 138 aliases: [ key ] 139 version_added: "2.1" 140 state: 141 description: 142 - Create, terminate or remove instances. 143 choices: [ absent, present, restarted, shutdown, started ] 144 default: present 145requirements: 146 - ovirt-engine-sdk-python 147''' 148 149EXAMPLES = ''' 150- name: Basic example to provision from image 151 ovirt: 152 user: admin@internal 153 url: https://ovirt.example.com 154 instance_name: ansiblevm04 155 password: secret 156 image: centos_64 157 zone: cluster01 158 resource_type: template 159 160- name: Full example to create new instance from scratch 161 ovirt: 162 instance_name: testansible 163 resource_type: new 164 instance_type: server 165 user: admin@internal 166 password: secret 167 url: https://ovirt.example.com 168 instance_disksize: 10 169 zone: cluster01 170 region: datacenter1 171 instance_cpus: 1 172 instance_nic: nic1 173 instance_network: rhevm 174 instance_mem: 1000 175 disk_alloc: thin 176 sdomain: FIBER01 177 instance_cores: 1 178 instance_os: rhel_6x64 179 disk_int: virtio 180 181- name: Stopping an existing instance 182 ovirt: 183 instance_name: testansible 184 state: stopped 185 user: admin@internal 186 password: secret 187 url: https://ovirt.example.com 188 189- name: Start an existing instance 190 ovirt: 191 instance_name: testansible 192 state: started 193 user: admin@internal 194 password: secret 195 url: https://ovirt.example.com 196 197- name: Start an instance with cloud init information 198 ovirt: 199 instance_name: testansible 200 state: started 201 user: admin@internal 202 password: secret 203 url: https://ovirt.example.com 204 hostname: testansible 205 domain: ansible.local 206 ip: 192.0.2.100 207 netmask: 255.255.255.0 208 gateway: 192.0.2.1 209 rootpw: bigsecret 210''' 211 212import time 213 214try: 215 from ovirtsdk.api import API 216 from ovirtsdk.xml import params 217 HAS_OVIRTSDK = True 218except ImportError: 219 HAS_OVIRTSDK = False 220 221from ansible.module_utils.basic import AnsibleModule 222 223 224# ------------------------------------------------------------------- # 225# create connection with API 226# 227def conn(url, user, password): 228 api = API(url=url, username=user, password=password, insecure=True) 229 try: 230 value = api.test() 231 except Exception: 232 raise Exception("error connecting to the oVirt API") 233 return api 234 235 236# ------------------------------------------------------------------- # 237# Create VM from scratch 238def create_vm(conn, vmtype, vmname, zone, vmdisk_size, vmcpus, vmnic, vmnetwork, vmmem, vmdisk_alloc, sdomain, vmcores, vmos, vmdisk_int): 239 if vmdisk_alloc == 'thin': 240 # define VM params 241 vmparams = params.VM(name=vmname, cluster=conn.clusters.get(name=zone), os=params.OperatingSystem(type_=vmos), 242 template=conn.templates.get(name="Blank"), memory=1024 * 1024 * int(vmmem), 243 cpu=params.CPU(topology=params.CpuTopology(cores=int(vmcores))), type_=vmtype) 244 # define disk params 245 vmdisk = params.Disk(size=1024 * 1024 * 1024 * int(vmdisk_size), wipe_after_delete=True, sparse=True, interface=vmdisk_int, type_="System", 246 format='cow', 247 storage_domains=params.StorageDomains(storage_domain=[conn.storagedomains.get(name=sdomain)])) 248 # define network parameters 249 network_net = params.Network(name=vmnetwork) 250 nic_net1 = params.NIC(name='nic1', network=network_net, interface='virtio') 251 elif vmdisk_alloc == 'preallocated': 252 # define VM params 253 vmparams = params.VM(name=vmname, cluster=conn.clusters.get(name=zone), os=params.OperatingSystem(type_=vmos), 254 template=conn.templates.get(name="Blank"), memory=1024 * 1024 * int(vmmem), 255 cpu=params.CPU(topology=params.CpuTopology(cores=int(vmcores))), type_=vmtype) 256 # define disk params 257 vmdisk = params.Disk(size=1024 * 1024 * 1024 * int(vmdisk_size), wipe_after_delete=True, sparse=False, interface=vmdisk_int, type_="System", 258 format='raw', storage_domains=params.StorageDomains(storage_domain=[conn.storagedomains.get(name=sdomain)])) 259 # define network parameters 260 network_net = params.Network(name=vmnetwork) 261 nic_net1 = params.NIC(name=vmnic, network=network_net, interface='virtio') 262 263 try: 264 conn.vms.add(vmparams) 265 except Exception: 266 raise Exception("Error creating VM with specified parameters") 267 vm = conn.vms.get(name=vmname) 268 try: 269 vm.disks.add(vmdisk) 270 except Exception: 271 raise Exception("Error attaching disk") 272 try: 273 vm.nics.add(nic_net1) 274 except Exception: 275 raise Exception("Error adding nic") 276 277 278# create an instance from a template 279def create_vm_template(conn, vmname, image, zone): 280 vmparams = params.VM(name=vmname, cluster=conn.clusters.get(name=zone), template=conn.templates.get(name=image), disks=params.Disks(clone=True)) 281 try: 282 conn.vms.add(vmparams) 283 except Exception: 284 raise Exception('error adding template %s' % image) 285 286 287# start instance 288def vm_start(conn, vmname, hostname=None, ip=None, netmask=None, gateway=None, 289 domain=None, dns=None, rootpw=None, key=None): 290 vm = conn.vms.get(name=vmname) 291 use_cloud_init = False 292 nics = None 293 nic = None 294 if hostname or ip or netmask or gateway or domain or dns or rootpw or key: 295 use_cloud_init = True 296 if ip and netmask and gateway: 297 ipinfo = params.IP(address=ip, netmask=netmask, gateway=gateway) 298 nic = params.GuestNicConfiguration(name='eth0', boot_protocol='STATIC', ip=ipinfo, on_boot=True) 299 nics = params.Nics() 300 nics = params.GuestNicsConfiguration(nic_configuration=[nic]) 301 initialization = params.Initialization(regenerate_ssh_keys=True, host_name=hostname, domain=domain, user_name='root', 302 root_password=rootpw, nic_configurations=nics, dns_servers=dns, 303 authorized_ssh_keys=key) 304 action = params.Action(use_cloud_init=use_cloud_init, vm=params.VM(initialization=initialization)) 305 vm.start(action=action) 306 307 308# Stop instance 309def vm_stop(conn, vmname): 310 vm = conn.vms.get(name=vmname) 311 vm.stop() 312 313 314# restart instance 315def vm_restart(conn, vmname): 316 state = vm_status(conn, vmname) 317 vm = conn.vms.get(name=vmname) 318 vm.stop() 319 while conn.vms.get(vmname).get_status().get_state() != 'down': 320 time.sleep(5) 321 vm.start() 322 323 324# remove an instance 325def vm_remove(conn, vmname): 326 vm = conn.vms.get(name=vmname) 327 vm.delete() 328 329 330# ------------------------------------------------------------------- # 331# VM statuses 332# 333# Get the VMs status 334def vm_status(conn, vmname): 335 status = conn.vms.get(name=vmname).status.state 336 return status 337 338 339# Get VM object and return it's name if object exists 340def get_vm(conn, vmname): 341 vm = conn.vms.get(name=vmname) 342 if vm is None: 343 name = "empty" 344 else: 345 name = vm.get_name() 346 return name 347 348# ------------------------------------------------------------------- # 349# Hypervisor operations 350# 351# not available yet 352# ------------------------------------------------------------------- # 353# Main 354 355 356def main(): 357 module = AnsibleModule( 358 argument_spec=dict( 359 state=dict(type='str', default='present', choices=['absent', 'present', 'restart', 'shutdown', 'started']), 360 user=dict(type='str', required=True), 361 url=dict(type='str', required=True), 362 instance_name=dict(type='str', required=True, aliases=['vmname']), 363 password=dict(type='str', required=True, no_log=True), 364 image=dict(type='str'), 365 resource_type=dict(type='str', choices=['new', 'template']), 366 zone=dict(type='str'), 367 instance_disksize=dict(type='str', aliases=['vm_disksize']), 368 instance_cpus=dict(type='str', default=1, aliases=['vmcpus']), 369 instance_nic=dict(type='str', aliases=['vmnic']), 370 instance_network=dict(type='str', default='rhevm', aliases=['vmnetwork']), 371 instance_mem=dict(type='str', aliases=['vmmem']), 372 instance_type=dict(type='str', default='server', aliases=['vmtype'], choices=['desktop', 'server', 'high_performance']), 373 disk_alloc=dict(type='str', default='thin', choices=['preallocated', 'thin']), 374 disk_int=dict(type='str', default='virtio', choices=['ide', 'virtio']), 375 instance_os=dict(type='str', aliases=['vmos']), 376 instance_cores=dict(type='str', default=1, aliases=['vmcores']), 377 instance_hostname=dict(type='str', aliases=['hostname']), 378 instance_ip=dict(type='str', aliases=['ip']), 379 instance_netmask=dict(type='str', aliases=['netmask']), 380 instance_gateway=dict(type='str', aliases=['gateway']), 381 instance_domain=dict(type='str', aliases=['domain']), 382 instance_dns=dict(type='str', aliases=['dns']), 383 instance_rootpw=dict(type='str', aliases=['rootpw'], no_log=True), 384 instance_key=dict(type='str', aliases=['key']), 385 sdomain=dict(type='str'), 386 region=dict(type='str'), 387 ), 388 ) 389 390 if not HAS_OVIRTSDK: 391 module.fail_json(msg='ovirtsdk required for this module') 392 393 state = module.params['state'] 394 user = module.params['user'] 395 url = module.params['url'] 396 vmname = module.params['instance_name'] 397 password = module.params['password'] 398 image = module.params['image'] # name of the image to deploy 399 resource_type = module.params['resource_type'] # template or from scratch 400 zone = module.params['zone'] # oVirt cluster 401 vmdisk_size = module.params['instance_disksize'] # disksize 402 vmcpus = module.params['instance_cpus'] # number of cpu 403 vmnic = module.params['instance_nic'] # network interface 404 vmnetwork = module.params['instance_network'] # logical network 405 vmmem = module.params['instance_mem'] # mem size 406 vmdisk_alloc = module.params['disk_alloc'] # thin, preallocated 407 vmdisk_int = module.params['disk_int'] # disk interface virtio or ide 408 vmos = module.params['instance_os'] # Operating System 409 vmtype = module.params['instance_type'] # server, desktop or high_performance 410 vmcores = module.params['instance_cores'] # number of cores 411 sdomain = module.params['sdomain'] # storage domain to store disk on 412 region = module.params['region'] # oVirt Datacenter 413 hostname = module.params['instance_hostname'] 414 ip = module.params['instance_ip'] 415 netmask = module.params['instance_netmask'] 416 gateway = module.params['instance_gateway'] 417 domain = module.params['instance_domain'] 418 dns = module.params['instance_dns'] 419 rootpw = module.params['instance_rootpw'] 420 key = module.params['instance_key'] 421 # initialize connection 422 try: 423 c = conn(url + "/api", user, password) 424 except Exception as e: 425 module.fail_json(msg='%s' % e) 426 427 if state == 'present': 428 if get_vm(c, vmname) == "empty": 429 if resource_type == 'template': 430 try: 431 create_vm_template(c, vmname, image, zone) 432 except Exception as e: 433 module.fail_json(msg='%s' % e) 434 module.exit_json(changed=True, msg="deployed VM %s from template %s" % (vmname, image)) 435 elif resource_type == 'new': 436 # FIXME: refactor, use keyword args. 437 try: 438 create_vm(c, vmtype, vmname, zone, vmdisk_size, vmcpus, vmnic, vmnetwork, vmmem, vmdisk_alloc, sdomain, vmcores, vmos, vmdisk_int) 439 except Exception as e: 440 module.fail_json(msg='%s' % e) 441 module.exit_json(changed=True, msg="deployed VM %s from scratch" % vmname) 442 else: 443 module.exit_json(changed=False, msg="You did not specify a resource type") 444 else: 445 module.exit_json(changed=False, msg="VM %s already exists" % vmname) 446 447 if state == 'started': 448 if vm_status(c, vmname) == 'up': 449 module.exit_json(changed=False, msg="VM %s is already running" % vmname) 450 else: 451 # vm_start(c, vmname) 452 vm_start(c, vmname, hostname, ip, netmask, gateway, domain, dns, rootpw, key) 453 module.exit_json(changed=True, msg="VM %s started" % vmname) 454 455 if state == 'shutdown': 456 if vm_status(c, vmname) == 'down': 457 module.exit_json(changed=False, msg="VM %s is already shutdown" % vmname) 458 else: 459 vm_stop(c, vmname) 460 module.exit_json(changed=True, msg="VM %s is shutting down" % vmname) 461 462 if state == 'restart': 463 if vm_status(c, vmname) == 'up': 464 vm_restart(c, vmname) 465 module.exit_json(changed=True, msg="VM %s is restarted" % vmname) 466 else: 467 module.exit_json(changed=False, msg="VM %s is not running" % vmname) 468 469 if state == 'absent': 470 if get_vm(c, vmname) == "empty": 471 module.exit_json(changed=False, msg="VM %s does not exist" % vmname) 472 else: 473 vm_remove(c, vmname) 474 module.exit_json(changed=True, msg="VM %s removed" % vmname) 475 476 477if __name__ == '__main__': 478 main() 479