diff --git a/cloud/softlayer/__init__.py b/cloud/softlayer/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/cloud/softlayer/sl.py b/cloud/softlayer/sl.py new file mode 100644 index 00000000000..f5a48bd4e93 --- /dev/null +++ b/cloud/softlayer/sl.py @@ -0,0 +1,348 @@ +#!/usr/bin/python +# 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 . + +DOCUMENTATION = ''' +--- +module: SoftLayer +short_description: create or cancel a virtual instance in SoftLayer +description: + - Creates or cancels SoftLayer instances. When created, optionally waits for it to be 'running'. +version_added: "2.1" +options: + instance_id: + description: + - Instance Id of the virtual instance to perform action option + required: false + default: null + hostname: + description: + - Hostname to be provided to a virtual instance + required: false + default: null + domain: + description: + - Domain name to be provided to a virtual instance + required: false + default: null + datacenter: + description: + - Datacenter for the virtual instance to be deployed + required: false + default: null + tags: + description: + - Tag or list of tags to be provided to a virtual instance + required: false + default: null + hourly: + description: + - Flag to determine if the instance should be hourly billed + required: false + default: true + private: + description: + - Flag to determine if the instance should be private only + required: false + default: false + dedicated: + description: + - Falg to determine if the instance should be deployed in dedicated space + required: false + default: false + local_disk: + description: + - Flag to determine if local disk should be used for the new instance + required: false + default: true + cpus: + description: + - Count of cpus to be assigned to new virtual instance + required: true + default: null + memory: + description: + - Amount of memory to be assigned to new virtual instance + required: true + default: null + disks: + description: + - List of disk sizes to be assigned to new virtual instance + required: true + default: [25] + os_code: + description: + - OS Code to be used for new virtual instance + required: false + default: null + image_id: + description: + - Image Template to be used for new virtual instance + required: false + default: null + nic_speed: + description: + - NIC Speed to be assigned to new virtual instance + required: false + default: 10 + public_vlan: + description: + - VLAN by its Id to be assigned to the public NIC + required: false + default: null + private_vlan: + description: + - VLAN by its Id to be assigned to the private NIC + required: false + default: null + ssh_keys: + description: + - List of ssh keys by their Id to be assigned to a virtual instance + required: false + default: null + post_uri: + description: + - URL of a post provisioning script ot be loaded and exectued on virtual instance + required: false + default: null + state: + description: + - Create, or cancel a virtual instance. Specify "present" for create, "absent" to cancel. + required: false + default: 'present' + wait: + description: + - Flag used to wait for active status before returning + required: false + default: true + wait_timeout: + description: + - time in seconds before wait returns + required: false + default: 600 + +requirements: + - "python >= 2.6" + - "softlayer >= 4.1.1" +author: "Matt Colton (@mcltn)" +''' + +EXAMPLES = ''' +- name: Build instance + hosts: localhost + gather_facts: False + tasks: + - name: Build instance request + local_action: + module: sl + hostname: instance-1 + domain: anydomain.com + datacenter: dal09 + tags: ansible-module-test + hourly: True + private: False + dedicated: False + local_disk: True + cpus: 1 + memory: 1024 + disks: [25] + os_code: UBUNTU_LATEST + wait: False + +- name: Build additional instances + hosts: localhost + gather_facts: False + tasks: + - name: Build instances request + local_action: + module: sl + hostname: "{{ item.hostname }}" + domain: "{{ item.domain }}" + datacenter: "{{ item.datacenter }}" + tags: "{{ item.tags }}" + hourly: "{{ item.hourly }}" + private: "{{ item.private }}" + dedicated: "{{ item.dedicated }}" + local_disk: "{{ item.local_disk }}" + cpus: "{{ item.cpus }}" + memory: "{{ item.memory }}" + disks: "{{ item.disks }}" + os_code: "{{ item.os_code }}" + ssh_keys: "{{ item.ssh_keys }}" + wait: "{{ item.wait }}" + with_items: + - { hostname: 'instance-2', domain: 'anydomain.com', datacenter: 'dal09', tags: ['ansible-module-test', 'ansible-module-test-slaves'], hourly: True, private: False, dedicated: False, local_disk: True, cpus: 1, memory: 1024, disks: [25,100], os_code: 'UBUNTU_LATEST', ssh_keys: [], wait: True } + - { hostname: 'instance-3', domain: 'anydomain.com', datacenter: 'dal09', tags: ['ansible-module-test', 'ansible-module-test-slaves'], hourly: True, private: False, dedicated: False, local_disk: True, cpus: 1, memory: 1024, disks: [25,100], os_code: 'UBUNTU_LATEST', ssh_keys: [], wait: True } + + +- name: Cancel instances + hosts: localhost + gather_facts: False + tasks: + - name: Cancel by tag + local_action: + module: sl + state: absent + tags: ansible-module-test +''' + +# TODO: Disabled RETURN as it is breaking the build for docs. Needs to be fixed. +RETURN = '''# ''' + +import time + +STATES = ['present', 'absent'] +DATACENTERS = ['ams01','ams03','dal01','dal05','dal06','dal09','fra02','hkg02','hou02','lon2','mel01','mex01','mil01','mon01','par01','sjc01','sjc03','sao01','sea01','sng01','syd01','tok02','tor01','wdc01','wdc04'] +CPU_SIZES = [1,2,4,8,16] +MEMORY_SIZES = [1024,2048,4096,6144,8192,12288,16384,32768,49152,65536] +INITIALDISK_SIZES = [25,100] +LOCALDISK_SIZES = [25,100,150,200,300] +SANDISK_SIZES = [10,20,25,30,40,50,75,100,125,150,175,200,250,300,350,400,500,750,1000,1500,2000] +NIC_SPEEDS = [10,100,1000] + +try: + import SoftLayer + from SoftLayer import VSManager + + HAS_SL = True + vsManager = VSManager(SoftLayer.create_client_from_env()) +except ImportError: + HAS_SL = False + + +def create_virtual_instance(module): + + # Check if OS or Image Template is provided (Can't be both, defaults to OS) + if (module.params.get('os_code') != None and module.params.get('os_code') != ''): + module.params['image_id'] = '' + elif (module.params.get('image_id') != None and module.params.get('image_id') != ''): + module.params['os_code'] = '' + module.params['disks'] = [] # Blank out disks since it will use the template + else: + return False, None + + tags = module.params.get('tags') + if isinstance(tags, list): + tags = ','.join(map(str, module.params.get('tags'))) + + instance = vsManager.create_instance( + hostname = module.params.get('hostname'), + domain = module.params.get('domain'), + cpus = module.params.get('cpus'), + memory = module.params.get('memory'), + hourly = module.params.get('hourly'), + datacenter = module.params.get('datacenter'), + os_code = module.params.get('os_code'), + image_id = module.params.get('image_id'), + local_disk = module.params.get('local_disk'), + disks = module.params.get('disks'), + ssh_keys = module.params.get('ssh_keys'), + nic_speed = module.params.get('nic_speed'), + private = module.params.get('private'), + public_vlan = module.params.get('public_vlan'), + private_vlan = module.params.get('private_vlan'), + dedicated = module.params.get('dedicated'), + post_uri = module.params.get('post_uri'), + tags = tags) + + if instance != None and instance['id'] > 0: + return True, instance + else: + return False, None + + +def wait_for_instance(module,id): + instance = None + completed = False + wait_timeout = time.time() + module.params.get('wait_time') + while not completed and wait_timeout > time.time(): + try: + completed = vsManager.wait_for_ready(id, 10, 2) + if completed: + instance = vsManager.get_instance(id) + except: + completed = False + + return completed, instance + + +def cancel_instance(module): + canceled = True + if module.params.get('instance_id') == None and (module.params.get('tags') or module.params.get('hostname') or module.params.get('domain')): + tags = module.params.get('tags') + if isinstance(tags, basestring): + tags = [module.params.get('tags')] + instances = vsManager.list_instances(tags = tags, hostname = module.params.get('hostname'), domain = module.params.get('domain')) + for instance in instances: + try: + vsManager.cancel_instance(instance['id']) + except: + canceled = False + elif module.params.get('instance_id') and module.params.get('instance_id') != 0: + try: + vsManager.cancel_instance(instance['id']) + except: + canceled = False + else: + return False, None + + return canceled, None + + +def main(): + module = AnsibleModule( + argument_spec=dict( + instance_id=dict(), + hostname=dict(), + domain=dict(), + datacenter=dict(choices=DATACENTERS), + tags=dict(), + hourly=dict(type='bool', default=True), + private=dict(type='bool', default=False), + dedicated=dict(type='bool', default=False), + local_disk=dict(type='bool', default=True), + cpus=dict(type='int', choices=CPU_SIZES), + memory=dict(type='int', choices=MEMORY_SIZES), + disks=dict(type='list', default=[25]), + os_code=dict(), + image_id=dict(), + nic_speed=dict(type='int', choices=NIC_SPEEDS), + public_vlan=dict(), + private_vlan=dict(), + ssh_keys=dict(type='list', default=[]), + post_uri=dict(), + state=dict(default='present', choices=STATES), + wait=dict(type='bool', default=True), + wait_time=dict(type='int', default=600) + ) + ) + + if not HAS_SL: + module.fail_json(msg='softlayer python library required for this module') + + if module.params.get('state') == 'absent': + (changed, instance) = cancel_instance(module) + + elif module.params.get('state') == 'present': + (changed, instance) = create_virtual_instance(module) + if module.params.get('wait') == True: + (changed, instance) = wait_for_instance(module, instance['id']) + + module.exit_json(changed=changed, instance=json.loads(json.dumps(instance, default=lambda o: o.__dict__))) + +from ansible.module_utils.basic import * + +if __name__ == '__main__': + main() \ No newline at end of file