1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2026-02-04 07:51:50 +00:00
community.general/plugins/modules/sl_vm.py
Felix Fontein 236b9c0e04
Sort imports with ruff check --fix (#11400)
Sort imports with ruff check --fix.
2026-01-09 07:40:58 +01:00

478 lines
13 KiB
Python

#!/usr/bin/python
# Copyright (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
DOCUMENTATION = r"""
module: sl_vm
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'.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
instance_id:
description:
- Instance ID of the virtual instance to perform action option.
type: str
hostname:
description:
- Hostname to be provided to a virtual instance.
type: str
domain:
description:
- Domain name to be provided to a virtual instance.
type: str
datacenter:
description:
- Datacenter for the virtual instance to be deployed.
type: str
choices:
- ams01
- ams03
- che01
- dal01
- dal05
- dal06
- dal09
- dal10
- dal12
- dal13
- fra02
- fra04
- fra05
- hkg02
- hou02
- lon02
- lon04
- lon06
- mel01
- mex01
- mil01
- mon01
- osl01
- par01
- sao01
- sea01
- seo01
- sjc01
- sjc03
- sjc04
- sng01
- syd01
- syd04
- tok02
- tor01
- wdc01
- wdc04
- wdc06
- wdc07
tags:
description:
- Tag or list of tags to be provided to a virtual instance.
type: str
hourly:
description:
- Flag to determine if the instance should be hourly billed.
type: bool
default: true
private:
description:
- Flag to determine if the instance should be private only.
type: bool
default: false
dedicated:
description:
- Flag to determine if the instance should be deployed in dedicated space.
type: bool
default: false
local_disk:
description:
- Flag to determine if local disk should be used for the new instance.
type: bool
default: true
cpus:
description:
- Count of cpus to be assigned to new virtual instance.
type: int
choices: [1, 2, 4, 8, 16, 32, 56]
memory:
description:
- Amount of memory to be assigned to new virtual instance.
type: int
choices: [1024, 2048, 4096, 6144, 8192, 12288, 16384, 32768, 49152, 65536, 131072, 247808]
flavor:
description:
- Specify which SoftLayer flavor template to use instead of cpus and memory.
version_added: '0.2.0'
type: str
disks:
description:
- List of disk sizes to be assigned to new virtual instance.
default: [25]
type: list
elements: int
os_code:
description:
- OS Code to be used for new virtual instance.
type: str
image_id:
description:
- Image Template to be used for new virtual instance.
type: str
nic_speed:
description:
- NIC Speed to be assigned to new virtual instance.
choices: [10, 100, 1000]
type: int
public_vlan:
description:
- VLAN by its ID to be assigned to the public NIC.
type: str
private_vlan:
description:
- VLAN by its ID to be assigned to the private NIC.
type: str
ssh_keys:
description:
- List of ssh keys by their ID to be assigned to a virtual instance.
type: list
elements: str
default: []
post_uri:
description:
- URL of a post provisioning script to be loaded and executed on virtual instance.
type: str
state:
description:
- Create, or cancel a virtual instance.
- Specify V(present) for create, V(absent) to cancel.
choices: [absent, present]
default: present
type: str
wait:
description:
- Flag used to wait for active status before returning.
type: bool
default: true
wait_time:
description:
- Time in seconds before wait returns.
default: 600
type: int
requirements:
- softlayer >= 4.1.1
notes:
- The C(softlayer-python) library, at version 6.2.6 (from Jan 2025), only supports Python version 3.8, 3.9 and 3.10.
author:
- Matt Colton (@mcltn)
seealso:
- name: SoftLayer API Python Client
description: The SoftLayer API Python Client is required for this module.
link: https://github.com/SoftLayer/softlayer-python
"""
EXAMPLES = r"""
- name: Build instance
hosts: localhost
gather_facts: false
tasks:
- name: Build instance request
community.general.sl_vm:
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
community.general.sl_vm:
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-replicas
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-replicas
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
community.general.sl_vm:
state: absent
tags: ansible-module-test
"""
# TODO: Disabled RETURN as it is breaking the build for docs. Needs to be fixed.
RETURN = """#"""
import json
import time
try:
import SoftLayer
from SoftLayer import VSManager
HAS_SL = True
vsManager = VSManager(SoftLayer.create_client_from_env())
except ImportError:
HAS_SL = False
from ansible.module_utils.basic import AnsibleModule
# TODO: get this info from API
STATES = ["present", "absent"]
DATACENTERS = [
"ams01",
"ams03",
"che01",
"dal01",
"dal05",
"dal06",
"dal09",
"dal10",
"dal12",
"dal13",
"fra02",
"fra04",
"fra05",
"hkg02",
"hou02",
"lon02",
"lon04",
"lon06",
"mel01",
"mex01",
"mil01",
"mon01",
"osl01",
"par01",
"sao01",
"sea01",
"seo01",
"sjc01",
"sjc03",
"sjc04",
"sng01",
"syd01",
"syd04",
"tok02",
"tor01",
"wdc01",
"wdc04",
"wdc06",
"wdc07",
]
CPU_SIZES = [1, 2, 4, 8, 16, 32, 56]
MEMORY_SIZES = [1024, 2048, 4096, 6144, 8192, 12288, 16384, 32768, 49152, 65536, 131072, 247808]
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]
def create_virtual_instance(module):
instances = vsManager.list_instances(
hostname=module.params.get("hostname"),
domain=module.params.get("domain"),
datacenter=module.params.get("datacenter"),
)
if instances:
return False, None
# Check if OS or Image Template is provided (Can't be both, defaults to OS)
if module.params.get("os_code") is not None and module.params.get("os_code") != "":
module.params["image_id"] = ""
elif module.params.get("image_id") is not 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"),
flavor=module.params.get("flavor"),
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 is not 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 Exception:
completed = False
return completed, instance
def cancel_instance(module):
canceled = True
if module.params.get("instance_id") is None and (
module.params.get("tags") or module.params.get("hostname") or module.params.get("domain")
):
tags = module.params.get("tags")
if isinstance(tags, str):
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 Exception:
canceled = False
elif module.params.get("instance_id") and module.params.get("instance_id") != 0:
try:
vsManager.cancel_instance(instance["id"])
except Exception:
canceled = False
else:
return False, None
return canceled, None
def main():
module = AnsibleModule(
argument_spec=dict(
instance_id=dict(type="str"),
hostname=dict(type="str"),
domain=dict(type="str"),
datacenter=dict(type="str", choices=DATACENTERS),
tags=dict(type="str"),
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),
flavor=dict(type="str"),
disks=dict(type="list", elements="int", default=[25]),
os_code=dict(type="str"),
image_id=dict(type="str"),
nic_speed=dict(type="int", choices=NIC_SPEEDS),
public_vlan=dict(type="str"),
private_vlan=dict(type="str"),
ssh_keys=dict(type="list", elements="str", default=[], no_log=False),
post_uri=dict(type="str"),
state=dict(type="str", 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") is True and instance:
(changed, instance) = wait_for_instance(module, instance["id"])
module.exit_json(changed=changed, instance=json.loads(json.dumps(instance, default=lambda o: o.__dict__)))
if __name__ == "__main__":
main()