cloudscale_server: implement param server_groups (#54868)

This commit is contained in:
René Moser 2019-04-11 00:16:35 +02:00 committed by GitHub
parent c51f840faa
commit e28d08a3c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 160 additions and 11 deletions

View file

@ -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,
) )

View file

@ -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')

View file

@ -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

View file

@ -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 }}'