mirror of
https://github.com/trailofbits/algo.git
synced 2025-08-02 02:43:01 +02:00
Scaleway modules upgrade
This commit is contained in:
parent
5613003cbe
commit
d7f8f9363f
2 changed files with 132 additions and 26 deletions
|
@ -29,6 +29,15 @@ extends_documentation_fragment: scaleway
|
|||
|
||||
options:
|
||||
|
||||
public_ip:
|
||||
description:
|
||||
- Manage public IP on a Scaleway server
|
||||
- Could be Scaleway IP address UUID
|
||||
- C(dynamic) Means that IP is destroyed at the same time the host is destroyed
|
||||
- C(absent) Means no public IP at all
|
||||
version_added: '2.8'
|
||||
default: absent
|
||||
|
||||
enable_ipv6:
|
||||
description:
|
||||
- Enable public IPv6 connectivity on the instance
|
||||
|
@ -106,6 +115,13 @@ options:
|
|||
- Time to wait before every attempt to check the state of the server
|
||||
required: false
|
||||
default: 3
|
||||
|
||||
security_group:
|
||||
description:
|
||||
- Security group unique identifier
|
||||
- If no value provided, the default security group or current security group will be used
|
||||
required: false
|
||||
version_added: "2.8"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
|
@ -121,6 +137,19 @@ EXAMPLES = '''
|
|||
- test
|
||||
- www
|
||||
|
||||
- name: Create a server attached to a security group
|
||||
scaleway_compute:
|
||||
name: foobar
|
||||
state: present
|
||||
image: 89ee4018-f8c3-4dc4-a6b5-bca14f985ebe
|
||||
organization: 951df375-e094-4d26-97c1-ba548eeb9c42
|
||||
region: ams1
|
||||
commercial_type: VC1S
|
||||
security_group: 4a31b633-118e-4900-bd52-facf1085fc8d
|
||||
tags:
|
||||
- test
|
||||
- www
|
||||
|
||||
- name: Destroy it right after
|
||||
scaleway_compute:
|
||||
name: foobar
|
||||
|
@ -141,7 +170,6 @@ from ansible.module_utils.basic import AnsibleModule
|
|||
from ansible.module_utils.six.moves.urllib.parse import quote as urlquote
|
||||
from ansible.module_utils.scaleway import SCALEWAY_LOCATION, scaleway_argument_spec, Scaleway
|
||||
|
||||
|
||||
SCALEWAY_SERVER_STATES = (
|
||||
'stopped',
|
||||
'stopping',
|
||||
|
@ -157,6 +185,17 @@ SCALEWAY_TRANSITIONS_STATES = (
|
|||
)
|
||||
|
||||
|
||||
def check_image_id(compute_api, image_id):
|
||||
response = compute_api.get(path="images")
|
||||
|
||||
if response.ok and response.json:
|
||||
image_ids = [image["id"] for image in response.json["images"]]
|
||||
if image_id not in image_ids:
|
||||
compute_api.module.fail_json(msg='Error in getting image %s on %s' % (image_id, compute_api.module.params.get('api_url')))
|
||||
else:
|
||||
compute_api.module.fail_json(msg="Error in getting images from: %s" % compute_api.module.params.get('api_url'))
|
||||
|
||||
|
||||
def fetch_state(compute_api, server):
|
||||
compute_api.module.debug("fetch_state of server: %s" % server["id"])
|
||||
response = compute_api.get(path="servers/%s" % server["id"])
|
||||
|
@ -195,17 +234,51 @@ def wait_to_complete_state_transition(compute_api, server):
|
|||
compute_api.module.fail_json(msg="Server takes too long to finish its transition")
|
||||
|
||||
|
||||
def public_ip_payload(compute_api, public_ip):
|
||||
# We don't want a public ip
|
||||
if public_ip in ("absent",):
|
||||
return {"dynamic_ip_required": False}
|
||||
|
||||
# IP is only attached to the instance and is released as soon as the instance terminates
|
||||
if public_ip in ("dynamic", "allocated"):
|
||||
return {"dynamic_ip_required": True}
|
||||
|
||||
# We check that the IP we want to attach exists, if so its ID is returned
|
||||
response = compute_api.get("ips")
|
||||
if not response.ok:
|
||||
msg = 'Error during public IP validation: (%s) %s' % (response.status_code, response.json)
|
||||
compute_api.module.fail_json(msg=msg)
|
||||
|
||||
ip_list = []
|
||||
try:
|
||||
ip_list = response.json["ips"]
|
||||
except KeyError:
|
||||
compute_api.module.fail_json(msg="Error in getting the IP information from: %s" % response.json)
|
||||
|
||||
lookup = [ip["id"] for ip in ip_list]
|
||||
if public_ip in lookup:
|
||||
return {"public_ip": public_ip}
|
||||
|
||||
|
||||
def create_server(compute_api, server):
|
||||
compute_api.module.debug("Starting a create_server")
|
||||
target_server = None
|
||||
response = compute_api.post(path="servers",
|
||||
data={"enable_ipv6": server["enable_ipv6"],
|
||||
"boot_type": server["boot_type"],
|
||||
"tags": server["tags"],
|
||||
"commercial_type": server["commercial_type"],
|
||||
"image": server["image"],
|
||||
"name": server["name"],
|
||||
"organization": server["organization"]})
|
||||
data = {"enable_ipv6": server["enable_ipv6"],
|
||||
"tags": server["tags"],
|
||||
"commercial_type": server["commercial_type"],
|
||||
"image": server["image"],
|
||||
"dynamic_ip_required": server["dynamic_ip_required"],
|
||||
"name": server["name"],
|
||||
"organization": server["organization"]
|
||||
}
|
||||
|
||||
if server["boot_type"]:
|
||||
data["boot_type"] = server["boot_type"]
|
||||
|
||||
if server["security_group"]:
|
||||
data["security_group"] = server["security_group"]
|
||||
|
||||
response = compute_api.post(path="servers", data=data)
|
||||
|
||||
if not response.ok:
|
||||
msg = 'Error during server creation: (%s) %s' % (response.status_code, response.json)
|
||||
|
@ -278,7 +351,7 @@ def present_strategy(compute_api, wished_server):
|
|||
if compute_api.module.check_mode:
|
||||
return changed, {"status": "Server %s attributes would be changed." % target_server["id"]}
|
||||
|
||||
server_change_attributes(compute_api=compute_api, target_server=target_server, wished_server=wished_server)
|
||||
target_server = server_change_attributes(compute_api=compute_api, target_server=target_server, wished_server=wished_server)
|
||||
|
||||
return changed, target_server
|
||||
|
||||
|
@ -300,7 +373,7 @@ def absent_strategy(compute_api, wished_server):
|
|||
return changed, {"status": "Server %s would be made absent." % target_server["id"]}
|
||||
|
||||
# A server MUST be stopped to be deleted.
|
||||
while not fetch_state(compute_api=compute_api, server=target_server) == "stopped":
|
||||
while fetch_state(compute_api=compute_api, server=target_server) != "stopped":
|
||||
wait_to_complete_state_transition(compute_api=compute_api, server=target_server)
|
||||
response = stop_server(compute_api=compute_api, server=target_server)
|
||||
|
||||
|
@ -341,7 +414,7 @@ def running_strategy(compute_api, wished_server):
|
|||
if compute_api.module.check_mode:
|
||||
return changed, {"status": "Server %s attributes would be changed before running it." % target_server["id"]}
|
||||
|
||||
server_change_attributes(compute_api=compute_api, target_server=target_server, wished_server=wished_server)
|
||||
target_server = server_change_attributes(compute_api=compute_api, target_server=target_server, wished_server=wished_server)
|
||||
|
||||
current_state = fetch_state(compute_api=compute_api, server=target_server)
|
||||
if current_state not in ("running", "starting"):
|
||||
|
@ -385,7 +458,7 @@ def stop_strategy(compute_api, wished_server):
|
|||
return changed, {
|
||||
"status": "Server %s attributes would be changed before stopping it." % target_server["id"]}
|
||||
|
||||
server_change_attributes(compute_api=compute_api, target_server=target_server, wished_server=wished_server)
|
||||
target_server = server_change_attributes(compute_api=compute_api, target_server=target_server, wished_server=wished_server)
|
||||
|
||||
wait_to_complete_state_transition(compute_api=compute_api, server=target_server)
|
||||
|
||||
|
@ -432,7 +505,7 @@ def restart_strategy(compute_api, wished_server):
|
|||
return changed, {
|
||||
"status": "Server %s attributes would be changed before rebooting it." % target_server["id"]}
|
||||
|
||||
server_change_attributes(compute_api=compute_api, target_server=target_server, wished_server=wished_server)
|
||||
target_server = server_change_attributes(compute_api=compute_api, target_server=target_server, wished_server=wished_server)
|
||||
|
||||
changed = True
|
||||
if compute_api.module.check_mode:
|
||||
|
@ -471,8 +544,8 @@ state_strategy = {
|
|||
def find(compute_api, wished_server, per_page=1):
|
||||
compute_api.module.debug("Getting inside find")
|
||||
# Only the name attribute is accepted in the Compute query API
|
||||
url = 'servers?name=%s&per_page=%d' % (urlquote(wished_server["name"]), per_page)
|
||||
response = compute_api.get(url)
|
||||
response = compute_api.get("servers", params={"name": wished_server["name"],
|
||||
"per_page": per_page})
|
||||
|
||||
if not response.ok:
|
||||
msg = 'Error during server search: (%s) %s' % (response.status_code, response.json)
|
||||
|
@ -488,6 +561,7 @@ PATCH_MUTABLE_SERVER_ATTRIBUTES = (
|
|||
"tags",
|
||||
"name",
|
||||
"dynamic_ip_required",
|
||||
"security_group",
|
||||
)
|
||||
|
||||
|
||||
|
@ -499,29 +573,51 @@ def server_attributes_should_be_changed(compute_api, target_server, wished_serve
|
|||
for x in PATCH_MUTABLE_SERVER_ATTRIBUTES
|
||||
if x in target_server and x in wished_server)
|
||||
compute_api.module.debug("Debug dict %s" % debug_dict)
|
||||
|
||||
try:
|
||||
return any([target_server[x] != wished_server[x]
|
||||
for x in PATCH_MUTABLE_SERVER_ATTRIBUTES
|
||||
if x in target_server and x in wished_server])
|
||||
for key in PATCH_MUTABLE_SERVER_ATTRIBUTES:
|
||||
if key in target_server and key in wished_server:
|
||||
# When you are working with dict, only ID matter as we ask user to put only the resource ID in the playbook
|
||||
if isinstance(target_server[key], dict) and wished_server[key] and "id" in target_server[key].keys(
|
||||
) and target_server[key]["id"] != wished_server[key]:
|
||||
return True
|
||||
# Handling other structure compare simply the two objects content
|
||||
elif not isinstance(target_server[key], dict) and target_server[key] != wished_server[key]:
|
||||
return True
|
||||
return False
|
||||
except AttributeError:
|
||||
compute_api.module.fail_json(msg="Error while checking if attributes should be changed")
|
||||
|
||||
|
||||
def server_change_attributes(compute_api, target_server, wished_server):
|
||||
compute_api.module.debug("Starting patching server attributes")
|
||||
patch_payload = dict((x, wished_server[x])
|
||||
for x in PATCH_MUTABLE_SERVER_ATTRIBUTES
|
||||
if x in wished_server and x in target_server)
|
||||
patch_payload = dict()
|
||||
|
||||
for key in PATCH_MUTABLE_SERVER_ATTRIBUTES:
|
||||
if key in target_server and key in wished_server:
|
||||
# When you are working with dict, only ID matter as we ask user to put only the resource ID in the playbook
|
||||
if isinstance(target_server[key], dict) and "id" in target_server[key] and wished_server[key]:
|
||||
# Setting all key to current value except ID
|
||||
key_dict = dict((x, target_server[key][x]) for x in target_server[key].keys() if x != "id")
|
||||
# Setting ID to the user specified ID
|
||||
key_dict["id"] = wished_server[key]
|
||||
patch_payload[key] = key_dict
|
||||
elif not isinstance(target_server[key], dict):
|
||||
patch_payload[key] = wished_server[key]
|
||||
|
||||
response = compute_api.patch(path="servers/%s" % target_server["id"],
|
||||
data=patch_payload)
|
||||
if not response.ok:
|
||||
msg = 'Error during server attributes patching: (%s) %s' % (response.status_code, response.json)
|
||||
compute_api.module.fail_json(msg=msg)
|
||||
|
||||
try:
|
||||
target_server = response.json["server"]
|
||||
except KeyError:
|
||||
compute_api.module.fail_json(msg="Error in getting the server information from: %s" % response.json)
|
||||
|
||||
wait_to_complete_state_transition(compute_api=compute_api, server=target_server)
|
||||
|
||||
return response
|
||||
return target_server
|
||||
|
||||
|
||||
def core(module):
|
||||
|
@ -534,12 +630,19 @@ def core(module):
|
|||
"enable_ipv6": module.params["enable_ipv6"],
|
||||
"boot_type": module.params["boot_type"],
|
||||
"tags": module.params["tags"],
|
||||
"organization": module.params["organization"]
|
||||
"organization": module.params["organization"],
|
||||
"security_group": module.params["security_group"]
|
||||
}
|
||||
module.params['api_url'] = SCALEWAY_LOCATION[region]["api_endpoint"]
|
||||
|
||||
compute_api = Scaleway(module=module)
|
||||
|
||||
check_image_id(compute_api, wished_server["image"])
|
||||
|
||||
# IP parameters of the wished server depends on the configuration
|
||||
ip_payload = public_ip_payload(compute_api=compute_api, public_ip=module.params["public_ip"])
|
||||
wished_server.update(ip_payload)
|
||||
|
||||
changed, summary = state_strategy[wished_server["state"]](compute_api=compute_api, wished_server=wished_server)
|
||||
module.exit_json(changed=changed, msg=summary)
|
||||
|
||||
|
@ -552,13 +655,15 @@ def main():
|
|||
region=dict(required=True, choices=SCALEWAY_LOCATION.keys()),
|
||||
commercial_type=dict(required=True),
|
||||
enable_ipv6=dict(default=False, type="bool"),
|
||||
boot_type=dict(default="bootscript"),
|
||||
boot_type=dict(choices=['bootscript', 'local']),
|
||||
public_ip=dict(default="absent"),
|
||||
state=dict(choices=state_strategy.keys(), default='present'),
|
||||
tags=dict(type="list", default=[]),
|
||||
organization=dict(required=True),
|
||||
wait=dict(type="bool", default=False),
|
||||
wait_timeout=dict(type="int", default=300),
|
||||
wait_sleep_time=dict(type="int", default=3),
|
||||
security_group=dict(),
|
||||
))
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
scaleway_compute:
|
||||
name: "{{ algo_server_name }}"
|
||||
enable_ipv6: true
|
||||
public_ip: dynamic
|
||||
boot_type: local
|
||||
state: running
|
||||
image: "{{ images[0] }}"
|
||||
|
|
Loading…
Add table
Reference in a new issue