1#!/usr/local/bin/python3.8 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 8 9DOCUMENTATION = r''' 10--- 11module: vca_vapp 12deprecated: 13 removed_at_date: '2022-06-01' 14 why: Module depends upon deprecated version of Pyvcloud library. 15 alternative: Use U(https://github.com/vmware/ansible-module-vcloud-director) instead. 16short_description: Manages vCloud Air vApp instances. 17description: 18 - This module will actively managed vCloud Air vApp instances. Instances 19 can be created and deleted as well as both deployed and undeployed. 20author: 21- Peter Sprygada (@privateip) 22notes: 23- VMware sold their vCloud Air service in Q2 2017. 24- VMware made significant changes to the pyvcloud interface around this time. The C(vca_vapp) module relies on now deprecated code. 25- Mileage with C(vca_vapp) may vary as vCloud Director APIs advance. 26- A viable alternative maybe U(https://github.com/vmware/ansible-module-vcloud-director) 27requirements: 28- pyvcloud <= 18.2.2 29options: 30 vapp_name: 31 description: 32 - The name of the vCloud Air vApp instance 33 required: true 34 template_name: 35 description: 36 - The name of the vApp template to use to create the vApp instance. If 37 the I(state) is not `absent` then the I(template_name) value must be 38 provided. The I(template_name) must be previously uploaded to the 39 catalog specified by I(catalog_name) 40 network_name: 41 description: 42 - The name of the network that should be attached to the virtual machine 43 in the vApp. The virtual network specified must already be created in 44 the vCloud Air VDC. If the I(state) is not 'absent' then the 45 I(network_name) argument must be provided. 46 network_mode: 47 description: 48 - Configures the mode of the network connection. 49 default: pool 50 choices: ['pool', 'dhcp', 'static'] 51 vm_name: 52 description: 53 - The name of the virtual machine instance in the vApp to manage. 54 vm_cpus: 55 description: 56 - The number of vCPUs to configure for the VM in the vApp. If the 57 I(vm_name) argument is provided, then this becomes a per VM setting 58 otherwise it is applied to all VMs in the vApp. 59 vm_memory: 60 description: 61 - The amount of memory in MB to allocate to VMs in the vApp. If the 62 I(vm_name) argument is provided, then this becomes a per VM setting 63 otherwise it is applied to all VMs in the vApp. 64 operation: 65 description: 66 - Specifies an operation to be performed on the vApp. 67 default: noop 68 choices: ['noop', 'poweron', 'poweroff', 'suspend', 'shutdown', 'reboot', 'reset'] 69 state: 70 description: 71 - Configures the state of the vApp. 72 default: present 73 choices: ['present', 'absent', 'deployed', 'undeployed'] 74 username: 75 description: 76 - The vCloud Air username to use during authentication 77 password: 78 description: 79 - The vCloud Air password to use during authentication 80 org: 81 description: 82 - The org to login to for creating vapp, mostly set when the service_type is vdc. 83 instance_id: 84 description: 85 - The instance id in a vchs environment to be used for creating the vapp 86 host: 87 description: 88 - The authentication host to be used when service type is vcd. 89 api_version: 90 description: 91 - The api version to be used with the vca 92 default: "5.7" 93 service_type: 94 description: 95 - The type of service we are authenticating against 96 default: vca 97 choices: [ "vca", "vchs", "vcd" ] 98 vdc_name: 99 description: 100 - The name of the virtual data center (VDC) where the vm should be created or contains the vAPP. 101extends_documentation_fragment: 102- community.vmware.vca 103 104''' 105 106EXAMPLES = r''' 107- name: Creates a new vApp in a VCA instance 108 community.vmware.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_collections.community.vmware.plugins.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 the_vdc = module.vca.get_vdc(vdc_name) 262 the_vapp = module.vca.get_vapp(the_vdc, vapp_name) 263 if the_vapp and the_vapp.name != vapp_name: 264 module.fail_json(msg="Failed to find vapp named %s" % the_vapp.name) 265 the_vm_details = dict() 266 267 for vm in the_vapp.me.Children.Vm: 268 sections = vm.get_Section() 269 270 customization_section = ( 271 filter(lambda section: 272 section.__class__.__name__ == "GuestCustomizationSectionType", 273 sections)[0]) 274 if customization_section.get_AdminPasswordEnabled(): 275 the_vm_details["vm_admin_password"] = customization_section.get_AdminPassword() 276 277 virtual_hardware_section = ( 278 filter(lambda section: 279 section.__class__.__name__ == "VirtualHardwareSection_Type", 280 sections)[0]) 281 items = virtual_hardware_section.get_Item() 282 ips = [] 283 _url = '{http://www.vmware.com/vcloud/v1.5}ipAddress' 284 for item in items: 285 if item.Connection: 286 for c in item.Connection: 287 if c.anyAttributes_.get( 288 _url): 289 ips.append(c.anyAttributes_.get( 290 _url)) 291 if len(ips) > 0: 292 the_vm_details["vm_ip"] = ips[0] 293 294 return the_vm_details 295 296 297def main(): 298 argument_spec = dict( 299 vapp_name=dict(required=True), 300 vdc_name=dict(required=True), 301 template_name=dict(), 302 catalog_name=dict(default='Public Catalog'), 303 network_name=dict(), 304 network_mode=dict(default='pool', choices=['dhcp', 'static', 'pool']), 305 vm_name=dict(), 306 vm_cpus=dict(), 307 vm_memory=dict(), 308 operation=dict(default=DEFAULT_VAPP_OPERATION, choices=VAPP_OPERATIONS), 309 state=dict(default='present', choices=VAPP_STATES) 310 ) 311 312 module = VcaAnsibleModule(argument_spec=argument_spec, 313 supports_check_mode=True) 314 315 module.deprecate( 316 msg="The 'vca_fw' module is deprecated, Please use https://github.com/vmware/ansible-module-vcloud-director instead", 317 date="2022-06-01", 318 collection_name="community.vmware" 319 ) 320 321 state = module.params['state'] 322 operation = module.params['operation'] 323 324 instance = get_instance(module) 325 326 result = dict(changed=False) 327 328 if instance and state == 'absent': 329 if not module.check_mode: 330 delete(module) 331 result['changed'] = True 332 333 elif state != 'absent': 334 if instance['state'] == 'absent': 335 if not module.check_mode: 336 result['ansible_facts'] = create(module) 337 result['changed'] = True 338 339 elif instance['state'] != state and state != 'present': 340 if not module.check_mode: 341 set_state(module) 342 result['changed'] = True 343 344 if operation != instance.get('status') and operation != 'noop': 345 if not module.check_mode: 346 do_operation(module) 347 result['changed'] = True 348 result['ansible_facts'] = get_vm_details(module) 349 350 return module.exit(**result) 351 352 353if __name__ == '__main__': 354 main() 355