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:
|
anti_affinity_with:
|
||||||
description:
|
description:
|
||||||
- UUID of another server to create an anti-affinity group with.
|
- 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
|
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:
|
user_data:
|
||||||
description:
|
description:
|
||||||
- Cloud-init configuration (cloud-config) data to use for the server.
|
- Cloud-init configuration (cloud-config) data to use for the server.
|
||||||
|
@ -109,28 +117,29 @@ extends_documentation_fragment: cloudscale
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
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
|
- name: Start cloudscale.ch server
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
name: my-shiny-cloudscale-server
|
name: my-shiny-cloudscale-server
|
||||||
image: debian-8
|
image: debian-8
|
||||||
flavor: flex-4
|
flavor: flex-4
|
||||||
ssh_keys: ssh-rsa XXXXXXXXXX...XXXX ansible@cloudscale
|
ssh_keys: ssh-rsa XXXXXXXXXX...XXXX ansible@cloudscale
|
||||||
|
server_groups: shiny-group
|
||||||
use_private_network: True
|
use_private_network: True
|
||||||
bulk_volume_size_gb: 100
|
bulk_volume_size_gb: 100
|
||||||
api_token: xxxxxx
|
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
|
- name: Start second cloudscale.ch server
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
name: my-other-shiny-server
|
name: my-other-shiny-server
|
||||||
image: ubuntu-16.04
|
image: ubuntu-16.04
|
||||||
flavor: flex-8
|
flavor: flex-8
|
||||||
ssh_keys: ssh-rsa XXXXXXXXXXX ansible@cloudscale
|
ssh_keys: ssh-rsa XXXXXXXXXXX ansible@cloudscale
|
||||||
anti_affinity_with: '{{ server1.uuid }}'
|
server_groups: shiny-group
|
||||||
api_token: xxxxxx
|
api_token: xxxxxx
|
||||||
|
|
||||||
|
|
||||||
# Force to update the flavor of a running server
|
# Force to update the flavor of a running server
|
||||||
- name: Start cloudscale.ch server
|
- name: Start cloudscale.ch server
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
|
@ -224,10 +233,18 @@ ssh_host_keys:
|
||||||
type: list
|
type: list
|
||||||
sample: ["ecdsa-sha2-nistp256 XXXXX", ... ]
|
sample: ["ecdsa-sha2-nistp256 XXXXX", ... ]
|
||||||
anti_affinity_with:
|
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
|
returned: success when not state == absent
|
||||||
type: str
|
type: list
|
||||||
sample: []
|
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
|
from datetime import datetime, timedelta
|
||||||
|
@ -378,12 +395,42 @@ class AnsibleCloudscaleServer(AnsibleCloudscaleBase):
|
||||||
|
|
||||||
return server_info
|
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):
|
def _create_server(self, server_info):
|
||||||
self._result['changed'] = True
|
self._result['changed'] = True
|
||||||
|
|
||||||
data = deepcopy(self._module.params)
|
data = deepcopy(self._module.params)
|
||||||
for i in ('uuid', 'state', 'force', 'api_timeout', 'api_token'):
|
for i in ('uuid', 'state', 'force', 'api_timeout', 'api_token'):
|
||||||
del data[i]
|
del data[i]
|
||||||
|
data['server_groups'] = self._get_server_group_ids()
|
||||||
|
|
||||||
self._result['diff']['before'] = self._init_server_container()
|
self._result['diff']['before'] = self._init_server_container()
|
||||||
self._result['diff']['after'] = deepcopy(data)
|
self._result['diff']['after'] = deepcopy(data)
|
||||||
|
@ -396,6 +443,14 @@ class AnsibleCloudscaleServer(AnsibleCloudscaleBase):
|
||||||
|
|
||||||
previous_state = server_info.get('state')
|
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('flavor', server_info, requires_stop=True)
|
||||||
server_info = self._update_param('name', server_info)
|
server_info = self._update_param('name', server_info)
|
||||||
|
|
||||||
|
@ -450,7 +505,8 @@ def main():
|
||||||
use_public_network=dict(type='bool', default=True),
|
use_public_network=dict(type='bool', default=True),
|
||||||
use_private_network=dict(type='bool', default=False),
|
use_private_network=dict(type='bool', default=False),
|
||||||
use_ipv6=dict(type='bool', default=True),
|
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(),
|
user_data=dict(),
|
||||||
force=dict(type='bool', default=False)
|
force=dict(type='bool', default=False)
|
||||||
))
|
))
|
||||||
|
@ -458,6 +514,7 @@ def main():
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=argument_spec,
|
argument_spec=argument_spec,
|
||||||
required_one_of=(('name', 'uuid'),),
|
required_one_of=(('name', 'uuid'),),
|
||||||
|
mutually_exclusive=(('anti_affinity_with', 'server_groups'),),
|
||||||
supports_check_mode=True,
|
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:
|
- block:
|
||||||
|
- import_tasks: failures.yml
|
||||||
- import_tasks: tests.yml
|
- import_tasks: tests.yml
|
||||||
always:
|
always:
|
||||||
- import_role:
|
- import_role:
|
||||||
name: cloudscale_common
|
name: cloudscale_common
|
||||||
tasks_from: cleanup_servers
|
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
|
- name: Test create a running server in check mode
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
name: '{{ cloudscale_resource_prefix }}-test'
|
name: '{{ cloudscale_resource_prefix }}-test'
|
||||||
flavor: '{{ cloudscale_test_flavor }}'
|
flavor: '{{ cloudscale_test_flavor }}'
|
||||||
image: '{{ cloudscale_test_image }}'
|
image: '{{ cloudscale_test_image }}'
|
||||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||||
|
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||||
register: server
|
register: server
|
||||||
check_mode: yes
|
check_mode: yes
|
||||||
- name: Verify create a running server in check mode
|
- name: Verify create a running server in check mode
|
||||||
|
@ -19,12 +26,14 @@
|
||||||
flavor: '{{ cloudscale_test_flavor }}'
|
flavor: '{{ cloudscale_test_flavor }}'
|
||||||
image: '{{ cloudscale_test_image }}'
|
image: '{{ cloudscale_test_image }}'
|
||||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||||
|
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||||
register: server
|
register: server
|
||||||
- name: Verify create a running server
|
- name: Verify create a running server
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- server is changed
|
- server is changed
|
||||||
- server.state == 'running'
|
- server.state == 'running'
|
||||||
|
- server.server_groups.0.name == '{{ cloudscale_resource_prefix }}-group-1'
|
||||||
|
|
||||||
- name: Test create a running server idempotence
|
- name: Test create a running server idempotence
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
|
@ -32,12 +41,14 @@
|
||||||
flavor: '{{ cloudscale_test_flavor }}'
|
flavor: '{{ cloudscale_test_flavor }}'
|
||||||
image: '{{ cloudscale_test_image }}'
|
image: '{{ cloudscale_test_image }}'
|
||||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||||
|
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||||
register: server
|
register: server
|
||||||
- name: Verify create a running server idempotence
|
- name: Verify create a running server idempotence
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- server is not changed
|
- server is not changed
|
||||||
- server.state == 'running'
|
- 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
|
- name: Test update flavor of a running server without force in check mode
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
|
@ -54,6 +65,7 @@
|
||||||
- server is not changed
|
- server is not changed
|
||||||
- server.state == 'running'
|
- server.state == 'running'
|
||||||
- server.flavor.slug == '{{ cloudscale_test_flavor }}'
|
- 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
|
- name: Test update flavor of a running server without force
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
|
@ -69,6 +81,7 @@
|
||||||
- server is not changed
|
- server is not changed
|
||||||
- server.state == 'running'
|
- server.state == 'running'
|
||||||
- server.flavor.slug == '{{ cloudscale_test_flavor }}'
|
- 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
|
- name: Test update flavor of a running server without force idempotence
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
|
@ -84,6 +97,7 @@
|
||||||
- server is not changed
|
- server is not changed
|
||||||
- server.state == 'running'
|
- server.state == 'running'
|
||||||
- server.flavor.slug == '{{ cloudscale_test_flavor }}'
|
- 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
|
- name: Test update flavor and name of a running server without force in check mode
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
|
@ -196,7 +210,7 @@
|
||||||
flavor: '{{ cloudscale_test_flavor }}'
|
flavor: '{{ cloudscale_test_flavor }}'
|
||||||
image: '{{ cloudscale_test_image }}'
|
image: '{{ cloudscale_test_image }}'
|
||||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||||
anti_affinity_with: '{{ running_server_uuid }}'
|
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||||
use_public_network: no
|
use_public_network: no
|
||||||
use_private_network: yes
|
use_private_network: yes
|
||||||
state: stopped
|
state: stopped
|
||||||
|
@ -214,7 +228,7 @@
|
||||||
flavor: '{{ cloudscale_test_flavor }}'
|
flavor: '{{ cloudscale_test_flavor }}'
|
||||||
image: '{{ cloudscale_test_image }}'
|
image: '{{ cloudscale_test_image }}'
|
||||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||||
anti_affinity_with: '{{ running_server_uuid }}'
|
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||||
use_public_network: no
|
use_public_network: no
|
||||||
use_private_network: yes
|
use_private_network: yes
|
||||||
state: stopped
|
state: stopped
|
||||||
|
@ -226,6 +240,7 @@
|
||||||
- server_stopped.state == 'stopped'
|
- server_stopped.state == 'stopped'
|
||||||
- server_stopped.anti_affinity_with.0.uuid == running_server_uuid
|
- server_stopped.anti_affinity_with.0.uuid == running_server_uuid
|
||||||
- server_stopped.interfaces.0.type == 'private'
|
- 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
|
- name: Test create server stopped in anti affinity and private network only idempotence
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
|
@ -233,7 +248,7 @@
|
||||||
flavor: '{{ cloudscale_test_flavor }}'
|
flavor: '{{ cloudscale_test_flavor }}'
|
||||||
image: '{{ cloudscale_test_image }}'
|
image: '{{ cloudscale_test_image }}'
|
||||||
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
ssh_keys: '{{ cloudscale_test_ssh_key }}'
|
||||||
anti_affinity_with: '{{ running_server_uuid }}'
|
server_groups: '{{ cloudscale_resource_prefix }}-group-1'
|
||||||
use_public_network: no
|
use_public_network: no
|
||||||
use_private_network: yes
|
use_private_network: yes
|
||||||
state: stopped
|
state: stopped
|
||||||
|
@ -245,6 +260,27 @@
|
||||||
- server_stopped.state == 'stopped'
|
- server_stopped.state == 'stopped'
|
||||||
- server_stopped.anti_affinity_with.0.uuid == running_server_uuid
|
- server_stopped.anti_affinity_with.0.uuid == running_server_uuid
|
||||||
- server_stopped.interfaces.0.type == 'private'
|
- 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
|
- name: Test create server with password in check mode
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
|
@ -371,7 +407,6 @@
|
||||||
- server.flavor.slug == '{{ cloudscale_test_flavor }}'
|
- server.flavor.slug == '{{ cloudscale_test_flavor }}'
|
||||||
- server.name == '{{ cloudscale_resource_prefix }}-test'
|
- server.name == '{{ cloudscale_resource_prefix }}-test'
|
||||||
|
|
||||||
|
|
||||||
- name: Test update a stopped server idempotence
|
- name: Test update a stopped server idempotence
|
||||||
cloudscale_server:
|
cloudscale_server:
|
||||||
uuid: '{{ server.uuid }}'
|
uuid: '{{ server.uuid }}'
|
||||||
|
|
Loading…
Reference in a new issue