cloudscale_server: implement param server_groups (#54868)
This commit is contained in:
parent
c51f840faa
commit
e28d08a3c1
4 changed files with 160 additions and 11 deletions
|
@ -92,7 +92,15 @@ options:
|
|||
anti_affinity_with:
|
||||
description:
|
||||
- UUID of another server to create an anti-affinity group with.
|
||||
- Mutually exclusive with I(server_groups).
|
||||
- Deprecated, removed in version 2.11.
|
||||
type: str
|
||||
server_groups:
|
||||
description:
|
||||
- List of UUID or names of server groups.
|
||||
- Mutually exclusive with I(anti_affinity_with).
|
||||
type: list
|
||||
version_added: '2.8'
|
||||
user_data:
|
||||
description:
|
||||
- Cloud-init configuration (cloud-config) data to use for the server.
|
||||
|
@ -109,28 +117,29 @@ extends_documentation_fragment: cloudscale
|
|||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Start a server (if it does not exist) and register the server details
|
||||
# Create and start a server with an existing server group (shiny-group)
|
||||
- name: Start cloudscale.ch server
|
||||
cloudscale_server:
|
||||
name: my-shiny-cloudscale-server
|
||||
image: debian-8
|
||||
flavor: flex-4
|
||||
ssh_keys: ssh-rsa XXXXXXXXXX...XXXX ansible@cloudscale
|
||||
server_groups: shiny-group
|
||||
use_private_network: True
|
||||
bulk_volume_size_gb: 100
|
||||
api_token: xxxxxx
|
||||
register: server1
|
||||
|
||||
# Start another server in anti-affinity to the first one
|
||||
# Start another server in anti-affinity (server group shiny-group)
|
||||
- name: Start second cloudscale.ch server
|
||||
cloudscale_server:
|
||||
name: my-other-shiny-server
|
||||
image: ubuntu-16.04
|
||||
flavor: flex-8
|
||||
ssh_keys: ssh-rsa XXXXXXXXXXX ansible@cloudscale
|
||||
anti_affinity_with: '{{ server1.uuid }}'
|
||||
server_groups: shiny-group
|
||||
api_token: xxxxxx
|
||||
|
||||
|
||||
# Force to update the flavor of a running server
|
||||
- name: Start cloudscale.ch server
|
||||
cloudscale_server:
|
||||
|
@ -224,10 +233,18 @@ ssh_host_keys:
|
|||
type: list
|
||||
sample: ["ecdsa-sha2-nistp256 XXXXX", ... ]
|
||||
anti_affinity_with:
|
||||
description: List of servers in the same anti-affinity group
|
||||
description:
|
||||
- List of servers in the same anti-affinity group
|
||||
- Deprecated, removed in version 2.11.
|
||||
returned: success when not state == absent
|
||||
type: str
|
||||
type: list
|
||||
sample: []
|
||||
server_groups:
|
||||
description: List of server groups
|
||||
returned: success when not state == absent
|
||||
type: list
|
||||
sample: [ {"href": "https://api.cloudscale.ch/v1/server-groups/...", "uuid": "...", "name": "db-group"} ]
|
||||
version_added: '2.8'
|
||||
'''
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
@ -378,12 +395,42 @@ class AnsibleCloudscaleServer(AnsibleCloudscaleBase):
|
|||
|
||||
return server_info
|
||||
|
||||
def _get_server_group_ids(self):
|
||||
server_group_params = self._module.params['server_groups']
|
||||
if not server_group_params:
|
||||
return None
|
||||
|
||||
matching_group_names = []
|
||||
results = []
|
||||
server_groups = self._get('server-groups')
|
||||
for server_group in server_groups:
|
||||
if server_group['uuid'] in server_group_params:
|
||||
results.append(server_group['uuid'])
|
||||
server_group_params.remove(server_group['uuid'])
|
||||
|
||||
elif server_group['name'] in server_group_params:
|
||||
results.append(server_group['uuid'])
|
||||
server_group_params.remove(server_group['name'])
|
||||
# Remember the names found
|
||||
matching_group_names.append(server_group['name'])
|
||||
|
||||
# Names are not unique, verify if name already found in previous iterations
|
||||
elif server_group['name'] in matching_group_names:
|
||||
self._module.fail_json(msg="More than one server group with name exists: '%s'. "
|
||||
"Use the 'uuid' parameter to identify the server group." % server_group['name'])
|
||||
|
||||
if server_group_params:
|
||||
self._module.fail_json(msg="Server group name or UUID not found: %s" % ', '.join(server_group_params))
|
||||
|
||||
return results
|
||||
|
||||
def _create_server(self, server_info):
|
||||
self._result['changed'] = True
|
||||
|
||||
data = deepcopy(self._module.params)
|
||||
for i in ('uuid', 'state', 'force', 'api_timeout', 'api_token'):
|
||||
del data[i]
|
||||
data['server_groups'] = self._get_server_group_ids()
|
||||
|
||||
self._result['diff']['before'] = self._init_server_container()
|
||||
self._result['diff']['after'] = deepcopy(data)
|
||||
|
@ -396,6 +443,14 @@ class AnsibleCloudscaleServer(AnsibleCloudscaleBase):
|
|||
|
||||
previous_state = server_info.get('state')
|
||||
|
||||
# The API doesn't support to update server groups.
|
||||
# Show a warning to the user if the desired state does not match.
|
||||
desired_server_group_ids = self._get_server_group_ids()
|
||||
if desired_server_group_ids is not None:
|
||||
current_server_group_ids = [grp['uuid'] for grp in server_info['server_groups']]
|
||||
if desired_server_group_ids != current_server_group_ids:
|
||||
self._module.warn("Server groups can not be mutated, server needs redeployment to change groups.")
|
||||
|
||||
server_info = self._update_param('flavor', server_info, requires_stop=True)
|
||||
server_info = self._update_param('name', server_info)
|
||||
|
||||
|
@ -450,7 +505,8 @@ def main():
|
|||
use_public_network=dict(type='bool', default=True),
|
||||
use_private_network=dict(type='bool', default=False),
|
||||
use_ipv6=dict(type='bool', default=True),
|
||||
anti_affinity_with=dict(),
|
||||
anti_affinity_with=dict(removed_in_version='2.11'),
|
||||
server_groups=dict(type='list'),
|
||||
user_data=dict(),
|
||||
force=dict(type='bool', default=False)
|
||||
))
|
||||
|
@ -458,6 +514,7 @@ def main():
|
|||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
required_one_of=(('name', 'uuid'),),
|
||||
mutually_exclusive=(('anti_affinity_with', 'server_groups'),),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
- name: Fail missing params
|
||||
cloudscale_server:
|
||||
register: srv
|
||||
ignore_errors: True
|
||||
- name: 'VERIFY: Fail name and UUID'
|
||||
assert:
|
||||
that:
|
||||
- srv is failed
|
||||
|
||||
- name: Fail unexisting server group
|
||||
cloudscale_server:
|
||||
name: '{{ cloudscale_resource_prefix }}-test-group'
|
||||
flavor: '{{ cloudscale_test_flavor }}'
|
||||
image: '{{ cloudscale_test_image }}'
|
||||
password: '{{ cloudscale_test_password }}'
|
||||
server_groups: '{{ cloudscale_resource_prefix }}-unexist-group'
|
||||
ignore_errors: True
|
||||
register: srv
|
||||
- name: 'VERIFY: Fail unexisting server group'
|
||||
assert:
|
||||
that:
|
||||
- srv is failed
|
||||
- srv.msg.startswith('Server group name or UUID not found')
|
||||
|
||||
- name: Create two server groups with the same name
|
||||
uri:
|
||||
url: https://api.cloudscale.ch/v1/server-groups
|
||||
method: POST
|
||||
headers:
|
||||
Authorization: 'Bearer {{ cloudscale_api_token }}'
|
||||
body:
|
||||
name: '{{ cloudscale_resource_prefix }}-duplicate'
|
||||
type: anti-affinity
|
||||
body_format: json
|
||||
status_code: 201
|
||||
register: duplicate
|
||||
with_sequence: count=2
|
||||
|
||||
- name: Try to use server groups with identical name
|
||||
cloudscale_server:
|
||||
name: '{{ cloudscale_resource_prefix }}-test-group'
|
||||
flavor: '{{ cloudscale_test_flavor }}'
|
||||
image: '{{ cloudscale_test_image }}'
|
||||
password: '{{ cloudscale_test_password }}'
|
||||
server_groups: '{{ cloudscale_resource_prefix }}-duplicate'
|
||||
ignore_errors: True
|
||||
register: srv
|
||||
- name: 'VERIFY: Fail unexisting server group'
|
||||
assert:
|
||||
that:
|
||||
- srv is failed
|
||||
- srv.msg.startswith('More than one server group with name exists')
|
|
@ -1,7 +1,11 @@
|
|||
---
|
||||
- block:
|
||||
- import_tasks: failures.yml
|
||||
- import_tasks: tests.yml
|
||||
always:
|
||||
- import_role:
|
||||
name: cloudscale_common
|
||||
tasks_from: cleanup_servers
|
||||
- import_role:
|
||||
name: cloudscale_common
|
||||
tasks_from: cleanup_server_groups
|
|
@ -1,10 +1,17 @@
|
|||
---
|
||||
- name: Setup server groups
|
||||
cloudscale_server_group:
|
||||
name: '{{ cloudscale_resource_prefix }}-group-{{ item }}'
|
||||
type: anti-affinity
|
||||
with_sequence: count=2
|
||||
|
||||
- name: Test create a running server in check mode
|
||||
cloudscale_server:
|
||||
name: '{{ cloudscale_resource_prefix }}-test'
|
||||
flavor: '{{ cloudscale_test_flavor }}'
|
||||
image: '{{ cloudscale_test_image }}'
|
||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||
register: server
|
||||
check_mode: yes
|
||||
- name: Verify create a running server in check mode
|
||||
|
@ -19,12 +26,14 @@
|
|||
flavor: '{{ cloudscale_test_flavor }}'
|
||||
image: '{{ cloudscale_test_image }}'
|
||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||
register: server
|
||||
- name: Verify create a running server
|
||||
assert:
|
||||
that:
|
||||
- server is changed
|
||||
- server.state == 'running'
|
||||
- server.server_groups.0.name == '{{ cloudscale_resource_prefix }}-group-1'
|
||||
|
||||
- name: Test create a running server idempotence
|
||||
cloudscale_server:
|
||||
|
@ -32,12 +41,14 @@
|
|||
flavor: '{{ cloudscale_test_flavor }}'
|
||||
image: '{{ cloudscale_test_image }}'
|
||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||
register: server
|
||||
- name: Verify create a running server idempotence
|
||||
assert:
|
||||
that:
|
||||
- server is not changed
|
||||
- server.state == 'running'
|
||||
- server.server_groups.0.name == '{{ cloudscale_resource_prefix }}-group-1'
|
||||
|
||||
- name: Test update flavor of a running server without force in check mode
|
||||
cloudscale_server:
|
||||
|
@ -54,6 +65,7 @@
|
|||
- server is not changed
|
||||
- server.state == 'running'
|
||||
- server.flavor.slug == '{{ cloudscale_test_flavor }}'
|
||||
- server.server_groups.0.name == '{{ cloudscale_resource_prefix }}-group-1'
|
||||
|
||||
- name: Test update flavor of a running server without force
|
||||
cloudscale_server:
|
||||
|
@ -69,6 +81,7 @@
|
|||
- server is not changed
|
||||
- server.state == 'running'
|
||||
- server.flavor.slug == '{{ cloudscale_test_flavor }}'
|
||||
- server.server_groups.0.name == '{{ cloudscale_resource_prefix }}-group-1'
|
||||
|
||||
- name: Test update flavor of a running server without force idempotence
|
||||
cloudscale_server:
|
||||
|
@ -84,6 +97,7 @@
|
|||
- server is not changed
|
||||
- server.state == 'running'
|
||||
- server.flavor.slug == '{{ cloudscale_test_flavor }}'
|
||||
- server.server_groups.0.name == '{{ cloudscale_resource_prefix }}-group-1'
|
||||
|
||||
- name: Test update flavor and name of a running server without force in check mode
|
||||
cloudscale_server:
|
||||
|
@ -196,7 +210,7 @@
|
|||
flavor: '{{ cloudscale_test_flavor }}'
|
||||
image: '{{ cloudscale_test_image }}'
|
||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||
anti_affinity_with: '{{ running_server_uuid }}'
|
||||
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||
use_public_network: no
|
||||
use_private_network: yes
|
||||
state: stopped
|
||||
|
@ -214,7 +228,7 @@
|
|||
flavor: '{{ cloudscale_test_flavor }}'
|
||||
image: '{{ cloudscale_test_image }}'
|
||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||
anti_affinity_with: '{{ running_server_uuid }}'
|
||||
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||
use_public_network: no
|
||||
use_private_network: yes
|
||||
state: stopped
|
||||
|
@ -226,6 +240,7 @@
|
|||
- server_stopped.state == 'stopped'
|
||||
- server_stopped.anti_affinity_with.0.uuid == running_server_uuid
|
||||
- server_stopped.interfaces.0.type == 'private'
|
||||
- server_stopped.server_groups.0.name == '{{ cloudscale_resource_prefix }}-group-1'
|
||||
|
||||
- name: Test create server stopped in anti affinity and private network only idempotence
|
||||
cloudscale_server:
|
||||
|
@ -233,7 +248,7 @@
|
|||
flavor: '{{ cloudscale_test_flavor }}'
|
||||
image: '{{ cloudscale_test_image }}'
|
||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||
anti_affinity_with: '{{ running_server_uuid }}'
|
||||
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||
use_public_network: no
|
||||
use_private_network: yes
|
||||
state: stopped
|
||||
|
@ -245,6 +260,27 @@
|
|||
- server_stopped.state == 'stopped'
|
||||
- server_stopped.anti_affinity_with.0.uuid == running_server_uuid
|
||||
- server_stopped.interfaces.0.type == 'private'
|
||||
- server_stopped.server_groups.0.name == '{{ cloudscale_resource_prefix }}-group-1'
|
||||
|
||||
- name: Test change server group not changed
|
||||
cloudscale_server:
|
||||
name: '{{ cloudscale_resource_prefix }}-test-stopped'
|
||||
flavor: '{{ cloudscale_test_flavor }}'
|
||||
image: '{{ cloudscale_test_image }}'
|
||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||
server_groups: '{{ cloudscale_resource_prefix }}-group-2'
|
||||
use_public_network: no
|
||||
use_private_network: yes
|
||||
state: stopped
|
||||
register: server_stopped
|
||||
- name: Verify Test update server group not changed
|
||||
assert:
|
||||
that:
|
||||
- server_stopped is not changed
|
||||
- server_stopped.state == 'stopped'
|
||||
- server_stopped.anti_affinity_with.0.uuid == running_server_uuid
|
||||
- server_stopped.interfaces.0.type == 'private'
|
||||
- server_stopped.server_groups.0.name == '{{ cloudscale_resource_prefix }}-group-1'
|
||||
|
||||
- name: Test create server with password in check mode
|
||||
cloudscale_server:
|
||||
|
@ -371,7 +407,6 @@
|
|||
- server.flavor.slug == '{{ cloudscale_test_flavor }}'
|
||||
- server.name == '{{ cloudscale_resource_prefix }}-test'
|
||||
|
||||
|
||||
- name: Test update a stopped server idempotence
|
||||
cloudscale_server:
|
||||
uuid: '{{ server.uuid }}'
|
||||
|
|
Loading…
Reference in a new issue