2018-03-28 21:32:04 +00:00
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Make coding more python3-ish
from __future__ import ( absolute_import , division , print_function )
__metaclass__ = type
"""
( c ) 2017 , Milan Ilic < milani @nordeus.com >
This file is part of Ansible
Ansible is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Ansible is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Ansible . If not , see < http : / / www . gnu . org / licenses / > .
"""
ANSIBLE_METADATA = { ' status ' : [ ' preview ' ] ,
' supported_by ' : ' community ' ,
' metadata_version ' : ' 1.1 ' }
DOCUMENTATION = '''
- - -
module : one_vm
short_description : Creates or terminates OpenNebula instances
description :
- Manages OpenNebula instances
version_added : " 2.6 "
requirements :
- python - oca
options :
api_url :
description :
- URL of the OpenNebula RPC server .
- It is recommended to use HTTPS so that the username / password are not
- transferred over the network unencrypted .
- If not set then the value of the C ( ONE_URL ) environment variable is used .
api_username :
description :
- Name of the user to login into the OpenNebula RPC server . If not set
- then the value of the C ( ONE_USERNAME ) environment variable is used .
api_password :
description :
- Password of the user to login into OpenNebula RPC server . If not set
template_name :
description :
- Name of VM template to use to create a new instace
template_id :
description :
- ID of a VM template to use to create a new instance
instance_ids :
description :
- A list of instance ids used for states ' : ' C ( absent ) , C ( running ) , C ( rebooted ) , C ( poweredoff )
aliases : [ ' ids ' ]
state :
description :
- C ( present ) - create instances from a template specified with C ( template_id ) / C ( template_name ) .
- C ( running ) - run instances
- C ( poweredoff ) - power - off instances
- C ( rebooted ) - reboot instances
- C ( absent ) - terminate instances
choices : [ " present " , " absent " , " running " , " rebooted " , " poweredoff " ]
default : present
hard :
description :
- Reboot , power - off or terminate instances C ( hard )
default : no
type : bool
wait :
description :
- Wait for the instance to reach its desired state before returning . Keep
- in mind if you are waiting for instance to be in running state it
- doesn ' t mean that you will be able to SSH on that machine only that
- boot process have started on that instance , see ' wait_for ' example for
- details .
default : yes
type : bool
wait_timeout :
description :
- How long before wait gives up , in seconds
default : 300
attributes :
description :
- A dictionary of key / value attributes to add to new instances , or for
- setting C ( state ) of instances with these attributes .
- Keys are case insensitive and OpenNebula automatically converts them to upper case .
- Be aware C ( NAME ) is a special attribute which sets the name of the VM when it ' s deployed.
- C ( #) character(s) can be appended to the C(NAME) and the module will automatically add
- indexes to the names of VMs .
- For example ' : ' C ( NAME ' : ' foo - ###) would create VMs with names C(foo-000), C(foo-001),...
- When used with C ( count_attributes ) and C ( exact_count ) the module will
- match the base name without the index part .
default : { }
labels :
description :
- A list of labels to associate with new instances , or for setting
- C ( state ) of instances with these labels .
default : [ ]
count_attributes :
description :
- A dictionary of key / value attributes that can only be used with
- C ( exact_count ) to determine how many nodes based on a specific
- attributes criteria should be deployed . This can be expressed in
- multiple ways and is shown in the EXAMPLES section .
count_labels :
description :
- A list of labels that can only be used with C ( exact_count ) to determine
- how many nodes based on a specific labels criteria should be deployed .
- This can be expressed in multiple ways and is shown in the EXAMPLES
- section .
count :
description :
- Number of instances to launch
default : 1
exact_count :
description :
- Indicates how many instances that match C ( count_attributes ) and
- C ( count_labels ) parameters should be deployed . Instances are either
- created or terminated based on this value .
- NOTE ' : ' Instances with the least IDs will be terminated first .
2018-05-25 06:00:26 +00:00
mode :
description :
- Set permission mode of the instance in octet format , e . g . C ( 600 ) to give owner C ( use ) and C ( manage ) and nothing to group and others .
owner_id :
description :
- ID of the user which will be set as the owner of the instance
group_id :
description :
- ID of the group which will be set as the group of the instance
2018-03-28 21:32:04 +00:00
memory :
description :
- The size of the memory for new instances ( in MB , GB , . . . )
disk_size :
description :
- The size of the disk created for new instances ( in MB , GB , TB , . . . ) .
- NOTE ' : ' This option can be used only if the VM template specified with
- C ( template_id ) / C ( template_name ) has exactly one disk .
cpu :
description :
- Percentage of CPU divided by 100 required for the new instance . Half a
- processor is written 0.5 .
vcpu :
description :
- Number of CPUs ( cores ) new VM will have .
networks :
description :
- A list of dictionaries with network parameters . See examples for more details .
default : [ ]
disk_saveas :
description :
- Creates an image from a VM disk .
- It is a dictionary where you have to specife C ( name ) of the new image .
- Optionally you can specife C ( disk_id ) of the disk you want to save . By default C ( disk_id ) is 0.
- I ( NOTE ) ' : ' This operation will only be performed on the first VM ( if more than one VM ID is passed )
- and the VM has to be in the C ( poweredoff ) state .
- Also this operation will fail if an image with specified C ( name ) already exists .
author :
- " Milan Ilic (@ilicmilan) "
'''
EXAMPLES = '''
# Create a new instance
- one_vm :
template_id : 90
register : result
# Print VM properties
- debug :
msg : result
# Deploy a new VM and set its name to 'foo'
- one_vm :
template_name : ' app1_template '
attributes :
name : foo
2018-05-25 06:00:26 +00:00
# Deploy a new VM and set its group_id and mode
- one_vm :
template_id : 90
group_id : 16
mode : 660
# Change VM's permissions to 640
- one_vm :
instance_ids : 5
mode : 640
2018-03-28 21:32:04 +00:00
# Deploy 2 new instances and set memory, vcpu, disk_size and 3 networks
- one_vm :
template_id : 15
disk_size : 35.2 GB
memory : 4 GB
vcpu : 4
count : 2
networks :
- NETWORK_ID : 27
- NETWORK : " default-network "
NETWORK_UNAME : " app-user "
SECURITY_GROUPS : " 120,124 "
- NETWORK_ID : 27
SECURITY_GROUPS : " 10 "
# Deploy an new instance with attribute 'bar: bar1' and set its name to 'foo'
- one_vm :
template_id : 53
attributes :
name : foo
bar : bar1
# Enforce that 2 instances with attributes 'foo1: app1' and 'foo2: app2' are deployed
- one_vm :
template_id : 53
attributes :
foo1 : app1
foo2 : app2
exact_count : 2
count_attributes :
foo1 : app1
foo2 : app2
# Enforce that 4 instances with an attribute 'bar' are deployed
- one_vm :
template_id : 53
attributes :
name : app
bar : bar2
exact_count : 4
count_attributes :
bar :
# Deploy 2 new instances with attribute 'foo: bar' and labels 'app1' and 'app2' and names in format 'fooapp-##'
# Names will be: fooapp-00 and fooapp-01
- one_vm :
template_id : 53
attributes :
name : fooapp - ##
foo : bar
labels :
- app1
- app2
count : 2
# Deploy 2 new instances with attribute 'app: app1' and names in format 'fooapp-###'
# Names will be: fooapp-002 and fooapp-003
- one_vm :
template_id : 53
attributes :
name : fooapp - ###
app : app1
count : 2
# Reboot all instances with name in format 'fooapp-#'
# Instances 'fooapp-00', 'fooapp-01', 'fooapp-002' and 'fooapp-003' will be rebooted
- one_vm :
attributes :
name : fooapp - #
state : rebooted
# Enforce that only 1 instance with name in format 'fooapp-#' is deployed
# The task will delete oldest instances, so only the 'fooapp-003' will remain
- one_vm :
template_id : 53
exact_count : 1
count_attributes :
name : fooapp - #
# Deploy an new instance with a network
- one_vm :
template_id : 53
networks :
- NETWORK_ID : 27
register : vm
# Wait for SSH to come up
- wait_for_connection :
delegate_to : ' {{ vm.instances[0].networks[0].ip }} '
# Terminate VMs by ids
- one_vm :
instance_ids :
- 153
- 160
state : absent
# Reboot all VMs that have labels 'foo' and 'app1'
- one_vm :
labels :
- foo
- app1
state : rebooted
# Fetch all VMs that have name 'foo' and attribute 'app: bar'
- one_vm :
attributes :
name : foo
app : bar
register : results
# Deploy 2 new instances with labels 'foo1' and 'foo2'
- one_vm :
template_name : app_template
labels :
- foo1
- foo2
count : 2
# Enforce that only 1 instance with label 'foo1' will be running
- one_vm :
template_name : app_template
labels :
- foo1
exact_count : 1
count_labels :
- foo1
# Terminate all instances that have attribute foo
- one_vm :
template_id : 53
exact_count : 0
count_attributes :
foo :
# Power-off the VM and save VM's disk with id=0 to the image with name 'foo-image'
- one_vm :
instance_ids : 351
state : powered - off
disk_saveas :
name : foo - image
# Save VM's disk with id=1 to the image with name 'bar-image'
- one_vm :
instance_ids : 351
disk_saveas :
name : bar - image
disk_id : 1
'''
RETURN = '''
instances_ids :
description : a list of instances ids whose state is changed or which are fetched with C ( instance_ids ) option .
type : list
returned : success
sample : [ 1234 , 1235 ]
instances :
description : a list of instances info whose state is changed or which are fetched with C ( instance_ids ) option .
type : complex
returned : success
contains :
vm_id :
description : vm id
type : integer
sample : 153
vm_name :
description : vm name
type : string
sample : foo
template_id :
description : vm ' s template id
type : integer
sample : 153
group_id :
description : vm ' s group id
type : integer
sample : 1
group_name :
description : vm ' s group name
type : string
sample : one - users
2018-05-25 06:00:26 +00:00
owner_id :
description : vm ' s owner id
2018-03-28 21:32:04 +00:00
type : integer
sample : 143
2018-05-25 06:00:26 +00:00
owner_name :
description : vm ' s owner name
2018-03-28 21:32:04 +00:00
type : string
sample : app - user
2018-05-25 06:00:26 +00:00
mode :
description : vm ' s mode
type : string
returned : success
sample : 660
2018-03-28 21:32:04 +00:00
state :
description : state of an instance
type : string
sample : ACTIVE
lcm_state :
description : lcm state of an instance that is only relevant when the state is ACTIVE
type : string
sample : RUNNING
cpu :
description : Percentage of CPU divided by 100
type : float
sample : 0.2
vcpu :
description : Number of CPUs ( cores )
type : int
sample : 2
memory :
description : The size of the memory in MB
type : string
sample : 4096 MB
disk_size :
description : The size of the disk in MB
type : string
sample : 20480 MB
networks :
description : a list of dictionaries with info about IP , NAME , MAC , SECURITY_GROUPS for each NIC
type : list
sample : [
{
" ip " : " 10.120.5.33 " ,
" mac " : " 02:00:0a:78:05:21 " ,
" name " : " default-test-private " ,
" security_groups " : " 0,10 "
} ,
{
" ip " : " 10.120.5.34 " ,
" mac " : " 02:00:0a:78:05:22 " ,
" name " : " default-test-private " ,
" security_groups " : " 0 "
}
]
uptime_h :
description : Uptime of the instance in hours
type : integer
sample : 35
labels :
description : A list of string labels that are associated with the instance
type : list
sample : [
" foo " ,
" spec-label "
]
attributes :
description : A dictionary of key / values attributes that are associated with the instance
type : dict
sample : {
" HYPERVISOR " : " kvm " ,
" LOGO " : " images/logos/centos.png " ,
" TE_GALAXY " : " bar " ,
" USER_INPUTS " : null
}
tagged_instances :
description :
- A list of instances info based on a specific attributes and / or
- labels that are specified with C ( count_attributes ) and C ( count_labels )
- options .
type : complex
returned : success
contains :
vm_id :
description : vm id
type : integer
sample : 153
vm_name :
description : vm name
type : string
sample : foo
template_id :
description : vm ' s template id
type : integer
sample : 153
group_id :
description : vm ' s group id
type : integer
sample : 1
group_name :
description : vm ' s group name
type : string
sample : one - users
2018-05-25 06:00:26 +00:00
owner_id :
2018-03-28 21:32:04 +00:00
description : vm ' s user id
type : integer
sample : 143
2018-05-25 06:00:26 +00:00
owner_name :
2018-03-28 21:32:04 +00:00
description : vm ' s user name
type : string
sample : app - user
2018-05-25 06:00:26 +00:00
mode :
description : vm ' s mode
type : string
returned : success
sample : 660
2018-03-28 21:32:04 +00:00
state :
description : state of an instance
type : string
sample : ACTIVE
lcm_state :
description : lcm state of an instance that is only relevant when the state is ACTIVE
type : string
sample : RUNNING
cpu :
description : Percentage of CPU divided by 100
type : float
sample : 0.2
vcpu :
description : Number of CPUs ( cores )
type : int
sample : 2
memory :
description : The size of the memory in MB
type : string
sample : 4096 MB
disk_size :
description : The size of the disk in MB
type : string
sample : 20480 MB
networks :
description : a list of dictionaries with info about IP , NAME , MAC , SECURITY_GROUPS for each NIC
type : list
sample : [
{
" ip " : " 10.120.5.33 " ,
" mac " : " 02:00:0a:78:05:21 " ,
" name " : " default-test-private " ,
" security_groups " : " 0,10 "
} ,
{
" ip " : " 10.120.5.34 " ,
" mac " : " 02:00:0a:78:05:22 " ,
" name " : " default-test-private " ,
" security_groups " : " 0 "
}
]
uptime_h :
description : Uptime of the instance in hours
type : integer
sample : 35
labels :
description : A list of string labels that are associated with the instance
type : list
sample : [
" foo " ,
" spec-label "
]
attributes :
description : A dictionary of key / values attributes that are associated with the instance
type : dict
sample : {
" HYPERVISOR " : " kvm " ,
" LOGO " : " images/logos/centos.png " ,
" TE_GALAXY " : " bar " ,
" USER_INPUTS " : null
}
'''
try :
import oca
HAS_OCA = True
except ImportError :
HAS_OCA = False
from ansible . module_utils . basic import AnsibleModule
import os
def get_template ( module , client , predicate ) :
pool = oca . VmTemplatePool ( client )
# Filter -2 means fetch all templates user can Use
pool . info ( filter = - 2 )
found = 0
found_template = None
template_name = ' '
for template in pool :
if predicate ( template ) :
found = found + 1
found_template = template
template_name = template . name
if found == 0 :
return None
elif found > 1 :
module . fail_json ( msg = ' There are more templates with name: ' + template_name )
return found_template
def get_template_by_name ( module , client , template_name ) :
return get_template ( module , client , lambda template : ( template . name == template_name ) )
def get_template_by_id ( module , client , template_id ) :
return get_template ( module , client , lambda template : ( template . id == template_id ) )
def get_template_id ( module , client , requested_id , requested_name ) :
template = get_template_by_id ( module , client , requested_id ) if requested_id else get_template_by_name ( module , client , requested_name )
if template :
return template . id
else :
return None
def get_vm_by_id ( client , vm_id ) :
pool = oca . VirtualMachinePool ( client )
# Retrieves information for all or part of the vms pool
# -4: Vms belonging to the user's primary group
# -3: Vms belonging to the user
# -2: All vms user can Use
# -1: Vms belonging to the user and any of his groups - default
# >= 0: UID User's vms
pool . info ( filter = - 2 )
for vm in pool :
if str ( vm . id ) == str ( vm_id ) :
return vm
return None
def get_vms_by_ids ( module , client , state , ids ) :
vms = [ ]
for vm_id in ids :
vm = get_vm_by_id ( client , vm_id )
if vm is None and state != ' absent ' :
module . fail_json ( msg = ' There is no VM with id= ' + str ( vm_id ) )
vms . append ( vm )
return vms
def get_vm_info ( client , vm ) :
vm . info ( )
networks_info = [ ]
disk_size = ' '
if hasattr ( vm . template , ' disks ' ) :
disk_size = vm . template . disks [ 0 ] . size + ' MB '
if hasattr ( vm . template , ' nics ' ) :
for nic in vm . template . nics :
networks_info . append ( { ' ip ' : nic . ip , ' mac ' : nic . mac , ' name ' : nic . network , ' security_groups ' : nic . security_groups } )
import time
current_time = time . localtime ( )
vm_start_time = time . localtime ( vm . stime )
vm_uptime = time . mktime ( current_time ) - time . mktime ( vm_start_time )
vm_uptime / = ( 60 * 60 )
2018-05-25 06:00:26 +00:00
permissions_str = parse_vm_permissions ( client , vm )
2018-03-28 21:32:04 +00:00
# LCM_STATE is VM's sub-state that is relevant only when STATE is ACTIVE
vm_lcm_state = None
if vm . state == VM_STATES . index ( ' ACTIVE ' ) :
vm_lcm_state = LCM_STATES [ vm . lcm_state ]
vm_labels , vm_attributes = get_vm_labels_and_attributes_dict ( client , vm . id )
info = {
' template_id ' : int ( vm . template . template_id ) ,
' vm_id ' : vm . id ,
' vm_name ' : vm . name ,
' state ' : VM_STATES [ vm . state ] ,
' lcm_state ' : vm_lcm_state ,
2018-05-25 06:00:26 +00:00
' owner_name ' : vm . uname ,
' owner_id ' : vm . uid ,
2018-03-28 21:32:04 +00:00
' networks ' : networks_info ,
' disk_size ' : disk_size ,
' memory ' : vm . template . memory + ' MB ' ,
' vcpu ' : vm . template . vcpu ,
' cpu ' : vm . template . cpu ,
' group_name ' : vm . gname ,
' group_id ' : vm . gid ,
' uptime_h ' : int ( vm_uptime ) ,
' attributes ' : vm_attributes ,
2018-05-25 06:00:26 +00:00
' mode ' : permissions_str ,
2018-03-28 21:32:04 +00:00
' labels ' : vm_labels
}
return info
2018-05-25 06:00:26 +00:00
def parse_vm_permissions ( client , vm ) :
import xml . etree . ElementTree as ET
vm_XML = client . call ( ' vm.info ' , vm . id )
root = ET . fromstring ( vm_XML )
perm_dict = { }
root = root . find ( ' PERMISSIONS ' )
for child in root :
perm_dict [ child . tag ] = child . text
'''
This is the structure of the ' PERMISSIONS ' dictionary :
" PERMISSIONS " : {
" OWNER_U " : " 1 " ,
" OWNER_M " : " 1 " ,
" OWNER_A " : " 0 " ,
" GROUP_U " : " 0 " ,
" GROUP_M " : " 0 " ,
" GROUP_A " : " 0 " ,
" OTHER_U " : " 0 " ,
" OTHER_M " : " 0 " ,
" OTHER_A " : " 0 "
}
'''
owner_octal = int ( perm_dict [ " OWNER_U " ] ) * 4 + int ( perm_dict [ " OWNER_M " ] ) * 2 + int ( perm_dict [ " OWNER_A " ] )
group_octal = int ( perm_dict [ " GROUP_U " ] ) * 4 + int ( perm_dict [ " GROUP_M " ] ) * 2 + int ( perm_dict [ " GROUP_A " ] )
other_octal = int ( perm_dict [ " OTHER_U " ] ) * 4 + int ( perm_dict [ " OTHER_M " ] ) * 2 + int ( perm_dict [ " OTHER_A " ] )
permissions = str ( owner_octal ) + str ( group_octal ) + str ( other_octal )
return permissions
def set_vm_permissions ( module , client , vms , permissions ) :
changed = False
for vm in vms :
vm . info ( )
print ( vm . id )
old_permissions = parse_vm_permissions ( client , vm )
changed = changed or old_permissions != permissions
if not module . check_mode and old_permissions != permissions :
permissions_str = bin ( int ( permissions , base = 8 ) ) [ 2 : ] # 600 -> 110000000
mode_bits = [ int ( d ) for d in permissions_str ]
try :
client . call ( ' vm.chmod ' , vm . id , mode_bits [ 0 ] , mode_bits [ 1 ] , mode_bits [ 2 ] , mode_bits [ 3 ] ,
mode_bits [ 4 ] , mode_bits [ 5 ] , mode_bits [ 6 ] , mode_bits [ 7 ] , mode_bits [ 8 ] )
except oca . OpenNebulaException :
module . fail_json ( msg = " Permissions changing is unsuccessful, but instances are present if you deployed them. " )
return changed
def set_vm_ownership ( module , client , vms , owner_id , group_id ) :
changed = False
for vm in vms :
vm . info ( )
if owner_id is None :
owner_id = vm . uid
if group_id is None :
group_id = vm . gid
changed = changed or owner_id != vm . uid or group_id != vm . gid
if not module . check_mode and ( owner_id != vm . uid or group_id != vm . gid ) :
try :
client . call ( ' vm.chown ' , vm . id , owner_id , group_id )
except oca . OpenNebulaException :
module . fail_json ( msg = " Ownership changing is unsuccessful, but instances are present if you deployed them. " )
return changed
2018-03-28 21:32:04 +00:00
def get_size_in_MB ( module , size_str ) :
SYMBOLS = [ ' B ' , ' KB ' , ' MB ' , ' GB ' , ' TB ' ]
s = size_str
init = size_str
num = " "
while s and s [ 0 : 1 ] . isdigit ( ) or s [ 0 : 1 ] == ' . ' :
num + = s [ 0 ]
s = s [ 1 : ]
num = float ( num )
symbol = s . strip ( )
if symbol not in SYMBOLS :
module . fail_json ( msg = " Cannot interpret %r %r %d " % ( init , symbol , num ) )
prefix = { ' B ' : 1 }
for i , s in enumerate ( SYMBOLS [ 1 : ] ) :
prefix [ s ] = 1 << ( i + 1 ) * 10
size_in_bytes = int ( num * prefix [ symbol ] )
size_in_MB = size_in_bytes / ( 1024 * 1024 )
return size_in_MB
def create_disk_str ( module , client , template_id , disk_size_str ) :
if not disk_size_str :
return ' '
import xml . etree . ElementTree as ET
template_XML = client . call ( ' template.info ' , template_id )
root = ET . fromstring ( template_XML )
disks_num = 0
disk = None
for child in root . find ( ' TEMPLATE ' ) . findall ( ' DISK ' ) :
disks_num + = 1
root = child
if disks_num != 1 :
module . fail_json ( msg = ' You can pass disk_size only if template has exact one disk. This template has ' + str ( disks_num ) + ' disks. ' )
disk = { }
# Get all info about existed disk e.g. IMAGE_ID,...
for child in root :
disk [ child . tag ] = child . text
result = ' DISK = [ ' + ' , ' . join ( ' {key} = " {val} " ' . format ( key = key , val = val ) for key , val in disk . items ( ) if key != ' SIZE ' )
result + = ' , SIZE= ' + str ( get_size_in_MB ( module , disk_size_str ) ) + ' ] \n '
return result
def create_attributes_str ( attributes_dict , labels_list ) :
attributes_str = ' '
if labels_list :
attributes_str + = ' LABELS= " ' + ' , ' . join ( ' {label} ' . format ( label = label ) for label in labels_list ) + ' " \n '
if attributes_dict :
attributes_str + = ' \n ' . join ( ' {key} = " {val} " ' . format ( key = key . upper ( ) , val = val ) for key , val in attributes_dict . items ( ) ) + ' \n '
return attributes_str
def create_nics_str ( network_attrs_list ) :
nics_str = ' '
for network in network_attrs_list :
# Packing key-value dict in string with format key="value", key="value"
network_str = ' , ' . join ( ' {key} = " {val} " ' . format ( key = key , val = val ) for key , val in network . items ( ) )
nics_str = nics_str + ' NIC = [ ' + network_str + ' ] \n '
return nics_str
def create_vm ( module , client , template_id , attributes_dict , labels_list , disk_size , network_attrs_list ) :
if attributes_dict :
vm_name = attributes_dict . get ( ' NAME ' , ' ' )
disk_str = create_disk_str ( module , client , template_id , disk_size )
vm_extra_template_str = create_attributes_str ( attributes_dict , labels_list ) + create_nics_str ( network_attrs_list ) + disk_str
vm_id = client . call ( ' template.instantiate ' , template_id , vm_name , False , vm_extra_template_str )
vm = get_vm_by_id ( client , vm_id )
return get_vm_info ( client , vm )
def generate_next_index ( vm_filled_indexes_list , num_sign_cnt ) :
counter = 0
cnt_str = str ( counter ) . zfill ( num_sign_cnt )
while cnt_str in vm_filled_indexes_list :
counter = counter + 1
cnt_str = str ( counter ) . zfill ( num_sign_cnt )
return cnt_str
def get_vm_labels_and_attributes_dict ( client , vm_id ) :
import xml . etree . ElementTree as ET
vm_XML = client . call ( ' vm.info ' , vm_id )
root = ET . fromstring ( vm_XML )
attrs_dict = { }
labels_list = [ ]
root = root . find ( ' USER_TEMPLATE ' )
for child in root :
if child . tag != ' LABELS ' :
attrs_dict [ child . tag ] = child . text
else :
if child . text is not None :
labels_list = child . text . split ( ' , ' )
return labels_list , attrs_dict
def get_all_vms_by_attributes ( client , attributes_dict , labels_list ) :
pool = oca . VirtualMachinePool ( client )
# Retrieves information for all or part of the vms pool
# -4: Vms belonging to the user's primary group
# -3: Vms belonging to the user
# -2: All vms user can Use
# -1: Vms belonging to the user and any of his groups - default
# >= 0: UID User's vms
pool . info ( filter = - 2 )
vm_list = [ ]
name = ' '
if attributes_dict :
name = attributes_dict . pop ( ' NAME ' , ' ' )
if name != ' ' :
base_name = name [ : len ( name ) - name . count ( ' # ' ) ]
# Check does the name have indexed format
with_hash = name . endswith ( ' # ' )
for vm in pool :
if vm . name . startswith ( base_name ) :
if with_hash and vm . name [ len ( base_name ) : ] . isdigit ( ) :
# If the name has indexed format and after base_name it has only digits it'll be matched
vm_list . append ( vm )
elif not with_hash and vm . name == name :
# If the name is not indexed it has to be same
vm_list . append ( vm )
pool = vm_list
import copy
vm_list = copy . copy ( pool )
for vm in pool :
vm_labels_list , vm_attributes_dict = get_vm_labels_and_attributes_dict ( client , vm . id )
if attributes_dict and len ( attributes_dict ) > 0 :
for key , val in attributes_dict . items ( ) :
if key in vm_attributes_dict :
if val and vm_attributes_dict [ key ] != val and vm in vm_list :
vm_list . remove ( vm )
break
else :
if vm in vm_list :
vm_list . remove ( vm )
break
if labels_list and len ( labels_list ) > 0 :
for label in labels_list :
if label not in vm_labels_list and vm in vm_list :
vm_list . remove ( vm )
break
return vm_list
def create_count_of_vms ( module , client , template_id , count , attributes_dict , labels_list , disk_size , network_attrs_list , wait , wait_timeout ) :
new_vms_list = [ ]
vm_name = ' '
if attributes_dict :
vm_name = attributes_dict . get ( ' NAME ' , ' ' )
if module . check_mode :
2018-05-25 06:00:26 +00:00
return True , [ ] , [ ]
2018-03-28 21:32:04 +00:00
# Create list of used indexes
vm_filled_indexes_list = None
num_sign_cnt = vm_name . count ( ' # ' )
if vm_name != ' ' and num_sign_cnt > 0 :
vm_list = get_all_vms_by_attributes ( client , { ' NAME ' : vm_name } , None )
base_name = vm_name [ : len ( vm_name ) - num_sign_cnt ]
vm_name = base_name
# Make list which contains used indexes in format ['000', '001',...]
vm_filled_indexes_list = list ( ( vm . name [ len ( base_name ) : ] . zfill ( num_sign_cnt ) ) for vm in vm_list )
while count > 0 :
new_vm_name = vm_name
# Create indexed name
if vm_filled_indexes_list is not None :
next_index = generate_next_index ( vm_filled_indexes_list , num_sign_cnt )
vm_filled_indexes_list . append ( next_index )
new_vm_name + = next_index
# Update NAME value in the attributes in case there is index
attributes_dict [ ' NAME ' ] = new_vm_name
new_vm_dict = create_vm ( module , client , template_id , attributes_dict , labels_list , disk_size , network_attrs_list )
new_vm_id = new_vm_dict . get ( ' vm_id ' )
new_vm = get_vm_by_id ( client , new_vm_id )
new_vms_list . append ( new_vm )
count - = 1
if wait :
for vm in new_vms_list :
wait_for_running ( module , vm , wait_timeout )
2018-05-25 06:00:26 +00:00
return True , new_vms_list , [ ]
2018-03-28 21:32:04 +00:00
def create_exact_count_of_vms ( module , client , template_id , exact_count , attributes_dict , count_attributes_dict ,
labels_list , count_labels_list , disk_size , network_attrs_list , hard , wait , wait_timeout ) :
vm_list = get_all_vms_by_attributes ( client , count_attributes_dict , count_labels_list )
vm_count_diff = exact_count - len ( vm_list )
changed = vm_count_diff != 0
new_vms_list = [ ]
2018-05-25 06:00:26 +00:00
instances_list = [ ]
tagged_instances_list = vm_list
2018-03-28 21:32:04 +00:00
if module . check_mode :
2018-05-25 06:00:26 +00:00
return changed , instances_list , tagged_instances_list
2018-03-28 21:32:04 +00:00
if vm_count_diff > 0 :
# Add more VMs
2018-05-25 06:00:26 +00:00
changed , instances_list , tagged_instances = create_count_of_vms ( module , client , template_id , vm_count_diff , attributes_dict ,
labels_list , disk_size , network_attrs_list , wait , wait_timeout )
2018-03-28 21:32:04 +00:00
2018-05-25 06:00:26 +00:00
tagged_instances_list + = instances_list
2018-03-28 21:32:04 +00:00
elif vm_count_diff < 0 :
# Delete surplus VMs
old_vms_list = [ ]
while vm_count_diff < 0 :
old_vm = vm_list . pop ( 0 )
old_vms_list . append ( old_vm )
terminate_vm ( module , client , old_vm , hard )
vm_count_diff + = 1
if wait :
for vm in old_vms_list :
wait_for_done ( module , vm , wait_timeout )
2018-05-25 06:00:26 +00:00
instances_list = old_vms_list
# store only the remaining instances
old_vms_set = set ( old_vms_list )
tagged_instances_list = [ vm for vm in vm_list if vm not in old_vms_set ]
2018-03-28 21:32:04 +00:00
2018-05-25 06:00:26 +00:00
return changed , instances_list , tagged_instances_list
2018-03-28 21:32:04 +00:00
VM_STATES = [ ' INIT ' , ' PENDING ' , ' HOLD ' , ' ACTIVE ' , ' STOPPED ' , ' SUSPENDED ' , ' DONE ' , ' ' , ' POWEROFF ' , ' UNDEPLOYED ' , ' CLONING ' , ' CLONING_FAILURE ' ]
LCM_STATES = [ ' LCM_INIT ' , ' PROLOG ' , ' BOOT ' , ' RUNNING ' , ' MIGRATE ' , ' SAVE_STOP ' ,
' SAVE_SUSPEND ' , ' SAVE_MIGRATE ' , ' PROLOG_MIGRATE ' , ' PROLOG_RESUME ' ,
' EPILOG_STOP ' , ' EPILOG ' , ' SHUTDOWN ' , ' STATE13 ' , ' STATE14 ' , ' CLEANUP_RESUBMIT ' , ' UNKNOWN ' , ' HOTPLUG ' , ' SHUTDOWN_POWEROFF ' ,
' BOOT_UNKNOWN ' , ' BOOT_POWEROFF ' , ' BOOT_SUSPENDED ' , ' BOOT_STOPPED ' , ' CLEANUP_DELETE ' , ' HOTPLUG_SNAPSHOT ' , ' HOTPLUG_NIC ' ,
' HOTPLUG_SAVEAS ' , ' HOTPLUG_SAVEAS_POWEROFF ' , ' HOTPULG_SAVEAS_SUSPENDED ' , ' SHUTDOWN_UNDEPLOY ' ]
def wait_for_state ( module , vm , wait_timeout , state_predicate ) :
import time
start_time = time . time ( )
while ( time . time ( ) - start_time ) < wait_timeout :
vm . info ( )
state = vm . state
lcm_state = vm . lcm_state
if state_predicate ( state , lcm_state ) :
return vm
elif state not in [ VM_STATES . index ( ' INIT ' ) , VM_STATES . index ( ' PENDING ' ) , VM_STATES . index ( ' HOLD ' ) ,
VM_STATES . index ( ' ACTIVE ' ) , VM_STATES . index ( ' POWEROFF ' ) ] :
module . fail_json ( msg = ' Action is unsuccessful. VM state: ' + VM_STATES [ state ] )
time . sleep ( 1 )
module . fail_json ( msg = " Wait timeout has expired! " )
def wait_for_running ( module , vm , wait_timeout ) :
return wait_for_state ( module , vm , wait_timeout , lambda state ,
lcm_state : ( state in [ VM_STATES . index ( ' ACTIVE ' ) ] and lcm_state in [ LCM_STATES . index ( ' RUNNING ' ) ] ) )
def wait_for_done ( module , vm , wait_timeout ) :
return wait_for_state ( module , vm , wait_timeout , lambda state , lcm_state : ( state in [ VM_STATES . index ( ' DONE ' ) ] ) )
def wait_for_poweroff ( module , vm , wait_timeout ) :
return wait_for_state ( module , vm , wait_timeout , lambda state , lcm_state : ( state in [ VM_STATES . index ( ' POWEROFF ' ) ] ) )
def terminate_vm ( module , client , vm , hard = False ) :
changed = False
if not vm :
return changed
changed = True
if not module . check_mode :
if hard :
client . call ( ' vm.action ' , ' terminate-hard ' , vm . id )
else :
client . call ( ' vm.action ' , ' terminate ' , vm . id )
return changed
2018-05-25 06:00:26 +00:00
def terminate_vms ( module , client , vms , hard ) :
2018-03-28 21:32:04 +00:00
changed = False
for vm in vms :
changed = terminate_vm ( module , client , vm , hard ) or changed
2018-05-25 06:00:26 +00:00
return changed
2018-03-28 21:32:04 +00:00
def poweroff_vm ( module , vm , hard ) :
vm . info ( )
changed = False
lcm_state = vm . lcm_state
state = vm . state
if lcm_state not in [ LCM_STATES . index ( ' SHUTDOWN ' ) , LCM_STATES . index ( ' SHUTDOWN_POWEROFF ' ) ] and state not in [ VM_STATES . index ( ' POWEROFF ' ) ] :
changed = True
if changed and not module . check_mode :
if not hard :
vm . poweroff ( )
else :
vm . poweroff_hard ( )
return changed
2018-05-25 06:00:26 +00:00
def poweroff_vms ( module , client , vms , hard ) :
2018-03-28 21:32:04 +00:00
changed = False
for vm in vms :
changed = poweroff_vm ( module , vm , hard ) or changed
2018-05-25 06:00:26 +00:00
return changed
2018-03-28 21:32:04 +00:00
2018-05-25 06:00:26 +00:00
def reboot_vms ( module , client , vms , wait_timeout , hard ) :
2018-03-28 21:32:04 +00:00
if not module . check_mode :
# Firstly, power-off all instances
for vm in vms :
vm . info ( )
lcm_state = vm . lcm_state
state = vm . state
if lcm_state not in [ LCM_STATES . index ( ' SHUTDOWN_POWEROFF ' ) ] and state not in [ VM_STATES . index ( ' POWEROFF ' ) ] :
poweroff_vm ( module , vm , hard )
# Wait for all to be power-off
for vm in vms :
wait_for_poweroff ( module , vm , wait_timeout )
for vm in vms :
resume_vm ( module , vm )
2018-05-25 06:00:26 +00:00
return True
2018-03-28 21:32:04 +00:00
def resume_vm ( module , vm ) :
vm . info ( )
changed = False
lcm_state = vm . lcm_state
if lcm_state == LCM_STATES . index ( ' SHUTDOWN_POWEROFF ' ) :
module . fail_json ( msg = " Cannot perform action ' resume ' because this action is not available " +
" for LCM_STATE: ' SHUTDOWN_POWEROFF ' . Wait for the VM to shutdown properly " )
if lcm_state not in [ LCM_STATES . index ( ' RUNNING ' ) ] :
changed = True
if changed and not module . check_mode :
vm . resume ( )
return changed
2018-05-25 06:00:26 +00:00
def resume_vms ( module , client , vms ) :
2018-03-28 21:32:04 +00:00
changed = False
for vm in vms :
changed = resume_vm ( module , vm ) or changed
2018-05-25 06:00:26 +00:00
return changed
2018-03-28 21:32:04 +00:00
def check_name_attribute ( module , attributes ) :
if attributes . get ( " NAME " ) :
import re
if re . match ( r ' ^[^#]+#*$ ' , attributes . get ( " NAME " ) ) is None :
module . fail_json ( msg = " Ilegal ' NAME ' attribute: ' " + attributes . get ( " NAME " ) +
" ' .Signs ' # ' are allowed only at the end of the name and the name cannot contain only ' # ' . " )
TEMPLATE_RESTRICTED_ATTRIBUTES = [ " CPU " , " VCPU " , " OS " , " FEATURES " , " MEMORY " , " DISK " , " NIC " , " INPUT " , " GRAPHICS " ,
" CONTEXT " , " CREATED_BY " , " CPU_COST " , " DISK_COST " , " MEMORY_COST " ,
" TEMPLATE_ID " , " VMID " , " AUTOMATIC_DS_REQUIREMENTS " , " DEPLOY_FOLDER " , " LABELS " ]
def check_attributes ( module , attributes ) :
for key in attributes . keys ( ) :
if key in TEMPLATE_RESTRICTED_ATTRIBUTES :
module . fail_json ( msg = ' Restricted attribute ` ' + key + ' ` cannot be used when filtering VMs. ' )
# Check the format of the name attribute
check_name_attribute ( module , attributes )
def disk_save_as ( module , client , vm , disk_saveas , wait_timeout ) :
if not disk_saveas . get ( ' name ' ) :
module . fail_json ( msg = " Key ' name ' is required for ' disk_saveas ' option " )
image_name = disk_saveas . get ( ' name ' )
disk_id = disk_saveas . get ( ' disk_id ' , 0 )
if not module . check_mode :
if vm . state != VM_STATES . index ( ' POWEROFF ' ) :
module . fail_json ( msg = " ' disksaveas ' option can be used only when the VM is in ' POWEROFF ' state " )
client . call ( ' vm.disksaveas ' , vm . id , disk_id , image_name , ' OS ' , - 1 )
2018-05-25 06:00:26 +00:00
wait_for_poweroff ( module , vm , wait_timeout ) # wait for VM to leave the hotplug_saveas_poweroff state
2018-03-28 21:32:04 +00:00
def get_connection_info ( module ) :
url = module . params . get ( ' api_url ' )
username = module . params . get ( ' api_username ' )
password = module . params . get ( ' api_password ' )
if not url :
url = os . environ . get ( ' ONE_URL ' )
if not username :
username = os . environ . get ( ' ONE_USERNAME ' )
if not password :
password = os . environ . get ( ' ONE_PASSWORD ' )
if not ( url and username and password ) :
module . fail_json ( msg = " One or more connection parameters (api_url, api_username, api_password) were not specified " )
from collections import namedtuple
auth_params = namedtuple ( ' auth ' , ( ' url ' , ' username ' , ' password ' ) )
return auth_params ( url = url , username = username , password = password )
def main ( ) :
fields = {
" api_url " : { " required " : False , " type " : " str " } ,
" api_username " : { " required " : False , " type " : " str " } ,
" api_password " : { " required " : False , " type " : " str " , " no_log " : True } ,
" instance_ids " : { " required " : False , " aliases " : [ ' ids ' ] , " type " : " list " } ,
" template_name " : { " required " : False , " type " : " str " } ,
" template_id " : { " required " : False , " type " : " int " } ,
" state " : {
" default " : " present " ,
" choices " : [ ' present ' , ' absent ' , ' rebooted ' , ' poweredoff ' , ' running ' ] ,
" type " : " str "
} ,
2018-05-25 06:00:26 +00:00
" mode " : { " required " : False , " type " : " str " } ,
" owner_id " : { " required " : False , " type " : " int " } ,
" group_id " : { " required " : False , " type " : " int " } ,
2018-03-28 21:32:04 +00:00
" wait " : { " default " : True , " type " : " bool " } ,
" wait_timeout " : { " default " : 300 , " type " : " int " } ,
" hard " : { " default " : False , " type " : " bool " } ,
" memory " : { " required " : False , " type " : " str " } ,
" cpu " : { " required " : False , " type " : " float " } ,
" vcpu " : { " required " : False , " type " : " int " } ,
" disk_size " : { " required " : False , " type " : " str " } ,
" networks " : { " default " : [ ] , " type " : " list " } ,
" count " : { " default " : 1 , " type " : " int " } ,
" exact_count " : { " required " : False , " type " : " int " } ,
" attributes " : { " default " : { } , " type " : " dict " } ,
" count_attributes " : { " required " : False , " type " : " dict " } ,
" labels " : { " default " : [ ] , " type " : " list " } ,
" count_labels " : { " required " : False , " type " : " list " } ,
" disk_saveas " : { " type " : " dict " }
}
module = AnsibleModule ( argument_spec = fields ,
mutually_exclusive = [
[ ' template_id ' , ' template_name ' , ' instance_ids ' ] ,
2018-05-25 06:00:26 +00:00
[ ' template_id ' , ' template_name ' , ' disk_saveas ' ] ,
2018-03-28 21:32:04 +00:00
[ ' instance_ids ' , ' count_attributes ' , ' count ' ] ,
[ ' instance_ids ' , ' count_labels ' , ' count ' ] ,
[ ' instance_ids ' , ' exact_count ' ] ,
[ ' instance_ids ' , ' attributes ' ] ,
[ ' instance_ids ' , ' labels ' ] ,
[ ' disk_saveas ' , ' attributes ' ] ,
[ ' disk_saveas ' , ' labels ' ] ,
[ ' exact_count ' , ' count ' ] ,
[ ' count ' , ' hard ' ] ,
[ ' instance_ids ' , ' cpu ' ] , [ ' instance_ids ' , ' vcpu ' ] ,
[ ' instance_ids ' , ' memory ' ] , [ ' instance_ids ' , ' disk_size ' ] ,
[ ' instance_ids ' , ' networks ' ]
] ,
supports_check_mode = True )
if not HAS_OCA :
module . fail_json ( msg = ' This module requires python-oca to work! ' )
auth = get_connection_info ( module )
params = module . params
instance_ids = params . get ( ' instance_ids ' )
requested_template_name = params . get ( ' template_name ' )
requested_template_id = params . get ( ' template_id ' )
state = params . get ( ' state ' )
2018-05-25 06:00:26 +00:00
permissions = params . get ( ' mode ' )
owner_id = params . get ( ' owner_id ' )
group_id = params . get ( ' group_id ' )
2018-03-28 21:32:04 +00:00
wait = params . get ( ' wait ' )
wait_timeout = params . get ( ' wait_timeout ' )
hard = params . get ( ' hard ' )
memory = params . get ( ' memory ' )
cpu = params . get ( ' cpu ' )
vcpu = params . get ( ' vcpu ' )
disk_size = params . get ( ' disk_size ' )
networks = params . get ( ' networks ' )
count = params . get ( ' count ' )
exact_count = params . get ( ' exact_count ' )
attributes = params . get ( ' attributes ' )
count_attributes = params . get ( ' count_attributes ' )
labels = params . get ( ' labels ' )
count_labels = params . get ( ' count_labels ' )
disk_saveas = params . get ( ' disk_saveas ' )
client = oca . Client ( auth . username + ' : ' + auth . password , auth . url )
if attributes :
attributes = dict ( ( key . upper ( ) , value ) for key , value in attributes . items ( ) )
check_attributes ( module , attributes )
if count_attributes :
count_attributes = dict ( ( key . upper ( ) , value ) for key , value in count_attributes . items ( ) )
if not attributes :
import copy
module . warn ( ' When you pass `count_attributes` without `attributes` option when deploying, `attributes` option will have same values implicitly. ' )
attributes = copy . copy ( count_attributes )
check_attributes ( module , count_attributes )
if count_labels and not labels :
module . warn ( ' When you pass `count_labels` without `labels` option when deploying, `labels` option will have same values implicitly. ' )
labels = count_labels
# Fetch template
template_id = None
if requested_template_id or requested_template_name :
template_id = get_template_id ( module , client , requested_template_id , requested_template_name )
if not template_id :
if requested_template_id :
module . fail_json ( msg = ' There is no template with template_id: ' + str ( requested_template_id ) )
elif requested_template_name :
module . fail_json ( msg = " There is no template with name: " + requested_template_name )
if exact_count and not template_id :
module . fail_json ( msg = ' Option `exact_count` needs template_id or template_name ' )
if exact_count is not None and not ( count_attributes or count_labels ) :
module . fail_json ( msg = ' Either `count_attributes` or `count_labels` has to be specified with option `exact_count`. ' )
if ( count_attributes or count_labels ) and exact_count is None :
module . fail_json ( msg = ' Option `exact_count` has to be specified when either `count_attributes` or `count_labels` is used. ' )
if template_id and state != ' present ' :
module . fail_json ( msg = " Only state ' present ' is valid for the template " )
if memory :
attributes [ ' MEMORY ' ] = str ( int ( get_size_in_MB ( module , memory ) ) )
if cpu :
attributes [ ' CPU ' ] = str ( cpu )
if vcpu :
attributes [ ' VCPU ' ] = str ( vcpu )
if exact_count is not None and state != ' present ' :
module . fail_json ( msg = ' The `exact_count` option is valid only for the `present` state ' )
if exact_count is not None and exact_count < 0 :
module . fail_json ( msg = ' `exact_count` cannot be less than 0 ' )
if count < = 0 :
module . fail_json ( msg = ' `count` has to be grater than 0 ' )
2018-05-25 06:00:26 +00:00
if permissions is not None :
import re
if re . match ( " ^[0-7] {3} $ " , permissions ) is None :
module . fail_json ( msg = " Option `mode` has to have exactly 3 digits and be in the octet format e.g. 600 " )
2018-03-28 21:32:04 +00:00
if exact_count is not None :
# Deploy an exact count of VMs
2018-05-25 06:00:26 +00:00
changed , instances_list , tagged_instances_list = create_exact_count_of_vms ( module , client , template_id , exact_count , attributes ,
count_attributes , labels , count_labels , disk_size ,
networks , hard , wait , wait_timeout )
vms = tagged_instances_list
2018-03-28 21:32:04 +00:00
elif template_id and state == ' present ' :
# Deploy count VMs
2018-05-25 06:00:26 +00:00
changed , instances_list , tagged_instances_list = create_count_of_vms ( module , client , template_id , count ,
attributes , labels , disk_size , networks , wait , wait_timeout )
# instances_list - new instances
# tagged_instances_list - all instances with specified `count_attributes` and `count_labels`
vms = instances_list
2018-03-28 21:32:04 +00:00
else :
# Fetch data of instances, or change their state
if not ( instance_ids or attributes or labels ) :
module . fail_json ( msg = " At least one of `instance_ids`,`attributes`,`labels` must be passed! " )
if memory or cpu or vcpu or disk_size or networks :
module . fail_json ( msg = " Parameters as `memory`, `cpu`, `vcpu`, `disk_size` and `networks` you can only set when deploying a VM! " )
if hard and state not in [ ' rebooted ' , ' poweredoff ' , ' absent ' , ' present ' ] :
module . fail_json ( msg = " The ' hard ' option can be used only for one of these states: ' rebooted ' , ' poweredoff ' , ' absent ' and ' present ' " )
2018-05-25 06:00:26 +00:00
vms = [ ]
2018-03-28 21:32:04 +00:00
tagged = False
2018-05-25 06:00:26 +00:00
changed = False
2018-03-28 21:32:04 +00:00
if instance_ids :
vms = get_vms_by_ids ( module , client , state , instance_ids )
else :
tagged = True
vms = get_all_vms_by_attributes ( client , attributes , labels )
if len ( vms ) == 0 and state != ' absent ' and state != ' present ' :
module . fail_json ( msg = ' There are no instances with specified `instance_ids`, `attributes` and/or `labels` ' )
if len ( vms ) == 0 and state == ' present ' and not tagged :
module . fail_json ( msg = ' There are no instances with specified `instance_ids`. ' )
2018-05-25 06:00:26 +00:00
if tagged and state == ' absent ' :
module . fail_json ( msg = ' Option `instance_ids` is required when state is `absent`. ' )
2018-03-28 21:32:04 +00:00
if state == ' absent ' :
2018-05-25 06:00:26 +00:00
changed = terminate_vms ( module , client , vms , hard )
2018-03-28 21:32:04 +00:00
elif state == ' rebooted ' :
2018-05-25 06:00:26 +00:00
changed = reboot_vms ( module , client , vms , wait_timeout , hard )
2018-03-28 21:32:04 +00:00
elif state == ' poweredoff ' :
2018-05-25 06:00:26 +00:00
changed = poweroff_vms ( module , client , vms , hard )
2018-03-28 21:32:04 +00:00
elif state == ' running ' :
2018-05-25 06:00:26 +00:00
changed = resume_vms ( module , client , vms )
instances_list = vms
tagged_instances_list = [ ]
if permissions is not None :
changed = set_vm_permissions ( module , client , vms , permissions ) or changed
if owner_id is not None or group_id is not None :
changed = set_vm_ownership ( module , client , vms , owner_id , group_id ) or changed
if wait and not module . check_mode and state != ' present ' :
wait_for = {
' absent ' : wait_for_done ,
' rebooted ' : wait_for_running ,
' poweredoff ' : wait_for_poweroff ,
' running ' : wait_for_running
}
for vm in vms :
if vm is not None :
wait_for [ state ] ( module , vm , wait_timeout )
if disk_saveas is not None :
if len ( vms ) == 0 :
module . fail_json ( msg = " There is no VM whose disk will be saved. " )
disk_save_as ( module , client , vms [ 0 ] , disk_saveas , wait_timeout )
changed = True
# instances - a list of instances info whose state is changed or which are fetched with C(instance_ids) option
instances = list ( get_vm_info ( client , vm ) for vm in instances_list if vm is not None )
instances_ids = list ( vm . id for vm in instances_list if vm is not None )
# tagged_instances - A list of instances info based on a specific attributes and/or labels that are specified with C(count_attributes) and C(count_labels)
tagged_instances = list ( get_vm_info ( client , vm ) for vm in tagged_instances_list if vm is not None )
2018-03-28 21:32:04 +00:00
2018-05-25 06:00:26 +00:00
result = { ' changed ' : changed , ' instances ' : instances , ' instances_ids ' : instances_ids , ' tagged_instances ' : tagged_instances }
2018-03-28 21:32:04 +00:00
module . exit_json ( * * result )
if __name__ == ' __main__ ' :
main ( )