1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

Merge branch 'devel' into v2_final

Conflicts:
	lib/ansible/modules/core
	v2/ansible/modules/core
	v2/ansible/modules/extras
This commit is contained in:
James Cammarata 2015-05-04 16:35:11 -05:00
commit 8f504dacdd
33 changed files with 1423 additions and 41 deletions

View file

@ -8,8 +8,9 @@ Major Changes:
* template code now retains types for bools and Numbers instead of turning them into strings
If you need the old behaviour, quote the value and it will get passed around as a string
Deprecated Modules:
* ec2_ami_search, in favor of the new ec2_ami_find
Deprecated Modules (new ones in parens):
* ec2_ami_search (ec2_ami_find)
* nova_compute (os_server)
New Modules:
* find
@ -22,12 +23,17 @@ New Modules:
* cloudstack: cs_affinitygroup
* cloudstack: cs_firewall
* cloudstack: cs_iso
* cloudstack: cs_instance
* cloudstack: cs_sshkeypair
* cloudstack: cs_securitygroup
* cloudstack: cs_securitygroup_rule
* cloudstack: cs_vmsnapshot
* maven_artifact
* openstack: os_server
* openstack: os_server_facts
* openstack: os_server_volume
* openstack: os_subnet
* openstack: os_volume
* pushover
* zabbix_host
* zabbix_hostmacro
@ -40,6 +46,7 @@ New Modules:
* vmware_datacenter
New Inventory scripts:
* cloudstack
* fleetctl
Other Notable Changes:

View file

@ -301,12 +301,8 @@ Role dependencies can also be specified as a full path, just like top level role
dependencies:
- { role: '/path/to/common/roles/foo', x: 1 }
Role dependencies can also be installed from source control repos or tar files, using a comma separated format of path, an optional version (tag, commit, branch etc) and optional friendly role name (an attempt is made to derive a role name from the repo name or archive filename)::
Role dependencies can also be installed from source control repos or tar files (via `galaxy`) using comma separated format of path, an optional version (tag, commit, branch etc) and optional friendly role name (an attempt is made to derive a role name from the repo name or archive filename). Both through the command line or via a requirements.yml passed to ansible-galaxy.
---
dependencies:
- { role: 'git+http://git.example.com/repos/role-foo,v1.1,foo' }
- { role: '/path/to/tar/file.tgz,,friendly-name' }
Roles dependencies are always executed before the role that includes them, and are recursive. By default,
roles can also only be added as a dependency once - if another role also lists it as a dependency it will

View file

@ -41,15 +41,22 @@ class AnsibleCloudStack:
if not has_lib_cs:
module.fail_json(msg="python library cs required: pip install cs")
self.result = {
'changed': False,
}
self.module = module
self._connect()
self.project_id = None
self.ip_address_id = None
self.zone_id = None
self.vm_id = None
self.os_type_id = None
self.domain = None
self.account = None
self.project = None
self.ip_address = None
self.zone = None
self.vm = None
self.os_type = None
self.hypervisor = None
self.capabilities = None
def _connect(self):
@ -68,27 +75,73 @@ class AnsibleCloudStack:
else:
self.cs = CloudStack(**read_config())
# TODO: rename to has_changed()
def _has_changed(self, want_dict, current_dict, only_keys=None):
for key, value in want_dict.iteritems():
# Optionally limit by a list of keys
if only_keys and key not in only_keys:
continue;
# Skip None values
if value is None:
continue;
if key in current_dict:
# API returns string for int in some cases, just to make sure
if isinstance(value, int):
current_dict[key] = int(current_dict[key])
elif isinstance(value, str):
current_dict[key] = str(current_dict[key])
# Only need to detect a singe change, not every item
if value != current_dict[key]:
return True
return False
def _get_by_key(self, key=None, my_dict={}):
if key:
if key in my_dict:
return my_dict[key]
self.module.fail_json(msg="Something went wrong: %s not found" % key)
return my_dict
# TODO: for backward compatibility only, remove if not used anymore
def get_project_id(self):
if self.project_id:
return self.project_id
return self.get_project(key='id')
def get_project(self, key=None):
if self.project:
return self._get_by_key(key, self.project)
project = self.module.params.get('project')
if not project:
return None
projects = self.cs.listProjects()
args = {}
args['listall'] = True
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
projects = self.cs.listProjects(**args)
if projects:
for p in projects['project']:
if project in [ p['name'], p['displaytext'], p['id'] ]:
self.project_id = p['id']
return self.project_id
self.project = p
return self._get_by_key(key, self.project)
self.module.fail_json(msg="project '%s' not found" % project)
# TODO: for backward compatibility only, remove if not used anymore
def get_ip_address_id(self):
if self.ip_address_id:
return self.ip_address_id
return self.get_ip_address(key='id')
def get_ip_address(self, key=None):
if self.ip_address:
return self._get_by_key(key, self.ip_address)
ip_address = self.module.params.get('ip_address')
if not ip_address:
@ -96,58 +149,78 @@ class AnsibleCloudStack:
args = {}
args['ipaddress'] = ip_address
args['projectid'] = self.get_project_id()
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['projectid'] = self.get_project(key='id')
ip_addresses = self.cs.listPublicIpAddresses(**args)
if not ip_addresses:
self.module.fail_json(msg="IP address '%s' not found" % args['ipaddress'])
self.ip_address_id = ip_addresses['publicipaddress'][0]['id']
return self.ip_address_id
self.ip_address = ip_addresses['publicipaddress'][0]
return self._get_by_key(key, self.ip_address)
# TODO: for backward compatibility only, remove if not used anymore
def get_vm_id(self):
if self.vm_id:
return self.vm_id
return self.get_vm(key='id')
def get_vm(self, key=None):
if self.vm:
return self._get_by_key(key, self.vm)
vm = self.module.params.get('vm')
if not vm:
self.module.fail_json(msg="Virtual machine param 'vm' is required")
args = {}
args['projectid'] = self.get_project_id()
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['projectid'] = self.get_project(key='id')
args['zoneid'] = self.get_zone(key='id')
vms = self.cs.listVirtualMachines(**args)
if vms:
for v in vms['virtualmachine']:
if vm in [ v['displayname'], v['name'], v['id'] ]:
self.vm_id = v['id']
return self.vm_id
if vm in [ v['name'], v['displayname'], v['id'] ]:
self.vm = v
return self._get_by_key(key, self.vm)
self.module.fail_json(msg="Virtual machine '%s' not found" % vm)
# TODO: for backward compatibility only, remove if not used anymore
def get_zone_id(self):
if self.zone_id:
return self.zone_id
return self.get_zone(key='id')
def get_zone(self, key=None):
if self.zone:
return self._get_by_key(key, self.zone)
zone = self.module.params.get('zone')
zones = self.cs.listZones()
# use the first zone if no zone param given
if not zone:
self.zone_id = zones['zone'][0]['id']
return self.zone_id
self.zone = zones['zone'][0]
return self._get_by_key(key, self.zone)
if zones:
for z in zones['zone']:
if zone in [ z['name'], z['id'] ]:
self.zone_id = z['id']
return self.zone_id
self.zone = z
return self._get_by_key(key, self.zone)
self.module.fail_json(msg="zone '%s' not found" % zone)
# TODO: for backward compatibility only, remove if not used anymore
def get_os_type_id(self):
if self.os_type_id:
return self.os_type_id
return self.get_os_type(key='id')
def get_os_type(self, key=None):
if self.os_type:
return self._get_by_key(key, self.zone)
os_type = self.module.params.get('os_type')
if not os_type:
@ -157,8 +230,8 @@ class AnsibleCloudStack:
if os_types:
for o in os_types['ostype']:
if os_type in [ o['description'], o['id'] ]:
self.os_type_id = o['id']
return self.os_type_id
self.os_type = o
return self._get_by_key(key, self.os_type)
self.module.fail_json(msg="OS type '%s' not found" % os_type)
@ -181,6 +254,112 @@ class AnsibleCloudStack:
self.module.fail_json(msg="Hypervisor '%s' not found" % hypervisor)
def get_account(self, key=None):
if self.account:
return self._get_by_key(key, self.account)
account = self.module.params.get('account')
if not account:
return None
domain = self.module.params.get('domain')
if not domain:
self.module.fail_json(msg="Account must be specified with Domain")
args = {}
args['name'] = account
args['domainid'] = self.get_domain(key='id')
args['listall'] = True
accounts = self.cs.listAccounts(**args)
if accounts:
self.account = accounts['account'][0]
return self._get_by_key(key, self.account)
self.module.fail_json(msg="Account '%s' not found" % account)
def get_domain(self, key=None):
if self.domain:
return self._get_by_key(key, self.domain)
domain = self.module.params.get('domain')
if not domain:
return None
args = {}
args['name'] = domain
args['listall'] = True
domain = self.cs.listDomains(**args)
if domains:
self.domain = domains['domain'][0]
return self._get_by_key(key, self.domain)
self.module.fail_json(msg="Domain '%s' not found" % domain)
def get_tags(self, resource=None):
existing_tags = self.cs.listTags(resourceid=resource['id'])
if existing_tags:
return existing_tags['tag']
return []
def _delete_tags(self, resource, resource_type, tags):
existing_tags = resource['tags']
tags_to_delete = []
for existing_tag in existing_tags:
if existing_tag['key'] in tags:
if existing_tag['value'] != tags[key]:
tags_to_delete.append(existing_tag)
else:
tags_to_delete.append(existing_tag)
if tags_to_delete:
self.result['changed'] = True
if not self.module.check_mode:
args = {}
args['resourceids'] = resource['id']
args['resourcetype'] = resource_type
args['tags'] = tags_to_delete
self.cs.deleteTags(**args)
def _create_tags(self, resource, resource_type, tags):
tags_to_create = []
for i, tag_entry in enumerate(tags):
tag = {
'key': tag_entry['key'],
'value': tag_entry['value'],
}
tags_to_create.append(tag)
if tags_to_create:
self.result['changed'] = True
if not self.module.check_mode:
args = {}
args['resourceids'] = resource['id']
args['resourcetype'] = resource_type
args['tags'] = tags_to_create
self.cs.createTags(**args)
def ensure_tags(self, resource, resource_type=None):
if not resource_type or not resource:
self.module.fail_json(msg="Error: Missing resource or resource_type for tags.")
if 'tags' in resource:
tags = self.module.params.get('tags')
if tags is not None:
self._delete_tags(resource, resource_type, tags)
self._create_tags(resource, resource_type, tags)
resource['tags'] = self.get_tags(resource)
return resource
def get_capabilities(self, key=None):
if self.capabilities:
return self._get_by_key(key, self.capabilities)
capabilities = self.cs.listCapabilities()
self.capabilities = capabilities['capability']
return self._get_by_key(key, self.capabilities)
# TODO: rename to poll_job()
def _poll_job(self, job=None, key=None):
if 'jobid' in job:
while True:

@ -1 +1 @@
Subproject commit 0341ddd35ed5ff477ad5de2488d947255ce86259
Subproject commit 85c8a892c80b92730831d95fa654ef6d35b0eca0

@ -1 +1 @@
Subproject commit 495ad450e53feb1cd26218dc68056cc34d1ea9ff
Subproject commit 2690f096a47646cd17db135648def88afc40d92c

View file

@ -0,0 +1,5 @@
[cloudstack]
#endpoint = https://api.exoscale.ch/compute
endpoint = https://cloud.example.com/client/api
key = cloudstack api key
secret = cloudstack api secret

232
plugins/inventory/cloudstack.py Executable file
View file

@ -0,0 +1,232 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible,
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
######################################################################
"""
Ansible CloudStack external inventory script.
=============================================
Generates Ansible inventory from CloudStack. Configuration is read from
'cloudstack.ini'. If you need to pass the project, write a simple wrapper
script, e.g. project_cloudstack.sh:
#!/bin/bash
cloudstack.py --project <your_project> $@
When run against a specific host, this script returns the following attributes
based on the data obtained from CloudStack API:
"web01": {
"cpu_number": 2,
"nic": [
{
"ip": "10.102.76.98",
"mac": "02:00:50:99:00:01",
"type": "Isolated",
"netmask": "255.255.255.0",
"gateway": "10.102.76.1"
},
{
"ip": "10.102.138.63",
"mac": "06:b7:5a:00:14:84",
"type": "Shared",
"netmask": "255.255.255.0",
"gateway": "10.102.138.1"
}
],
"default_ip": "10.102.76.98",
"zone": "ZUERICH",
"created": "2014-07-02T07:53:50+0200",
"hypervisor": "VMware",
"memory": 2048,
"state": "Running",
"tags": [],
"cpu_speed": 1800,
"affinity_group": [],
"service_offering": "Small",
"cpu_used": "62%"
}
usage: cloudstack.py [--list] [--host HOST] [--project PROJECT]
"""
import os
import sys
import argparse
try:
import json
except:
import simplejson as json
try:
from cs import CloudStack, CloudStackException, read_config
except ImportError:
print >> sys.stderr, "Error: CloudStack library must be installed: pip install cs."
sys.exit(1)
class CloudStackInventory(object):
def __init__(self):
parser = argparse.ArgumentParser()
parser.add_argument('--host')
parser.add_argument('--list', action='store_true')
parser.add_argument('--project')
options = parser.parse_args()
try:
self.cs = CloudStack(**read_config())
except CloudStackException, e:
print >> sys.stderr, "Error: Could not connect to CloudStack API"
project_id = ''
if options.project:
project_id = self.get_project_id(options.project)
if options.host:
data = self.get_host(options.host)
print json.dumps(data, indent=2)
elif options.list:
data = self.get_list()
print json.dumps(data, indent=2)
else:
print >> sys.stderr, "usage: --list | --host <hostname> [--project <project>]"
sys.exit(1)
def get_project_id(self, project):
projects = self.cs.listProjects()
if projects:
for p in projects['project']:
if p['name'] == project or p['id'] == project:
return p['id']
print >> sys.stderr, "Error: Project %s not found." % project
sys.exit(1)
def get_host(self, name, project_id=''):
hosts = self.cs.listVirtualMachines(projectid=project_id)
data = {}
if not hosts:
return data
for host in hosts['virtualmachine']:
host_name = host['displayname']
if name == host_name:
data['zone'] = host['zonename']
if 'group' in host:
data['group'] = host['group']
data['state'] = host['state']
data['service_offering'] = host['serviceofferingname']
data['affinity_group'] = host['affinitygroup']
data['security_group'] = host['securitygroup']
data['cpu_number'] = host['cpunumber']
data['cpu_speed'] = host['cpuspeed']
if 'cpuused' in host:
data['cpu_used'] = host['cpuused']
data['memory'] = host['memory']
data['tags'] = host['tags']
data['hypervisor'] = host['hypervisor']
data['created'] = host['created']
data['nic'] = []
for nic in host['nic']:
data['nic'].append({
'ip': nic['ipaddress'],
'mac': nic['macaddress'],
'netmask': nic['netmask'],
'gateway': nic['gateway'],
'type': nic['type'],
})
if nic['isdefault']:
data['default_ip'] = nic['ipaddress']
break;
return data
def get_list(self, project_id=''):
data = {
'all': {
'hosts': [],
},
'_meta': {
'hostvars': {},
},
}
groups = self.cs.listInstanceGroups(projectid=project_id)
if groups:
for group in groups['instancegroup']:
group_name = group['name']
if group_name and not group_name in data:
data[group_name] = {
'hosts': []
}
hosts = self.cs.listVirtualMachines(projectid=project_id)
if not hosts:
return data
for host in hosts['virtualmachine']:
host_name = host['displayname']
data['all']['hosts'].append(host_name)
data['_meta']['hostvars'][host_name] = {}
data['_meta']['hostvars'][host_name]['zone'] = host['zonename']
if 'group' in host:
data['_meta']['hostvars'][host_name]['group'] = host['group']
data['_meta']['hostvars'][host_name]['state'] = host['state']
data['_meta']['hostvars'][host_name]['service_offering'] = host['serviceofferingname']
data['_meta']['hostvars'][host_name]['affinity_group'] = host['affinitygroup']
data['_meta']['hostvars'][host_name]['security_group'] = host['securitygroup']
data['_meta']['hostvars'][host_name]['cpu_number'] = host['cpunumber']
data['_meta']['hostvars'][host_name]['cpu_speed'] = host['cpuspeed']
if 'cpuused' in host:
data['_meta']['hostvars'][host_name]['cpu_used'] = host['cpuused']
data['_meta']['hostvars'][host_name]['created'] = host['created']
data['_meta']['hostvars'][host_name]['memory'] = host['memory']
data['_meta']['hostvars'][host_name]['tags'] = host['tags']
data['_meta']['hostvars'][host_name]['hypervisor'] = host['hypervisor']
data['_meta']['hostvars'][host_name]['created'] = host['created']
data['_meta']['hostvars'][host_name]['nic'] = []
for nic in host['nic']:
data['_meta']['hostvars'][host_name]['nic'].append({
'ip': nic['ipaddress'],
'mac': nic['macaddress'],
'netmask': nic['netmask'],
'gateway': nic['gateway'],
'type': nic['type'],
})
if nic['isdefault']:
data['_meta']['hostvars'][host_name]['default_ip'] = nic['ipaddress']
group_name = ''
if 'group' in host:
group_name = host['group']
if group_name and group_name in data:
data[group_name]['hosts'].append(host_name)
return data
if __name__ == '__main__':
CloudStackInventory()

View file

@ -144,6 +144,11 @@ rackspace: $(CREDENTIALS_FILE)
CLOUD_RESOURCE_PREFIX="$(CLOUD_RESOURCE_PREFIX)" make rackspace_cleanup ; \
exit $$RC;
cloudstack:
ansible-playbook cloudstack.yml -i $(INVENTORY) -e @$(VARS_FILE) -e "resource_prefix=$(CLOUD_RESOURCE_PREFIX)" -v $(TEST_FLAGS) ; \
RC=$$? ; \
exit $$RC;
$(CONSUL_RUNNING):
consul:

View file

@ -0,0 +1,13 @@
---
- hosts: localhost
connection: local
gather_facts: no
tags:
- cloudstack
roles:
- { role: test_cs_sshkeypair, tags: test_cs_sshkeypair }
- { role: test_cs_affinitygroup, tags: test_cs_affinitygroup }
- { role: test_cs_securitygroup, tags: test_cs_securitygroup }
- { role: test_cs_securitygroup_rule, tags: test_cs_securitygroup_rule }
- { role: test_cs_instance, tags: test_cs_instance }
- { role: test_cs_instancegroup, tags: test_cs_instancegroup }

View file

@ -0,0 +1,3 @@
---
dependencies:
- test_cs_common

View file

@ -0,0 +1,58 @@
---
- name: setup
cs_affinitygroup: name={{ cs_resource_prefix }}_ag state=absent
register: ag
- name: verify setup
assert:
that:
- ag|success
- name: test fail if missing name
action: cs_affinitygroup
register: ag
ignore_errors: true
- name: verify results of fail if missing name
assert:
that:
- ag|failed
- ag.msg == "missing required arguments: name"
- name: test present affinity group
cs_affinitygroup: name={{ cs_resource_prefix }}_ag
register: ag
- name: verify results of create affinity group
assert:
that:
- ag|success
- ag|changed
- ag.name == "{{ cs_resource_prefix }}_ag"
- name: test present affinity group is idempotence
cs_affinitygroup: name={{ cs_resource_prefix }}_ag
register: ag
- name: verify results present affinity group is idempotence
assert:
that:
- ag|success
- not ag|changed
- ag.name == "{{ cs_resource_prefix }}_ag"
- name: test absent affinity group
cs_affinitygroup: name={{ cs_resource_prefix }}_ag state=absent
register: ag
- name: verify results of absent affinity group
assert:
that:
- ag|success
- ag|changed
- ag.name == "{{ cs_resource_prefix }}_ag"
- name: test absent affinity group is idempotence
cs_affinitygroup: name={{ cs_resource_prefix }}_ag state=absent
register: ag
- name: verify results of absent affinity group is idempotence
assert:
that:
- ag|success
- not ag|changed
- ag.name is undefined

View file

@ -0,0 +1,2 @@
---
cs_resource_prefix: cloudstack

View file

@ -0,0 +1,2 @@
---
instance_number: 1

View file

@ -0,0 +1,3 @@
---
dependencies:
- test_cs_common

View file

@ -0,0 +1,23 @@
---
- name: test destroy instance
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
state: absent
register: instance
- name: verify destroy instance
assert:
that:
- instance|success
- instance|changed
- instance.state == "Destroyed"
- name: test destroy instance idempotence
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
state: absent
register: instance
- name: verify destroy instance idempotence
assert:
that:
- instance|success
- not instance|changed

View file

@ -0,0 +1,36 @@
---
- name: cleanup ssh key
cs_sshkeypair: name={{ cs_resource_prefix }}-sshkey state=absent
register: sshkey
- name: verify cleanup ssh key
assert:
that:
- sshkey|success
- name: cleanup affinity group
cs_affinitygroup: name={{ cs_resource_prefix }}-ag state=absent
register: ag
until: ag|success
retries: 20
delay: 5
- name: verify cleanup affinity group
assert:
that:
- ag|success
- name: cleanup security group ...take a while unless instance is expunged
cs_securitygroup: name={{ cs_resource_prefix }}-sg state=absent
register: sg
until: sg|success
retries: 100
delay: 10
- name: verify cleanup security group
assert:
that:
- sg|success
# force expunge, only works with admin permissions
- cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
state: expunged
failed_when: false

View file

@ -0,0 +1,11 @@
---
- include: setup.yml
tags: any
- include: present.yml
tags: test_cs_instance_present
#- include: tags.yml
# tags: test_cs_instance_tags
- include: absent.yml
tags: test_cs_instance_absent
- include: cleanup.yml
tags: test_cs_instance_cleanup

View file

@ -0,0 +1,168 @@
---
- name: test create instance
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
template: Linux Debian 7 64-bit
service_offering: Tiny
affinity_group: "{{ cs_resource_prefix }}-ag"
security_group: "{{ cs_resource_prefix }}-sg"
ssh_key: "{{ cs_resource_prefix }}-sshkey"
tags: []
register: instance
- name: verify create instance
assert:
that:
- instance|success
- instance|changed
- instance.name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.display_name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.service_offering == "Tiny"
- instance.state == "Running"
- instance.ssh_key == "{{ cs_resource_prefix }}-sshkey"
- not instance.tags
- name: test create instance idempotence
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
template: Linux Debian 7 64-bit
service_offering: Tiny
affinity_group: "{{ cs_resource_prefix }}-ag"
security_group: "{{ cs_resource_prefix }}-sg"
ssh_key: "{{ cs_resource_prefix }}-sshkey"
tags: []
register: instance
- name: verify create instance idempotence
assert:
that:
- instance|success
- not instance|changed
- instance.name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.display_name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.service_offering == "Tiny"
- instance.state == "Running"
- instance.ssh_key == "{{ cs_resource_prefix }}-sshkey"
- not instance.tags
- name: test running instance not updated
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
service_offering: Micro
register: instance
- name: verify running instance not updated
assert:
that:
- instance|success
- not instance|changed
- instance.name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.display_name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.service_offering == "Tiny"
- instance.state == "Running"
- name: test stopping instance
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
state: stopped
register: instance
- name: verify stopping instance
assert:
that:
- instance|success
- instance|changed
- instance.name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.display_name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.service_offering == "Tiny"
- instance.state == "Stopped"
- name: test stopping instance idempotence
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
state: stopped
register: instance
- name: verify stopping instance idempotence
assert:
that:
- instance|success
- not instance|changed
- instance.state == "Stopped"
- name: test updating stopped instance
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
display_name: "{{ cs_resource_prefix }}-display-{{ instance_number }}"
service_offering: Micro
register: instance
- name: verify updating stopped instance
assert:
that:
- instance|success
- instance|changed
- instance.name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.display_name == "{{ cs_resource_prefix }}-display-{{ instance_number }}"
- instance.service_offering == "Micro"
- instance.state == "Stopped"
- name: test starting instance
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
state: started
register: instance
- name: verify starting instance
assert:
that:
- instance|success
- instance|changed
- instance.name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.display_name == "{{ cs_resource_prefix }}-display-{{ instance_number }}"
- instance.service_offering == "Micro"
- instance.state == "Running"
- name: test starting instance idempotence
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
state: started
register: instance
- name: verify starting instance idempotence
assert:
that:
- instance|success
- not instance|changed
- instance.state == "Running"
- name: test force update running instance
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
service_offering: Tiny
force: true
register: instance
- name: verify force update running instance
assert:
that:
- instance|success
- instance|changed
- instance.name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.display_name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.service_offering == "Tiny"
- instance.state == "Running"
- name: test force update running instance idempotence
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
service_offering: Tiny
force: true
register: instance
- name: verify force update running instance idempotence
assert:
that:
- instance|success
- not instance|changed
- instance.name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.display_name == "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
- instance.service_offering == "Tiny"
- instance.state == "Running"

View file

@ -0,0 +1,32 @@
---
- name: setup ssh key
cs_sshkeypair: name={{ cs_resource_prefix }}-sshkey
register: sshkey
- name: verify setup ssh key
assert:
that:
- sshkey|success
- name: setup affinity group
cs_affinitygroup: name={{ cs_resource_prefix }}-ag
register: ag
- name: verify setup affinity group
assert:
that:
- ag|success
- name: setup security group
cs_securitygroup: name={{ cs_resource_prefix }}-sg
register: sg
- name: verify setup security group
assert:
that:
- sg|success
- name: setup instance to be absent
cs_instance: name={{ cs_resource_prefix }}-vm-{{ instance_number }} state=absent
register: instance
- name: verify instance to be absent
assert:
that:
- instance|success

View file

@ -0,0 +1,82 @@
---
- name: test add tags to instance
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
tags:
- { key: "{{ cs_resource_prefix }}-tag1", value: "{{ cs_resource_prefix }}-value1" }
- { key: "{{ cs_resource_prefix }}-tag2", value: "{{ cs_resource_prefix }}-value2" }
register: instance
- name: verify add tags to instance
assert:
that:
- instance|success
- instance|changed
- instance.tags|length == 2
- instance.tags[0]['key'] == "{{ cs_resource_prefix }}-tag1"
- instance.tags[1]['key'] == "{{ cs_resource_prefix }}-tag2"
- instance.tags[0]['value'] == "{{ cs_resource_prefix }}-value1"
- instance.tags[1]['value'] == "{{ cs_resource_prefix }}-value2"
- name: test tags to instance idempotence
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
tags:
- { key: "{{ cs_resource_prefix }}-tag1", value: "{{ cs_resource_prefix }}-value1" }
- { key: "{{ cs_resource_prefix }}-tag2", value: "{{ cs_resource_prefix }}-value2" }
register: instance
- name: verify tags to instance idempotence
assert:
that:
- instance|success
- not instance|changed
- instance.tags|length == 2
- instance.tags[0]['key'] == "{{ cs_resource_prefix }}-tag1"
- instance.tags[1]['key'] == "{{ cs_resource_prefix }}-tag2"
- instance.tags[0]['value'] == "{{ cs_resource_prefix }}-value1"
- instance.tags[1]['value'] == "{{ cs_resource_prefix }}-value2"
- name: test change tags of instance
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
tags:
- { key: "{{ cs_resource_prefix }}-tag2", value: "{{ cs_resource_prefix }}-value2" }
- { key: "{{ cs_resource_prefix }}-tag3", value: "{{ cs_resource_prefix }}-value3" }
register: instance
- name: verify tags to instance idempotence
assert:
that:
- instance|success
- not instance|changed
- instance.tags|length == 2
- instance.tags[0]['key'] == "{{ cs_resource_prefix }}-tag1"
- instance.tags[1]['key'] == "{{ cs_resource_prefix }}-tag3"
- instance.tags[0]['value'] == "{{ cs_resource_prefix }}-value1"
- instance.tags[1]['value'] == "{{ cs_resource_prefix }}-value3"
- name: test not touch tags of instance if no param tags
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
register: instance
- name: verify not touch tags of instance if no param tags
assert:
that:
- instance|success
- not instance|changed
- instance.tags|length == 2
- instance.tags[0]['key'] == "{{ cs_resource_prefix }}-tag1"
- instance.tags[1]['key'] == "{{ cs_resource_prefix }}-tag3"
- instance.tags[0]['value'] == "{{ cs_resource_prefix }}-value1"
- instance.tags[1]['value'] == "{{ cs_resource_prefix }}-value3"
- name: test remove tags
cs_instance:
name: "{{ cs_resource_prefix }}-vm-{{ instance_number }}"
tags: []
register: instance
- name: verify remove tags
assert:
that:
- instance|success
- not instance|changed
- instance.tags|length == 0

View file

@ -0,0 +1,3 @@
---
dependencies:
- test_cs_common

View file

@ -0,0 +1,58 @@
---
- name: setup
cs_instancegroup: name={{ cs_resource_prefix }}_ig state=absent
register: ig
- name: verify setup
assert:
that:
- ig|success
- name: test fail if missing name
action: cs_instancegroup
register: ig
ignore_errors: true
- name: verify results of fail if missing name
assert:
that:
- ig|failed
- ig.msg == "missing required arguments: name"
- name: test present instance group
cs_instancegroup: name={{ cs_resource_prefix }}_ig
register: ig
- name: verify results of create instance group
assert:
that:
- ig|success
- ig|changed
- ig.name == "{{ cs_resource_prefix }}_ig"
- name: test present instance group is idempotence
cs_instancegroup: name={{ cs_resource_prefix }}_ig
register: ig
- name: verify results present instance group is idempotence
assert:
that:
- ig|success
- not ig|changed
- ig.name == "{{ cs_resource_prefix }}_ig"
- name: test absent instance group
cs_instancegroup: name={{ cs_resource_prefix }}_ig state=absent
register: ig
- name: verify results of absent instance group
assert:
that:
- ig|success
- ig|changed
- ig.name == "{{ cs_resource_prefix }}_ig"
- name: test absent instance group is idempotence
cs_instancegroup: name={{ cs_resource_prefix }}_ig state=absent
register: ig
- name: verify results of absent instance group is idempotence
assert:
that:
- ig|success
- not ig|changed
- ig.name is undefined

View file

@ -0,0 +1,3 @@
---
dependencies:
- test_cs_common

View file

@ -0,0 +1,58 @@
---
- name: setup
cs_securitygroup: name={{ cs_resource_prefix }}_sg state=absent
register: sg
- name: verify setup
assert:
that:
- sg|success
- name: test fail if missing name
action: cs_securitygroup
register: sg
ignore_errors: true
- name: verify results of fail if missing name
assert:
that:
- sg|failed
- sg.msg == "missing required arguments: name"
- name: test present security group
cs_securitygroup: name={{ cs_resource_prefix }}_sg
register: sg
- name: verify results of create security group
assert:
that:
- sg|success
- sg|changed
- sg.name == "{{ cs_resource_prefix }}_sg"
- name: test present security group is idempotence
cs_securitygroup: name={{ cs_resource_prefix }}_sg
register: sg
- name: verify results present security group is idempotence
assert:
that:
- sg|success
- not sg|changed
- sg.name == "{{ cs_resource_prefix }}_sg"
- name: test absent security group
cs_securitygroup: name={{ cs_resource_prefix }}_sg state=absent
register: sg
- name: verify results of absent security group
assert:
that:
- sg|success
- sg|changed
- sg.name == "{{ cs_resource_prefix }}_sg"
- name: test absent security group is idempotence
cs_securitygroup: name={{ cs_resource_prefix }}_sg state=absent
register: sg
- name: verify results of absent security group is idempotence
assert:
that:
- sg|success
- not sg|changed
- sg.name is undefined

View file

@ -0,0 +1,3 @@
---
dependencies:
- test_cs_common

View file

@ -0,0 +1,105 @@
- name: test remove http range rule
cs_securitygroup_rule:
security_group: default
start_port: 8000
end_port: 8888
cidr: 1.2.3.4/32
state: absent
register: sg_rule
- name: verify create http range rule
assert:
that:
- sg_rule|success
- sg_rule|changed
- sg_rule.type == 'ingress'
- sg_rule.security_group == 'default'
- sg_rule.protocol == 'tcp'
- sg_rule.start_port == 8000
- sg_rule.end_port == 8888
- sg_rule.cidr == '1.2.3.4/32'
- name: test remove http range rule idempotence
cs_securitygroup_rule:
security_group: default
start_port: 8000
end_port: 8888
cidr: 1.2.3.4/32
state: absent
register: sg_rule
- name: verify create http range rule idempotence
assert:
that:
- sg_rule|success
- not sg_rule|changed
- name: test remove single port udp rule
cs_securitygroup_rule:
security_group: default
port: 5353
protocol: udp
type: egress
user_security_group: '{{ cs_resource_prefix }}_sg'
state: absent
register: sg_rule
- name: verify remove single port udp rule
assert:
that:
- sg_rule|success
- sg_rule|changed
- sg_rule.type == 'egress'
- sg_rule.security_group == 'default'
- sg_rule.protocol == 'udp'
- sg_rule.start_port == 5353
- sg_rule.end_port == 5353
- sg_rule.user_security_group == '{{ cs_resource_prefix }}_sg'
- name: test remove single port udp rule idempotence
cs_securitygroup_rule:
security_group: default
port: 5353
protocol: udp
type: egress
user_security_group: '{{ cs_resource_prefix }}_sg'
state: absent
register: sg_rule
- name: verify remove single port udp rule idempotence
assert:
that:
- sg_rule|success
- not sg_rule|changed
- name: test remove icmp rule
cs_securitygroup_rule:
security_group: default
protocol: icmp
type: ingress
icmp_type: -1
icmp_code: -1
state: absent
register: sg_rule
- name: verify icmp rule
assert:
that:
- sg_rule|success
- sg_rule|changed
- sg_rule.type == 'ingress'
- sg_rule.security_group == 'default'
- sg_rule.cidr == '0.0.0.0/0'
- sg_rule.protocol == 'icmp'
- sg_rule.icmp_code == -1
- sg_rule.icmp_type == -1
- name: test remove icmp rule idempotence
cs_securitygroup_rule:
security_group: default
protocol: icmp
type: ingress
icmp_type: -1
icmp_code: -1
state: absent
register: sg_rule
- name: verify icmp rule idempotence
assert:
that:
- sg_rule|success
- not sg_rule|changed

View file

@ -0,0 +1,7 @@
- name: cleanup custom security group
cs_securitygroup: name={{ cs_resource_prefix }}_sg state=absent
register: sg
- name: verify setup
assert:
that:
- sg|success

View file

@ -0,0 +1,4 @@
- include: setup.yml
- include: present.yml
- include: absent.yml
- include: cleanup.yml

View file

@ -0,0 +1,118 @@
- name: test create http range rule
cs_securitygroup_rule:
security_group: default
start_port: 8000
end_port: 8888
cidr: 1.2.3.4/32
register: sg_rule
- name: verify create http range rule
assert:
that:
- sg_rule|success
- sg_rule|changed
- sg_rule.type == 'ingress'
- sg_rule.security_group == 'default'
- sg_rule.protocol == 'tcp'
- sg_rule.start_port == 8000
- sg_rule.end_port == 8888
- sg_rule.cidr == '1.2.3.4/32'
- name: test create http range rule idempotence
cs_securitygroup_rule:
security_group: default
start_port: 8000
end_port: 8888
cidr: 1.2.3.4/32
register: sg_rule
- name: verify create http range rule idempotence
assert:
that:
- sg_rule|success
- not sg_rule|changed
- sg_rule.type == 'ingress'
- sg_rule.security_group == 'default'
- sg_rule.protocol == 'tcp'
- sg_rule.start_port == 8000
- sg_rule.end_port == 8888
- sg_rule.cidr == '1.2.3.4/32'
- name: test create single port udp rule
cs_securitygroup_rule:
security_group: default
port: 5353
protocol: udp
type: egress
user_security_group: '{{ cs_resource_prefix }}_sg'
register: sg_rule
- name: verify create single port udp rule
assert:
that:
- sg_rule|success
- sg_rule|changed
- sg_rule.type == 'egress'
- sg_rule.security_group == 'default'
- sg_rule.protocol == 'udp'
- sg_rule.start_port == 5353
- sg_rule.end_port == 5353
- sg_rule.user_security_group == '{{ cs_resource_prefix }}_sg'
- name: test single port udp rule idempotence
cs_securitygroup_rule:
security_group: default
port: 5353
protocol: udp
type: egress
user_security_group: '{{ cs_resource_prefix }}_sg'
register: sg_rule
- name: verify single port udp rule idempotence
assert:
that:
- sg_rule|success
- not sg_rule|changed
- sg_rule.type == 'egress'
- sg_rule.security_group == 'default'
- sg_rule.protocol == 'udp'
- sg_rule.start_port == 5353
- sg_rule.end_port == 5353
- sg_rule.user_security_group == '{{ cs_resource_prefix }}_sg'
- name: test icmp rule
cs_securitygroup_rule:
security_group: default
protocol: icmp
type: ingress
icmp_type: -1
icmp_code: -1
register: sg_rule
- name: verify icmp rule
assert:
that:
- sg_rule|success
- sg_rule|changed
- sg_rule.type == 'ingress'
- sg_rule.security_group == 'default'
- sg_rule.cidr == '0.0.0.0/0'
- sg_rule.protocol == 'icmp'
- sg_rule.icmp_code == -1
- sg_rule.icmp_type == -1
- name: test icmp rule idempotence
cs_securitygroup_rule:
security_group: default
protocol: icmp
type: ingress
icmp_type: -1
icmp_code: -1
register: sg_rule
- name: verify icmp rule idempotence
assert:
that:
- sg_rule|success
- not sg_rule|changed
- sg_rule.type == 'ingress'
- sg_rule.security_group == 'default'
- sg_rule.cidr == '0.0.0.0/0'
- sg_rule.protocol == 'icmp'
- sg_rule.icmp_code == -1
- sg_rule.icmp_type == -1

View file

@ -0,0 +1,56 @@
- name: setup custom security group
cs_securitygroup: name={{ cs_resource_prefix }}_sg
register: sg
- name: verify setup
assert:
that:
- sg|success
- name: setup default security group
cs_securitygroup: name=default
register: sg
- name: verify setup
assert:
that:
- sg|success
- name: setup remove icmp rule
cs_securitygroup_rule:
security_group: default
protocol: icmp
type: ingress
icmp_type: -1
icmp_code: -1
state: absent
register: sg_rule
- name: verify remove icmp rule
assert:
that:
- sg_rule|success
- name: setup remove http range rule
cs_securitygroup_rule:
security_group: default
start_port: 8000
end_port: 8888
cidr: 1.2.3.4/32
state: absent
register: sg_rule
- name: verify remove http range rule
assert:
that:
- sg_rule|success
- name: setup remove single port udp rule
cs_securitygroup_rule:
security_group: default
port: 5353
protocol: udp
type: egress
user_security_group: '{{ cs_resource_prefix }}-user-sg'
state: absent
register: sg_rule
- name: verify remove single port udp rule
assert:
that:
- sg_rule|success

View file

@ -0,0 +1,3 @@
---
dependencies:
- test_cs_common

View file

@ -0,0 +1,89 @@
---
- name: setup cleanup
cs_sshkeypair: name={{ cs_resource_prefix }}-sshkey state=absent
- name: test fail on missing name
action: cs_sshkeypair
ignore_errors: true
register: sshkey
- name: verify results of fail on missing name
assert:
that:
- sshkey|failed
- sshkey.msg == "missing required arguments: name"
- name: test ssh key creation
cs_sshkeypair: name={{ cs_resource_prefix }}-sshkey
register: sshkey
- name: verify results of ssh key creation
assert:
that:
- sshkey|success
- sshkey|changed
- sshkey.fingerprint is defined and sshkey.fingerprint != ""
- sshkey.private_key is defined and sshkey.private_key != ""
- sshkey.name == "{{ cs_resource_prefix }}-sshkey"
- name: test ssh key creation idempotence
cs_sshkeypair: name={{ cs_resource_prefix }}-sshkey
register: sshkey2
- name: verify results of ssh key creation idempotence
assert:
that:
- sshkey2|success
- not sshkey2|changed
- sshkey2.fingerprint is defined and sshkey2.fingerprint == sshkey.fingerprint
- sshkey2.private_key is not defined
- sshkey2.name == "{{ cs_resource_prefix }}-sshkey"
- name: test replace ssh public key
cs_sshkeypair: |
name={{ cs_resource_prefix }}-sshkey
public_key="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsTI7KJZ8tz/CwQIrSol41c6s3vzkGYCMI8o7P9Et48UG9eRoGaMaGYaTvBTj/VQrD7cfurI6Bn0HTT3FLK3OHOweyelm9rIiQ2hjkSl+2lIKWHu992GO58E5Gcy9yYW4sHGgGLNZkPBKrrj0w7lhmiHjPtVnf+2+7Ix1WOO2/HXPcAHhsX/AlyItDewIL4mr/BT83vq0202sPCiM2cFQJl+5WGwS1wYYK8d167cspsmdyX7OyAFCUB0vueuqjE8MFqJvyIJR9y8Lj9Ny71pSV5/QWrXUgELxMYOKSby3gHkxcIXgYBMFLl4DipRTO74OWQlRRaOlqXlOOQbikcY4T rene.moser@swisstxt.ch"
register: sshkey3
- name: verify results of replace ssh public key
assert:
that:
- sshkey3|success
- sshkey3|changed
- sshkey3.fingerprint is defined and sshkey3.fingerprint != sshkey2.fingerprint
- sshkey3.private_key is not defined
- sshkey3.name == "{{ cs_resource_prefix }}-sshkey"
- name: test replace ssh public key idempotence
cs_sshkeypair: |
name={{ cs_resource_prefix }}-sshkey
public_key="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsTI7KJZ8tz/CwQIrSol41c6s3vzkGYCMI8o7P9Et48UG9eRoGaMaGYaTvBTj/VQrD7cfurI6Bn0HTT3FLK3OHOweyelm9rIiQ2hjkSl+2lIKWHu992GO58E5Gcy9yYW4sHGgGLNZkPBKrrj0w7lhmiHjPtVnf+2+7Ix1WOO2/HXPcAHhsX/AlyItDewIL4mr/BT83vq0202sPCiM2cFQJl+5WGwS1wYYK8d167cspsmdyX7OyAFCUB0vueuqjE8MFqJvyIJR9y8Lj9Ny71pSV5/QWrXUgELxMYOKSby3gHkxcIXgYBMFLl4DipRTO74OWQlRRaOlqXlOOQbikcY4T rene.moser@swisstxt.ch"
register: sshkey4
- name: verify results of ssh public key idempotence
assert:
that:
- sshkey4|success
- not sshkey4|changed
- sshkey4.fingerprint is defined and sshkey4.fingerprint == sshkey3.fingerprint
- sshkey4.private_key is not defined
- sshkey4.name == "{{ cs_resource_prefix }}-sshkey"
- name: test ssh key absent
cs_sshkeypair: name={{ cs_resource_prefix }}-sshkey state=absent
register: sshkey5
- name: verify result of key absent
assert:
that:
- sshkey5|success
- sshkey5|changed
- sshkey5.fingerprint is defined and sshkey5.fingerprint == sshkey3.fingerprint
- sshkey5.private_key is not defined
- sshkey5.name == "{{ cs_resource_prefix }}-sshkey"
- name: test ssh key absent idempotence
cs_sshkeypair: name={{ cs_resource_prefix }}-sshkey state=absent
register: sshkey6
- name: verify result of ssh key absent idempotence
assert:
that:
- sshkey6|success
- not sshkey6|changed
- sshkey6.fingerprint is not defined
- sshkey6.private_key is not defined
- sshkey6.name is not defined

View file

@ -355,4 +355,22 @@
that:
- "result.stat.checksum == '73b271c2cc1cef5663713bc0f00444b4bf9f4543'"
- name: insert a line into the quoted file with many double quotation strings
lineinfile: dest={{output_dir}}/test_quoting.txt line="\"quote\" and \"unquote\""
register: result
- name: assert that the quoted file was changed
assert:
that:
- result.changed
- name: stat the quote test file
stat: path={{output_dir}}/test_quoting.txt
register: result
- name: assert test checksum matches after backref line was replaced
assert:
that:
- "result.stat.checksum == 'b10ab2a3c3b6492680c8d0b1d6f35aa6b8f9e731'"
###################################################################