1#!/usr/bin/python 2# Copyright: (c) 2015, Ansible, Inc. 3# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 4 5from __future__ import absolute_import, division, print_function 6__metaclass__ = type 7 8ANSIBLE_METADATA = { 9 'metadata_version': '1.1', 10 'status': ['preview'], 11 'supported_by': 'community' 12} 13 14DOCUMENTATION = ''' 15--- 16module: vca_vapp 17short_description: Manages vCloud Air vApp instances. 18description: 19 - This module will actively managed vCloud Air vApp instances. Instances 20 can be created and deleted as well as both deployed and undeployed. 21version_added: "2.0" 22author: 23- Peter Sprygada (@privateip) 24notes: 25- VMware sold their vCloud Air service in Q2 2017. 26- VMware made significant changes to the pyvcloud interface around this time. The C(vca_vapp) module relies on now deprecated code. 27- Mileage with C(vca_vapp) may vary as vCloud Director APIs advance. 28- A viable alternative maybe U(https://github.com/vmware/ansible-module-vcloud-director) 29requirements: 30- pyvcloud <= 18.2.2 31options: 32 vapp_name: 33 description: 34 - The name of the vCloud Air vApp instance 35 required: yes 36 template_name: 37 description: 38 - The name of the vApp template to use to create the vApp instance. If 39 the I(state) is not `absent` then the I(template_name) value must be 40 provided. The I(template_name) must be previously uploaded to the 41 catalog specified by I(catalog_name) 42 network_name: 43 description: 44 - The name of the network that should be attached to the virtual machine 45 in the vApp. The virtual network specified must already be created in 46 the vCloud Air VDC. If the I(state) is not 'absent' then the 47 I(network_name) argument must be provided. 48 network_mode: 49 description: 50 - Configures the mode of the network connection. 51 default: pool 52 choices: ['pool', 'dhcp', 'static'] 53 vm_name: 54 description: 55 - The name of the virtual machine instance in the vApp to manage. 56 vm_cpus: 57 description: 58 - The number of vCPUs to configure for the VM in the vApp. If the 59 I(vm_name) argument is provided, then this becomes a per VM setting 60 otherwise it is applied to all VMs in the vApp. 61 vm_memory: 62 description: 63 - The amount of memory in MB to allocate to VMs in the vApp. If the 64 I(vm_name) argument is provided, then this becomes a per VM setting 65 otherwise it is applied to all VMs in the vApp. 66 operation: 67 description: 68 - Specifies an operation to be performed on the vApp. 69 default: noop 70 choices: ['noop', 'poweron', 'poweroff', 'suspend', 'shutdown', 'reboot', 'reset'] 71 state: 72 description: 73 - Configures the state of the vApp. 74 default: present 75 choices: ['present', 'absent', 'deployed', 'undeployed'] 76 username: 77 description: 78 - The vCloud Air username to use during authentication 79 password: 80 description: 81 - The vCloud Air password to use during authentication 82 org: 83 description: 84 - The org to login to for creating vapp, mostly set when the service_type is vdc. 85 instance_id: 86 description: 87 - The instance id in a vchs environment to be used for creating the vapp 88 host: 89 description: 90 - The authentication host to be used when service type is vcd. 91 api_version: 92 description: 93 - The api version to be used with the vca 94 default: "5.7" 95 service_type: 96 description: 97 - The type of service we are authenticating against 98 default: vca 99 choices: [ "vca", "vchs", "vcd" ] 100 vdc_name: 101 description: 102 - The name of the virtual data center (VDC) where the vm should be created or contains the vAPP. 103extends_documentation_fragment: vca 104''' 105 106EXAMPLES = ''' 107- name: Creates a new vApp in a VCA instance 108 vca_vapp: 109 vapp_name: tower 110 state: present 111 template_name: 'Ubuntu Server 12.04 LTS (amd64 20150127)' 112 vdc_name: VDC1 113 instance_id: '<your instance id here>' 114 username: '<your username here>' 115 password: '<your password here>' 116 delegate_to: localhost 117''' 118 119from ansible.module_utils.vca import VcaAnsibleModule, VcaError 120 121DEFAULT_VAPP_OPERATION = 'noop' 122 123VAPP_STATUS = { 124 'Powered off': 'poweroff', 125 'Powered on': 'poweron', 126 'Suspended': 'suspend' 127} 128 129VAPP_STATES = ['present', 'absent', 'deployed', 'undeployed'] 130VAPP_OPERATIONS = ['poweron', 'poweroff', 'suspend', 'shutdown', 131 'reboot', 'reset', 'noop'] 132 133 134def get_instance(module): 135 vapp_name = module.params['vapp_name'] 136 inst = dict(vapp_name=vapp_name, state='absent') 137 try: 138 vapp = module.get_vapp(vapp_name) 139 if vapp: 140 status = module.vca.get_status(vapp.me.get_status()) 141 inst['status'] = VAPP_STATUS.get(status, 'unknown') 142 inst['state'] = 'deployed' if vapp.me.deployed else 'undeployed' 143 return inst 144 except VcaError: 145 return inst 146 147 148def create(module): 149 vdc_name = module.params['vdc_name'] 150 vapp_name = module.params['vapp_name'] 151 template_name = module.params['template_name'] 152 catalog_name = module.params['catalog_name'] 153 network_name = module.params['network_name'] 154 network_mode = module.params['network_mode'] 155 vm_name = module.params['vm_name'] 156 vm_cpus = module.params['vm_cpus'] 157 vm_memory = module.params['vm_memory'] 158 deploy = module.params['state'] == 'deploy' 159 poweron = module.params['operation'] == 'poweron' 160 161 task = module.vca.create_vapp(vdc_name, vapp_name, template_name, 162 catalog_name, network_name, 'bridged', 163 vm_name, vm_cpus, vm_memory, deploy, poweron) 164 165 if task is False: 166 module.fail('Failed to create vapp: %s' % vapp_name) 167 168 module.vca.block_until_completed(task) 169 170 # Connect the network to the Vapp/VM and return assigned IP 171 if network_name is not None: 172 vm_ip = connect_to_network(module, vdc_name, vapp_name, network_name, network_mode) 173 return vm_ip 174 175 176def delete(module): 177 vdc_name = module.params['vdc_name'] 178 vapp_name = module.params['vapp_name'] 179 module.vca.delete_vapp(vdc_name, vapp_name) 180 181 182def do_operation(module): 183 vapp_name = module.params['vapp_name'] 184 operation = module.params['operation'] 185 186 vm_name = module.params.get('vm_name') 187 vm = None 188 if vm_name: 189 vm = module.get_vm(vapp_name, vm_name) 190 191 if operation == 'poweron': 192 operation = 'powerOn' 193 elif operation == 'poweroff': 194 operation = 'powerOff' 195 196 cmd = 'power:%s' % operation 197 module.get_vapp(vapp_name).execute(cmd, 'post', targetVM=vm) 198 199 200def set_state(module): 201 state = module.params['state'] 202 vapp = module.get_vapp(module.params['vapp_name']) 203 if state == 'deployed': 204 action = module.params['operation'] == 'poweron' 205 if not vapp.deploy(action): 206 module.fail('unable to deploy vapp') 207 elif state == 'undeployed': 208 action = module.params['operation'] 209 if action == 'poweroff': 210 action = 'powerOff' 211 elif action != 'suspend': 212 action = None 213 if not vapp.undeploy(action): 214 module.fail('unable to undeploy vapp') 215 216 217def connect_to_network(module, vdc_name, vapp_name, network_name, network_mode): 218 nets = filter(lambda n: n.name == network_name, module.vca.get_networks(vdc_name)) 219 if len(nets) != 1: 220 module.fail_json("Unable to find network %s " % network_name) 221 222 the_vdc = module.vca.get_vdc(vdc_name) 223 the_vapp = module.vca.get_vapp(the_vdc, vapp_name) 224 225 if the_vapp and the_vapp.name != vapp_name: 226 module.fail_json(msg="Failed to find vapp named %s" % the_vapp.name) 227 228 # Connect vApp 229 task = the_vapp.connect_to_network(nets[0].name, nets[0].href) 230 result = module.vca.block_until_completed(task) 231 232 if result is None: 233 module.fail_json(msg="Failed to complete task") 234 235 # Connect VM 236 ip_allocation_mode = None 237 if network_mode == 'pool': 238 ip_allocation_mode = 'POOL' 239 elif network_mode == 'dhcp': 240 ip_allocation_mode = 'DHCP' 241 242 task = the_vapp.connect_vms(nets[0].name, connection_index=0, ip_allocation_mode=ip_allocation_mode) 243 if result is None: 244 module.fail_json(msg="Failed to complete task") 245 246 result = module.vca.block_until_completed(task) 247 if result is None: 248 module.fail_json(msg="Failed to complete task") 249 250 # Update VApp info and get VM IP 251 the_vapp = module.vca.get_vapp(the_vdc, vapp_name) 252 if the_vapp is None: 253 module.fail_json(msg="Failed to get vapp named %s" % vapp_name) 254 255 return get_vm_details(module) 256 257 258def get_vm_details(module): 259 vdc_name = module.params['vdc_name'] 260 vapp_name = module.params['vapp_name'] 261 vm_name = module.params['vm_name'] 262 the_vdc = module.vca.get_vdc(vdc_name) 263 the_vapp = module.vca.get_vapp(the_vdc, vapp_name) 264 if the_vapp and the_vapp.name != vapp_name: 265 module.fail_json(msg="Failed to find vapp named %s" % the_vapp.name) 266 the_vm_details = dict() 267 268 for vm in the_vapp.me.Children.Vm: 269 sections = vm.get_Section() 270 271 customization_section = ( 272 filter(lambda section: 273 section.__class__.__name__ == 274 "GuestCustomizationSectionType", 275 sections)[0]) 276 if customization_section.get_AdminPasswordEnabled(): 277 the_vm_details["vm_admin_password"] = customization_section.get_AdminPassword() 278 279 virtual_hardware_section = ( 280 filter(lambda section: 281 section.__class__.__name__ == 282 "VirtualHardwareSection_Type", 283 sections)[0]) 284 items = virtual_hardware_section.get_Item() 285 ips = [] 286 _url = '{http://www.vmware.com/vcloud/v1.5}ipAddress' 287 for item in items: 288 if item.Connection: 289 for c in item.Connection: 290 if c.anyAttributes_.get( 291 _url): 292 ips.append(c.anyAttributes_.get( 293 _url)) 294 if len(ips) > 0: 295 the_vm_details["vm_ip"] = ips[0] 296 297 return the_vm_details 298 299 300def main(): 301 argument_spec = dict( 302 vapp_name=dict(required=True), 303 vdc_name=dict(required=True), 304 template_name=dict(), 305 catalog_name=dict(default='Public Catalog'), 306 network_name=dict(), 307 network_mode=dict(default='pool', choices=['dhcp', 'static', 'pool']), 308 vm_name=dict(), 309 vm_cpus=dict(), 310 vm_memory=dict(), 311 operation=dict(default=DEFAULT_VAPP_OPERATION, choices=VAPP_OPERATIONS), 312 state=dict(default='present', choices=VAPP_STATES) 313 ) 314 315 module = VcaAnsibleModule(argument_spec=argument_spec, 316 supports_check_mode=True) 317 318 state = module.params['state'] 319 operation = module.params['operation'] 320 321 instance = get_instance(module) 322 323 result = dict(changed=False) 324 325 if instance and state == 'absent': 326 if not module.check_mode: 327 delete(module) 328 result['changed'] = True 329 330 elif state != 'absent': 331 if instance['state'] == 'absent': 332 if not module.check_mode: 333 result['ansible_facts'] = create(module) 334 result['changed'] = True 335 336 elif instance['state'] != state and state != 'present': 337 if not module.check_mode: 338 set_state(module) 339 result['changed'] = True 340 341 if operation != instance.get('status') and operation != 'noop': 342 if not module.check_mode: 343 do_operation(module) 344 result['changed'] = True 345 result['ansible_facts'] = get_vm_details(module) 346 347 return module.exit(**result) 348 349 350if __name__ == '__main__': 351 main() 352