mirror of
https://github.com/trailofbits/algo.git
synced 2025-08-02 19:03:04 +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:
|
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:
|
enable_ipv6:
|
||||||
description:
|
description:
|
||||||
- Enable public IPv6 connectivity on the instance
|
- 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
|
- Time to wait before every attempt to check the state of the server
|
||||||
required: false
|
required: false
|
||||||
default: 3
|
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 = '''
|
EXAMPLES = '''
|
||||||
|
@ -121,6 +137,19 @@ EXAMPLES = '''
|
||||||
- test
|
- test
|
||||||
- www
|
- 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
|
- name: Destroy it right after
|
||||||
scaleway_compute:
|
scaleway_compute:
|
||||||
name: foobar
|
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.six.moves.urllib.parse import quote as urlquote
|
||||||
from ansible.module_utils.scaleway import SCALEWAY_LOCATION, scaleway_argument_spec, Scaleway
|
from ansible.module_utils.scaleway import SCALEWAY_LOCATION, scaleway_argument_spec, Scaleway
|
||||||
|
|
||||||
|
|
||||||
SCALEWAY_SERVER_STATES = (
|
SCALEWAY_SERVER_STATES = (
|
||||||
'stopped',
|
'stopped',
|
||||||
'stopping',
|
'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):
|
def fetch_state(compute_api, server):
|
||||||
compute_api.module.debug("fetch_state of server: %s" % server["id"])
|
compute_api.module.debug("fetch_state of server: %s" % server["id"])
|
||||||
response = compute_api.get(path="servers/%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")
|
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):
|
def create_server(compute_api, server):
|
||||||
compute_api.module.debug("Starting a create_server")
|
compute_api.module.debug("Starting a create_server")
|
||||||
target_server = None
|
target_server = None
|
||||||
response = compute_api.post(path="servers",
|
|
||||||
data = {"enable_ipv6": server["enable_ipv6"],
|
data = {"enable_ipv6": server["enable_ipv6"],
|
||||||
"boot_type": server["boot_type"],
|
|
||||||
"tags": server["tags"],
|
"tags": server["tags"],
|
||||||
"commercial_type": server["commercial_type"],
|
"commercial_type": server["commercial_type"],
|
||||||
"image": server["image"],
|
"image": server["image"],
|
||||||
|
"dynamic_ip_required": server["dynamic_ip_required"],
|
||||||
"name": server["name"],
|
"name": server["name"],
|
||||||
"organization": server["organization"]})
|
"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:
|
if not response.ok:
|
||||||
msg = 'Error during server creation: (%s) %s' % (response.status_code, response.json)
|
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:
|
if compute_api.module.check_mode:
|
||||||
return changed, {"status": "Server %s attributes would be changed." % target_server["id"]}
|
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
|
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"]}
|
return changed, {"status": "Server %s would be made absent." % target_server["id"]}
|
||||||
|
|
||||||
# A server MUST be stopped to be deleted.
|
# 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)
|
wait_to_complete_state_transition(compute_api=compute_api, server=target_server)
|
||||||
response = stop_server(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:
|
if compute_api.module.check_mode:
|
||||||
return changed, {"status": "Server %s attributes would be changed before running it." % target_server["id"]}
|
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)
|
current_state = fetch_state(compute_api=compute_api, server=target_server)
|
||||||
if current_state not in ("running", "starting"):
|
if current_state not in ("running", "starting"):
|
||||||
|
@ -385,7 +458,7 @@ def stop_strategy(compute_api, wished_server):
|
||||||
return changed, {
|
return changed, {
|
||||||
"status": "Server %s attributes would be changed before stopping it." % target_server["id"]}
|
"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)
|
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, {
|
return changed, {
|
||||||
"status": "Server %s attributes would be changed before rebooting it." % target_server["id"]}
|
"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
|
changed = True
|
||||||
if compute_api.module.check_mode:
|
if compute_api.module.check_mode:
|
||||||
|
@ -471,8 +544,8 @@ state_strategy = {
|
||||||
def find(compute_api, wished_server, per_page=1):
|
def find(compute_api, wished_server, per_page=1):
|
||||||
compute_api.module.debug("Getting inside find")
|
compute_api.module.debug("Getting inside find")
|
||||||
# Only the name attribute is accepted in the Compute query API
|
# 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("servers", params={"name": wished_server["name"],
|
||||||
response = compute_api.get(url)
|
"per_page": per_page})
|
||||||
|
|
||||||
if not response.ok:
|
if not response.ok:
|
||||||
msg = 'Error during server search: (%s) %s' % (response.status_code, response.json)
|
msg = 'Error during server search: (%s) %s' % (response.status_code, response.json)
|
||||||
|
@ -488,6 +561,7 @@ PATCH_MUTABLE_SERVER_ATTRIBUTES = (
|
||||||
"tags",
|
"tags",
|
||||||
"name",
|
"name",
|
||||||
"dynamic_ip_required",
|
"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
|
for x in PATCH_MUTABLE_SERVER_ATTRIBUTES
|
||||||
if x in target_server and x in wished_server)
|
if x in target_server and x in wished_server)
|
||||||
compute_api.module.debug("Debug dict %s" % debug_dict)
|
compute_api.module.debug("Debug dict %s" % debug_dict)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return any([target_server[x] != wished_server[x]
|
for key in PATCH_MUTABLE_SERVER_ATTRIBUTES:
|
||||||
for x in PATCH_MUTABLE_SERVER_ATTRIBUTES
|
if key in target_server and key in wished_server:
|
||||||
if x in target_server and x 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:
|
except AttributeError:
|
||||||
compute_api.module.fail_json(msg="Error while checking if attributes should be changed")
|
compute_api.module.fail_json(msg="Error while checking if attributes should be changed")
|
||||||
|
|
||||||
|
|
||||||
def server_change_attributes(compute_api, target_server, wished_server):
|
def server_change_attributes(compute_api, target_server, wished_server):
|
||||||
compute_api.module.debug("Starting patching server attributes")
|
compute_api.module.debug("Starting patching server attributes")
|
||||||
patch_payload = dict((x, wished_server[x])
|
patch_payload = dict()
|
||||||
for x in PATCH_MUTABLE_SERVER_ATTRIBUTES
|
|
||||||
if x in wished_server and x in target_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 "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"],
|
response = compute_api.patch(path="servers/%s" % target_server["id"],
|
||||||
data=patch_payload)
|
data=patch_payload)
|
||||||
if not response.ok:
|
if not response.ok:
|
||||||
msg = 'Error during server attributes patching: (%s) %s' % (response.status_code, response.json)
|
msg = 'Error during server attributes patching: (%s) %s' % (response.status_code, response.json)
|
||||||
compute_api.module.fail_json(msg=msg)
|
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)
|
wait_to_complete_state_transition(compute_api=compute_api, server=target_server)
|
||||||
|
|
||||||
return response
|
return target_server
|
||||||
|
|
||||||
|
|
||||||
def core(module):
|
def core(module):
|
||||||
|
@ -534,12 +630,19 @@ def core(module):
|
||||||
"enable_ipv6": module.params["enable_ipv6"],
|
"enable_ipv6": module.params["enable_ipv6"],
|
||||||
"boot_type": module.params["boot_type"],
|
"boot_type": module.params["boot_type"],
|
||||||
"tags": module.params["tags"],
|
"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"]
|
module.params['api_url'] = SCALEWAY_LOCATION[region]["api_endpoint"]
|
||||||
|
|
||||||
compute_api = Scaleway(module=module)
|
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)
|
changed, summary = state_strategy[wished_server["state"]](compute_api=compute_api, wished_server=wished_server)
|
||||||
module.exit_json(changed=changed, msg=summary)
|
module.exit_json(changed=changed, msg=summary)
|
||||||
|
|
||||||
|
@ -552,13 +655,15 @@ def main():
|
||||||
region=dict(required=True, choices=SCALEWAY_LOCATION.keys()),
|
region=dict(required=True, choices=SCALEWAY_LOCATION.keys()),
|
||||||
commercial_type=dict(required=True),
|
commercial_type=dict(required=True),
|
||||||
enable_ipv6=dict(default=False, type="bool"),
|
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'),
|
state=dict(choices=state_strategy.keys(), default='present'),
|
||||||
tags=dict(type="list", default=[]),
|
tags=dict(type="list", default=[]),
|
||||||
organization=dict(required=True),
|
organization=dict(required=True),
|
||||||
wait=dict(type="bool", default=False),
|
wait=dict(type="bool", default=False),
|
||||||
wait_timeout=dict(type="int", default=300),
|
wait_timeout=dict(type="int", default=300),
|
||||||
wait_sleep_time=dict(type="int", default=3),
|
wait_sleep_time=dict(type="int", default=3),
|
||||||
|
security_group=dict(),
|
||||||
))
|
))
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=argument_spec,
|
argument_spec=argument_spec,
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
scaleway_compute:
|
scaleway_compute:
|
||||||
name: "{{ algo_server_name }}"
|
name: "{{ algo_server_name }}"
|
||||||
enable_ipv6: true
|
enable_ipv6: true
|
||||||
|
public_ip: dynamic
|
||||||
boot_type: local
|
boot_type: local
|
||||||
state: running
|
state: running
|
||||||
image: "{{ images[0] }}"
|
image: "{{ images[0] }}"
|
||||||
|
|
Loading…
Add table
Reference in a new issue