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

cloudstack migrated to dedicated collection ngine_io.cloudstack (#173)

* cloudstack migrated to dedicated collection ngine_io.cloudstack

* remove leftovers

* remove more leftovers
This commit is contained in:
René Moser 2020-04-14 07:33:10 +02:00 committed by GitHub
parent 6ccf3682ac
commit ec52007c8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
312 changed files with 0 additions and 37203 deletions

41
.github/BOTMETA.yml vendored
View file

@ -129,9 +129,6 @@ files:
$doc_fragments/avi.py:
$doc_fragments/ce.py:
$doc_fragments/cloudscale.py:
$doc_fragments/cloudstack.py:
maintainers: $team_cloudstack
labels: cloudstack
$doc_fragments/docker.py:
maintainers: $team_docker
labels: cloud docker
@ -227,9 +224,6 @@ files:
$module_utils/cloudscale.py:
maintainers: $team_cloudscale
labels: cloudscale
$module_utils/cloudstack.py:
maintainers: $team_cloudstack
labels: cloudstack
$module_utils/docker/:
maintainers: $team_docker
labels: cloud
@ -402,40 +396,6 @@ files:
maintainers: gaudenz
$modules/cloud/cloudscale/cloudscale_volume.py:
authors: gaudenz href resmo
$modules/cloud/cloudstack/:
authors: resmo
maintainers: $team_cloudstack
$modules/cloud/cloudstack/cs_disk_offering.py:
authors: dpassante resmo
maintainers: rhtyd
$modules/cloud/cloudstack/cs_image_store.py:
authors: PatTheSilent
$modules/cloud/cloudstack/cs_instance_nic.py:
authors: marcaurele resmo
$modules/cloud/cloudstack/cs_instance_password_reset.py:
authors: onitake
$modules/cloud/cloudstack/cs_ip_address.py:
authors: dazworrall resmo
$modules/cloud/cloudstack/cs_network_offering.py:
authors: dpassante
maintainers: rhtyd
$modules/cloud/cloudstack/cs_physical_network.py:
authors: PatTheSilent netservers
$modules/cloud/cloudstack/cs_role_permission.py:
authors: dpassante
maintainers: rhtyd
$modules/cloud/cloudstack/cs_storage_pool.py:
authors: netservers resmo
$modules/cloud/cloudstack/cs_traffic_type.py:
authors: PatTheSilent
$modules/cloud/cloudstack/cs_vlan_ip_range.py:
authors: dpassante
maintainers: rhtyd
$modules/cloud/cloudstack/cs_volume.py:
authors: jeffersongirao resmo
$modules/cloud/cloudstack/cs_vpc_offering.py:
authors: dpassante
maintainers: rhtyd
$modules/cloud/digital_ocean/_digital_ocean.py:
authors: zbal
$modules/cloud/digital_ocean/:
@ -2178,7 +2138,6 @@ macros:
team_avi: ericsysmin grastogi23 khaltore
team_bsd: JoergFiedler MacLemon bcoca dch jasperla mekanix opoplawski overhacked tuxillo
team_cloudscale: gaudenz resmo
team_cloudstack: dpassante rhtyd
team_cyberark_conjur: jvanderhoof ryanprior
team_digital_ocean: BondAnthony mgregson
team_docker: DBendit WojciechowskiPiotr akshay196 danihodovic dariko felixfontein jwitko kassiansun tbouvet

View file

@ -4,14 +4,6 @@ plugin_routing:
deprecation:
removal_date: TBD
warning_text: see plugin documentation for details
cs_instance_facts:
deprecation:
removal_date: TBD
warning_text: see plugin documentation for details
cs_zone_facts:
deprecation:
removal_date: TBD
warning_text: see plugin documentation for details
digital_ocean:
deprecation:
removal_date: TBD

View file

@ -1,71 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class ModuleDocFragment(object):
# Standard cloudstack documentation fragment
DOCUMENTATION = r'''
options:
api_key:
description:
- API key of the CloudStack API.
- If not given, the C(CLOUDSTACK_KEY) env variable is considered.
- As the last option, the value is taken from the ini config file, also see the notes.
type: str
api_secret:
description:
- Secret key of the CloudStack API.
- If not set, the C(CLOUDSTACK_SECRET) env variable is considered.
- As the last option, the value is taken from the ini config file, also see the notes.
type: str
api_url:
description:
- URL of the CloudStack API e.g. https://cloud.example.com/client/api.
- If not given, the C(CLOUDSTACK_ENDPOINT) env variable is considered.
- As the last option, the value is taken from the ini config file, also see the notes.
type: str
api_http_method:
description:
- HTTP method used to query the API endpoint.
- If not given, the C(CLOUDSTACK_METHOD) env variable is considered.
- As the last option, the value is taken from the ini config file, also see the notes.
- Fallback value is C(get) if not specified.
type: str
choices: [ get, post ]
api_timeout:
description:
- HTTP timeout in seconds.
- If not given, the C(CLOUDSTACK_TIMEOUT) env variable is considered.
- As the last option, the value is taken from the ini config file, also see the notes.
- Fallback value is 10 seconds if not specified.
type: int
api_region:
description:
- Name of the ini section in the C(cloustack.ini) file.
- If not given, the C(CLOUDSTACK_REGION) env variable is considered.
type: str
default: cloudstack
requirements:
- python >= 2.6
- cs >= 0.6.10
notes:
- Ansible uses the C(cs) library's configuration method if credentials are not
provided by the arguments C(api_url), C(api_key), C(api_secret).
Configuration is read from several locations, in the following order.
The C(CLOUDSTACK_ENDPOINT), C(CLOUDSTACK_KEY), C(CLOUDSTACK_SECRET) and
C(CLOUDSTACK_METHOD). C(CLOUDSTACK_TIMEOUT) environment variables.
A C(CLOUDSTACK_CONFIG) environment variable pointing to an C(.ini) file.
A C(cloudstack.ini) file in the current working directory.
A C(.cloudstack.ini) file in the users home directory.
Optionally multiple credentials and endpoints can be specified using ini sections in C(cloudstack.ini).
Use the argument C(api_region) to select the section name, default section is C(cloudstack).
See https://github.com/exoscale/cs for more information.
- A detailed guide about cloudstack modules can be found in the L(CloudStack Cloud Guide,../scenario_guides/guide_cloudstack.html).
- This module supports check mode.
'''

View file

@ -1,664 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, René Moser <mail@renemoser.net>
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import os
import sys
import time
import traceback
from ansible.module_utils._text import to_text, to_native
from ansible.module_utils.basic import missing_required_lib
CS_IMP_ERR = None
try:
from cs import CloudStack, CloudStackException, read_config
HAS_LIB_CS = True
except ImportError:
CS_IMP_ERR = traceback.format_exc()
HAS_LIB_CS = False
if sys.version_info > (3,):
long = int
def cs_argument_spec():
return dict(
api_key=dict(default=os.environ.get('CLOUDSTACK_KEY')),
api_secret=dict(default=os.environ.get('CLOUDSTACK_SECRET'), no_log=True),
api_url=dict(default=os.environ.get('CLOUDSTACK_ENDPOINT')),
api_http_method=dict(choices=['get', 'post'], default=os.environ.get('CLOUDSTACK_METHOD')),
api_timeout=dict(type='int', default=os.environ.get('CLOUDSTACK_TIMEOUT')),
api_region=dict(default=os.environ.get('CLOUDSTACK_REGION') or 'cloudstack'),
)
def cs_required_together():
return [['api_key', 'api_secret']]
class AnsibleCloudStack:
def __init__(self, module):
if not HAS_LIB_CS:
module.fail_json(msg=missing_required_lib('cs'), exception=CS_IMP_ERR)
self.result = {
'changed': False,
'diff': {
'before': dict(),
'after': dict()
}
}
# Common returns, will be merged with self.returns
# search_for_key: replace_with_key
self.common_returns = {
'id': 'id',
'name': 'name',
'created': 'created',
'zonename': 'zone',
'state': 'state',
'project': 'project',
'account': 'account',
'domain': 'domain',
'displaytext': 'display_text',
'displayname': 'display_name',
'description': 'description',
}
# Init returns dict for use in subclasses
self.returns = {}
# these values will be casted to int
self.returns_to_int = {}
# these keys will be compared case sensitive in self.has_changed()
self.case_sensitive_keys = [
'id',
'displaytext',
'displayname',
'description',
]
self.module = module
self._cs = None
# Helper for VPCs
self._vpc_networks_ids = None
self.domain = None
self.account = None
self.project = None
self.ip_address = None
self.network = None
self.physical_network = None
self.vpc = None
self.zone = None
self.vm = None
self.vm_default_nic = None
self.os_type = None
self.hypervisor = None
self.capabilities = None
self.network_acl = None
@property
def cs(self):
if self._cs is None:
api_config = self.get_api_config()
self._cs = CloudStack(**api_config)
return self._cs
def get_api_config(self):
api_region = self.module.params.get('api_region') or os.environ.get('CLOUDSTACK_REGION')
try:
config = read_config(api_region)
except KeyError:
config = {}
api_config = {
'endpoint': self.module.params.get('api_url') or config.get('endpoint'),
'key': self.module.params.get('api_key') or config.get('key'),
'secret': self.module.params.get('api_secret') or config.get('secret'),
'timeout': self.module.params.get('api_timeout') or config.get('timeout') or 10,
'method': self.module.params.get('api_http_method') or config.get('method') or 'get',
}
self.result.update({
'api_region': api_region,
'api_url': api_config['endpoint'],
'api_key': api_config['key'],
'api_timeout': int(api_config['timeout']),
'api_http_method': api_config['method'],
})
if not all([api_config['endpoint'], api_config['key'], api_config['secret']]):
self.fail_json(msg="Missing api credentials: can not authenticate")
return api_config
def fail_json(self, **kwargs):
self.result.update(kwargs)
self.module.fail_json(**self.result)
def get_or_fallback(self, key=None, fallback_key=None):
value = self.module.params.get(key)
if not value:
value = self.module.params.get(fallback_key)
return value
def has_changed(self, want_dict, current_dict, only_keys=None, skip_diff_for_keys=None):
result = False
for key, value in want_dict.items():
# 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:
if isinstance(value, (int, float, long, complex)):
# ensure we compare the same type
if isinstance(value, int):
current_dict[key] = int(current_dict[key])
elif isinstance(value, float):
current_dict[key] = float(current_dict[key])
elif isinstance(value, long):
current_dict[key] = long(current_dict[key])
elif isinstance(value, complex):
current_dict[key] = complex(current_dict[key])
if value != current_dict[key]:
if skip_diff_for_keys and key not in skip_diff_for_keys:
self.result['diff']['before'][key] = current_dict[key]
self.result['diff']['after'][key] = value
result = True
else:
before_value = to_text(current_dict[key])
after_value = to_text(value)
if self.case_sensitive_keys and key in self.case_sensitive_keys:
if before_value != after_value:
if skip_diff_for_keys and key not in skip_diff_for_keys:
self.result['diff']['before'][key] = before_value
self.result['diff']['after'][key] = after_value
result = True
# Test for diff in case insensitive way
elif before_value.lower() != after_value.lower():
if skip_diff_for_keys and key not in skip_diff_for_keys:
self.result['diff']['before'][key] = before_value
self.result['diff']['after'][key] = after_value
result = True
else:
if skip_diff_for_keys and key not in skip_diff_for_keys:
self.result['diff']['before'][key] = None
self.result['diff']['after'][key] = to_text(value)
result = True
return result
def _get_by_key(self, key=None, my_dict=None):
if my_dict is None:
my_dict = {}
if key:
if key in my_dict:
return my_dict[key]
self.fail_json(msg="Something went wrong: %s not found" % key)
return my_dict
def query_api(self, command, **args):
try:
res = getattr(self.cs, command)(**args)
if 'errortext' in res:
self.fail_json(msg="Failed: '%s'" % res['errortext'])
except CloudStackException as e:
self.fail_json(msg='CloudStackException: %s' % to_native(e))
except Exception as e:
self.fail_json(msg=to_native(e))
return res
def get_network_acl(self, key=None):
if self.network_acl is None:
args = {
'name': self.module.params.get('network_acl'),
'vpcid': self.get_vpc(key='id'),
}
network_acls = self.query_api('listNetworkACLLists', **args)
if network_acls:
self.network_acl = network_acls['networkacllist'][0]
self.result['network_acl'] = self.network_acl['name']
if self.network_acl:
return self._get_by_key(key, self.network_acl)
else:
self.fail_json(msg="Network ACL %s not found" % self.module.params.get('network_acl'))
def get_vpc(self, key=None):
"""Return a VPC dictionary or the value of given key of."""
if self.vpc:
return self._get_by_key(key, self.vpc)
vpc = self.module.params.get('vpc')
if not vpc:
vpc = os.environ.get('CLOUDSTACK_VPC')
if not vpc:
return None
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'zoneid': self.get_zone(key='id'),
}
vpcs = self.query_api('listVPCs', **args)
if not vpcs:
self.fail_json(msg="No VPCs available.")
for v in vpcs['vpc']:
if vpc in [v['name'], v['displaytext'], v['id']]:
# Fail if the identifyer matches more than one VPC
if self.vpc:
self.fail_json(msg="More than one VPC found with the provided identifyer '%s'" % vpc)
else:
self.vpc = v
self.result['vpc'] = v['name']
if self.vpc:
return self._get_by_key(key, self.vpc)
self.fail_json(msg="VPC '%s' not found" % vpc)
def is_vpc_network(self, network_id):
"""Returns True if network is in VPC."""
# This is an efficient way to query a lot of networks at a time
if self._vpc_networks_ids is None:
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'zoneid': self.get_zone(key='id'),
}
vpcs = self.query_api('listVPCs', **args)
self._vpc_networks_ids = []
if vpcs:
for vpc in vpcs['vpc']:
for n in vpc.get('network', []):
self._vpc_networks_ids.append(n['id'])
return network_id in self._vpc_networks_ids
def get_physical_network(self, key=None):
if self.physical_network:
return self._get_by_key(key, self.physical_network)
physical_network = self.module.params.get('physical_network')
args = {
'zoneid': self.get_zone(key='id')
}
physical_networks = self.query_api('listPhysicalNetworks', **args)
if not physical_networks:
self.fail_json(msg="No physical networks available.")
for net in physical_networks['physicalnetwork']:
if physical_network in [net['name'], net['id']]:
self.physical_network = net
self.result['physical_network'] = net['name']
return self._get_by_key(key, self.physical_network)
self.fail_json(msg="Physical Network '%s' not found" % physical_network)
def get_network(self, key=None):
"""Return a network dictionary or the value of given key of."""
if self.network:
return self._get_by_key(key, self.network)
network = self.module.params.get('network')
if not network:
vpc_name = self.get_vpc(key='name')
if vpc_name:
self.fail_json(msg="Could not find network for VPC '%s' due missing argument: network" % vpc_name)
return None
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'zoneid': self.get_zone(key='id'),
'vpcid': self.get_vpc(key='id')
}
networks = self.query_api('listNetworks', **args)
if not networks:
self.fail_json(msg="No networks available.")
for n in networks['network']:
# ignore any VPC network if vpc param is not given
if 'vpcid' in n and not self.get_vpc(key='id'):
continue
if network in [n['displaytext'], n['name'], n['id']]:
self.result['network'] = n['name']
self.network = n
return self._get_by_key(key, self.network)
self.fail_json(msg="Network '%s' not found" % network)
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:
project = os.environ.get('CLOUDSTACK_PROJECT')
if not project:
return None
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id')
}
projects = self.query_api('listProjects', **args)
if projects:
for p in projects['project']:
if project.lower() in [p['name'].lower(), p['id']]:
self.result['project'] = p['name']
self.project = p
return self._get_by_key(key, self.project)
self.fail_json(msg="project '%s' not found" % project)
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:
self.fail_json(msg="IP address param 'ip_address' is required")
args = {
'ipaddress': ip_address,
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'vpcid': self.get_vpc(key='id'),
}
ip_addresses = self.query_api('listPublicIpAddresses', **args)
if not ip_addresses:
self.fail_json(msg="IP address '%s' not found" % args['ipaddress'])
self.ip_address = ip_addresses['publicipaddress'][0]
return self._get_by_key(key, self.ip_address)
def get_vm_guest_ip(self):
vm_guest_ip = self.module.params.get('vm_guest_ip')
default_nic = self.get_vm_default_nic()
if not vm_guest_ip:
return default_nic['ipaddress']
for secondary_ip in default_nic['secondaryip']:
if vm_guest_ip == secondary_ip['ipaddress']:
return vm_guest_ip
self.fail_json(msg="Secondary IP '%s' not assigned to VM" % vm_guest_ip)
def get_vm_default_nic(self):
if self.vm_default_nic:
return self.vm_default_nic
nics = self.query_api('listNics', virtualmachineid=self.get_vm(key='id'))
if nics:
for n in nics['nic']:
if n['isdefault']:
self.vm_default_nic = n
return self.vm_default_nic
self.fail_json(msg="No default IP address of VM '%s' found" % self.module.params.get('vm'))
def get_vm(self, key=None, filter_zone=True):
if self.vm:
return self._get_by_key(key, self.vm)
vm = self.module.params.get('vm')
if not vm:
self.fail_json(msg="Virtual machine param 'vm' is required")
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'zoneid': self.get_zone(key='id') if filter_zone else None,
'fetch_list': True,
}
vms = self.query_api('listVirtualMachines', **args)
if vms:
for v in vms:
if vm.lower() in [v['name'].lower(), v['displayname'].lower(), v['id']]:
self.vm = v
return self._get_by_key(key, self.vm)
self.fail_json(msg="Virtual machine '%s' not found" % vm)
def get_disk_offering(self, key=None):
disk_offering = self.module.params.get('disk_offering')
if not disk_offering:
return None
# Do not add domain filter for disk offering listing.
disk_offerings = self.query_api('listDiskOfferings')
if disk_offerings:
for d in disk_offerings['diskoffering']:
if disk_offering in [d['displaytext'], d['name'], d['id']]:
return self._get_by_key(key, d)
self.fail_json(msg="Disk offering '%s' not found" % disk_offering)
def get_zone(self, key=None):
if self.zone:
return self._get_by_key(key, self.zone)
zone = self.module.params.get('zone')
if not zone:
zone = os.environ.get('CLOUDSTACK_ZONE')
zones = self.query_api('listZones')
if not zones:
self.fail_json(msg="No zones available. Please create a zone first")
# use the first zone if no zone param given
if not zone:
self.zone = zones['zone'][0]
self.result['zone'] = self.zone['name']
return self._get_by_key(key, self.zone)
if zones:
for z in zones['zone']:
if zone.lower() in [z['name'].lower(), z['id']]:
self.result['zone'] = z['name']
self.zone = z
return self._get_by_key(key, self.zone)
self.fail_json(msg="zone '%s' not found" % zone)
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:
return None
os_types = self.query_api('listOsTypes')
if os_types:
for o in os_types['ostype']:
if os_type in [o['description'], o['id']]:
self.os_type = o
return self._get_by_key(key, self.os_type)
self.fail_json(msg="OS type '%s' not found" % os_type)
def get_hypervisor(self):
if self.hypervisor:
return self.hypervisor
hypervisor = self.module.params.get('hypervisor')
hypervisors = self.query_api('listHypervisors')
# use the first hypervisor if no hypervisor param given
if not hypervisor:
self.hypervisor = hypervisors['hypervisor'][0]['name']
return self.hypervisor
for h in hypervisors['hypervisor']:
if hypervisor.lower() == h['name'].lower():
self.hypervisor = h['name']
return self.hypervisor
self.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:
account = os.environ.get('CLOUDSTACK_ACCOUNT')
if not account:
return None
domain = self.module.params.get('domain')
if not domain:
self.fail_json(msg="Account must be specified with Domain")
args = {
'name': account,
'domainid': self.get_domain(key='id'),
'listall': True
}
accounts = self.query_api('listAccounts', **args)
if accounts:
self.account = accounts['account'][0]
self.result['account'] = self.account['name']
return self._get_by_key(key, self.account)
self.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:
domain = os.environ.get('CLOUDSTACK_DOMAIN')
if not domain:
return None
args = {
'listall': True,
}
domains = self.query_api('listDomains', **args)
if domains:
for d in domains['domain']:
if d['path'].lower() in [domain.lower(), "root/" + domain.lower(), "root" + domain.lower()]:
self.domain = d
self.result['domain'] = d['path']
return self._get_by_key(key, self.domain)
self.fail_json(msg="Domain '%s' not found" % domain)
def query_tags(self, resource, resource_type):
args = {
'resourceid': resource['id'],
'resourcetype': resource_type,
}
tags = self.query_api('listTags', **args)
return self.get_tags(resource=tags, key='tag')
def get_tags(self, resource=None, key='tags'):
existing_tags = []
for tag in resource.get(key) or []:
existing_tags.append({'key': tag['key'], 'value': tag['value']})
return existing_tags
def _process_tags(self, resource, resource_type, tags, operation="create"):
if tags:
self.result['changed'] = True
if not self.module.check_mode:
args = {
'resourceids': resource['id'],
'resourcetype': resource_type,
'tags': tags,
}
if operation == "create":
response = self.query_api('createTags', **args)
else:
response = self.query_api('deleteTags', **args)
self.poll_job(response)
def _tags_that_should_exist_or_be_updated(self, resource, tags):
existing_tags = self.get_tags(resource)
return [tag for tag in tags if tag not in existing_tags]
def _tags_that_should_not_exist(self, resource, tags):
existing_tags = self.get_tags(resource)
return [tag for tag in existing_tags if tag not in tags]
def ensure_tags(self, resource, resource_type=None):
if not resource_type or not resource:
self.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._process_tags(resource, resource_type, self._tags_that_should_not_exist(resource, tags), operation="delete")
self._process_tags(resource, resource_type, self._tags_that_should_exist_or_be_updated(resource, tags))
resource['tags'] = self.query_tags(resource=resource, resource_type=resource_type)
return resource
def get_capabilities(self, key=None):
if self.capabilities:
return self._get_by_key(key, self.capabilities)
capabilities = self.query_api('listCapabilities')
self.capabilities = capabilities['capability']
return self._get_by_key(key, self.capabilities)
def poll_job(self, job=None, key=None):
if 'jobid' in job:
while True:
res = self.query_api('queryAsyncJobResult', jobid=job['jobid'])
if res['jobstatus'] != 0 and 'jobresult' in res:
if 'errortext' in res['jobresult']:
self.fail_json(msg="Failed: '%s'" % res['jobresult']['errortext'])
if key and key in res['jobresult']:
job = res['jobresult'][key]
break
time.sleep(2)
return job
def update_result(self, resource, result=None):
if result is None:
result = dict()
if resource:
returns = self.common_returns.copy()
returns.update(self.returns)
for search_key, return_key in returns.items():
if search_key in resource:
result[return_key] = resource[search_key]
# Bad bad API does not always return int when it should.
for search_key, return_key in self.returns_to_int.items():
if search_key in resource:
result[return_key] = int(resource[search_key])
if 'tags' in resource:
result['tags'] = resource['tags']
return result
def get_result(self, resource):
return self.update_result(resource, self.result)
def get_result_and_facts(self, facts_name, resource):
result = self.get_result(resource)
ansible_facts = {
facts_name: result.copy()
}
for k in ['diff', 'changed']:
if k in ansible_facts[facts_name]:
del ansible_facts[facts_name][k]
result.update(ansible_facts=ansible_facts)
return result

View file

@ -1,460 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_account
short_description: Manages accounts on Apache CloudStack based clouds.
description:
- Create, disable, lock, enable and remove accounts.
author: René Moser (@resmo)
options:
name:
description:
- Name of account.
type: str
required: true
username:
description:
- Username of the user to be created if account did not exist.
- Required on I(state=present).
type: str
password:
description:
- Password of the user to be created if account did not exist.
- Required on I(state=present) if I(ldap_domain) is not set.
type: str
first_name:
description:
- First name of the user to be created if account did not exist.
- Required on I(state=present) if I(ldap_domain) is not set.
type: str
last_name:
description:
- Last name of the user to be created if account did not exist.
- Required on I(state=present) if I(ldap_domain) is not set.
type: str
email:
description:
- Email of the user to be created if account did not exist.
- Required on I(state=present) if I(ldap_domain) is not set.
type: str
timezone:
description:
- Timezone of the user to be created if account did not exist.
type: str
network_domain:
description:
- Network domain of the account.
type: str
account_type:
description:
- Type of the account.
type: str
choices: [ user, root_admin, domain_admin ]
default: user
domain:
description:
- Domain the account is related to.
type: str
default: ROOT
role:
description:
- Creates the account under the specified role name or id.
type: str
ldap_domain:
description:
- Name of the LDAP group or OU to bind.
- If set, account will be linked to LDAP.
type: str
ldap_type:
description:
- Type of the ldap name. GROUP or OU, defaults to GROUP.
type: str
choices: [ GROUP, OU ]
default: GROUP
state:
description:
- State of the account.
- C(unlocked) is an alias for C(enabled).
type: str
choices: [ present, absent, enabled, disabled, locked, unlocked ]
default: present
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: create an account in domain 'CUSTOMERS'
cs_account:
name: customer_xy
username: customer_xy
password: S3Cur3
last_name: Doe
first_name: John
email: john.doe@example.com
domain: CUSTOMERS
role: Domain Admin
delegate_to: localhost
- name: Lock an existing account in domain 'CUSTOMERS'
cs_account:
name: customer_xy
domain: CUSTOMERS
state: locked
delegate_to: localhost
- name: Disable an existing account in domain 'CUSTOMERS'
cs_account:
name: customer_xy
domain: CUSTOMERS
state: disabled
delegate_to: localhost
- name: Enable an existing account in domain 'CUSTOMERS'
cs_account:
name: customer_xy
domain: CUSTOMERS
state: enabled
delegate_to: localhost
- name: Remove an account in domain 'CUSTOMERS'
cs_account:
name: customer_xy
domain: CUSTOMERS
state: absent
delegate_to: localhost
- name: Create a single user LDAP account in domain 'CUSTOMERS'
cs_account:
name: customer_xy
username: customer_xy
domain: CUSTOMERS
ldap_domain: cn=customer_xy,cn=team_xy,ou=People,dc=domain,dc=local
delegate_to: localhost
- name: Create a LDAP account in domain 'CUSTOMERS' and bind it to a LDAP group
cs_account:
name: team_xy
username: customer_xy
domain: CUSTOMERS
ldap_domain: cn=team_xy,ou=People,dc=domain,dc=local
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the account.
returned: success
type: str
sample: 87b1e0ce-4e01-11e4-bb66-0050569e64b8
name:
description: Name of the account.
returned: success
type: str
sample: linus@example.com
account_type:
description: Type of the account.
returned: success
type: str
sample: user
state:
description: State of the account.
returned: success
type: str
sample: enabled
network_domain:
description: Network domain of the account.
returned: success
type: str
sample: example.local
domain:
description: Domain the account is related.
returned: success
type: str
sample: ROOT
role:
description: The role name of the account
returned: success
type: str
sample: Domain Admin
'''
# import cloudstack common
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackAccount(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackAccount, self).__init__(module)
self.returns = {
'networkdomain': 'network_domain',
'rolename': 'role',
}
self.account = None
self.account_types = {
'user': 0,
'root_admin': 1,
'domain_admin': 2,
}
def get_role_id(self):
role_param = self.module.params.get('role')
role_id = None
if role_param:
role_list = self.query_api('listRoles')
for role in role_list['role']:
if role_param in [role['name'], role['id']]:
role_id = role['id']
if not role_id:
self.module.fail_json(msg="Role not found: %s" % role_param)
return role_id
def get_account_type(self):
account_type = self.module.params.get('account_type')
return self.account_types[account_type]
def get_account(self):
if not self.account:
args = {
'listall': True,
'domainid': self.get_domain(key='id'),
'fetch_list': True,
}
accounts = self.query_api('listAccounts', **args)
if accounts:
account_name = self.module.params.get('name')
for a in accounts:
if account_name == a['name']:
self.account = a
break
return self.account
def enable_account(self):
account = self.get_account()
if not account:
account = self.present_account()
if account['state'].lower() != 'enabled':
self.result['changed'] = True
args = {
'id': account['id'],
'account': self.module.params.get('name'),
'domainid': self.get_domain(key='id')
}
if not self.module.check_mode:
res = self.query_api('enableAccount', **args)
account = res['account']
return account
def lock_account(self):
return self.lock_or_disable_account(lock=True)
def disable_account(self):
return self.lock_or_disable_account()
def lock_or_disable_account(self, lock=False):
account = self.get_account()
if not account:
account = self.present_account()
# we need to enable the account to lock it.
if lock and account['state'].lower() == 'disabled':
account = self.enable_account()
if (lock and account['state'].lower() != 'locked' or
not lock and account['state'].lower() != 'disabled'):
self.result['changed'] = True
args = {
'id': account['id'],
'account': self.module.params.get('name'),
'domainid': self.get_domain(key='id'),
'lock': lock,
}
if not self.module.check_mode:
account = self.query_api('disableAccount', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
account = self.poll_job(account, 'account')
return account
def present_account(self):
account = self.get_account()
if not account:
self.result['changed'] = True
if self.module.params.get('ldap_domain'):
required_params = [
'domain',
'username',
]
self.module.fail_on_missing_params(required_params=required_params)
account = self.create_ldap_account(account)
else:
required_params = [
'email',
'username',
'password',
'first_name',
'last_name',
]
self.module.fail_on_missing_params(required_params=required_params)
account = self.create_account(account)
return account
def create_ldap_account(self, account):
args = {
'account': self.module.params.get('name'),
'domainid': self.get_domain(key='id'),
'accounttype': self.get_account_type(),
'networkdomain': self.module.params.get('network_domain'),
'username': self.module.params.get('username'),
'timezone': self.module.params.get('timezone'),
'roleid': self.get_role_id()
}
if not self.module.check_mode:
res = self.query_api('ldapCreateAccount', **args)
account = res['account']
args = {
'account': self.module.params.get('name'),
'domainid': self.get_domain(key='id'),
'accounttype': self.get_account_type(),
'ldapdomain': self.module.params.get('ldap_domain'),
'type': self.module.params.get('ldap_type')
}
self.query_api('linkAccountToLdap', **args)
return account
def create_account(self, account):
args = {
'account': self.module.params.get('name'),
'domainid': self.get_domain(key='id'),
'accounttype': self.get_account_type(),
'networkdomain': self.module.params.get('network_domain'),
'username': self.module.params.get('username'),
'password': self.module.params.get('password'),
'firstname': self.module.params.get('first_name'),
'lastname': self.module.params.get('last_name'),
'email': self.module.params.get('email'),
'timezone': self.module.params.get('timezone'),
'roleid': self.get_role_id()
}
if not self.module.check_mode:
res = self.query_api('createAccount', **args)
account = res['account']
return account
def absent_account(self):
account = self.get_account()
if account:
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('deleteAccount', id=account['id'])
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'account')
return account
def get_result(self, account):
super(AnsibleCloudStackAccount, self).get_result(account)
if account:
if 'accounttype' in account:
for key, value in self.account_types.items():
if value == account['accounttype']:
self.result['account_type'] = key
break
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
state=dict(choices=['present', 'absent', 'enabled', 'disabled', 'locked', 'unlocked'], default='present'),
account_type=dict(choices=['user', 'root_admin', 'domain_admin'], default='user'),
network_domain=dict(),
domain=dict(default='ROOT'),
email=dict(),
first_name=dict(),
last_name=dict(),
username=dict(),
password=dict(no_log=True),
timezone=dict(),
role=dict(),
ldap_domain=dict(),
ldap_type=dict(choices=['GROUP', 'OU'], default='GROUP'),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_acc = AnsibleCloudStackAccount(module)
state = module.params.get('state')
if state in ['absent']:
account = acs_acc.absent_account()
elif state in ['enabled', 'unlocked']:
account = acs_acc.enable_account()
elif state in ['disabled']:
account = acs_acc.disable_account()
elif state in ['locked']:
account = acs_acc.lock_account()
else:
account = acs_acc.present_account()
result = acs_acc.get_result(account)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,235 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_affinitygroup
short_description: Manages affinity groups on Apache CloudStack based clouds.
description:
- Create and remove affinity groups.
author: René Moser (@resmo)
options:
name:
description:
- Name of the affinity group.
type: str
required: true
affinity_type:
description:
- Type of the affinity group. If not specified, first found affinity type is used.
type: str
description:
description:
- Description of the affinity group.
type: str
state:
description:
- State of the affinity group.
type: str
choices: [ present, absent ]
default: present
domain:
description:
- Domain the affinity group is related to.
type: str
account:
description:
- Account the affinity group is related to.
type: str
project:
description:
- Name of the project the affinity group is related to.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a affinity group
cs_affinitygroup:
name: haproxy
affinity_type: host anti-affinity
delegate_to: localhost
- name: Remove a affinity group
cs_affinitygroup:
name: haproxy
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the affinity group.
returned: success
type: str
sample: 87b1e0ce-4e01-11e4-bb66-0050569e64b8
name:
description: Name of affinity group.
returned: success
type: str
sample: app
description:
description: Description of affinity group.
returned: success
type: str
sample: application affinity group
affinity_type:
description: Type of affinity group.
returned: success
type: str
sample: host anti-affinity
project:
description: Name of project the affinity group is related to.
returned: success
type: str
sample: Production
domain:
description: Domain the affinity group is related to.
returned: success
type: str
sample: example domain
account:
description: Account the affinity group is related to.
returned: success
type: str
sample: example account
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackAffinityGroup(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackAffinityGroup, self).__init__(module)
self.returns = {
'type': 'affinity_type',
}
self.affinity_group = None
def get_affinity_group(self):
if not self.affinity_group:
args = {
'projectid': self.get_project(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'name': self.module.params.get('name'),
}
affinity_groups = self.query_api('listAffinityGroups', **args)
if affinity_groups:
self.affinity_group = affinity_groups['affinitygroup'][0]
return self.affinity_group
def get_affinity_type(self):
affinity_type = self.module.params.get('affinity_type')
affinity_types = self.query_api('listAffinityGroupTypes', )
if affinity_types:
if not affinity_type:
return affinity_types['affinityGroupType'][0]['type']
for a in affinity_types['affinityGroupType']:
if a['type'] == affinity_type:
return a['type']
self.module.fail_json(msg="affinity group type not found: %s" % affinity_type)
def create_affinity_group(self):
affinity_group = self.get_affinity_group()
if not affinity_group:
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'type': self.get_affinity_type(),
'description': self.module.params.get('description'),
'projectid': self.get_project(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
}
if not self.module.check_mode:
res = self.query_api('createAffinityGroup', **args)
poll_async = self.module.params.get('poll_async')
if res and poll_async:
affinity_group = self.poll_job(res, 'affinitygroup')
return affinity_group
def remove_affinity_group(self):
affinity_group = self.get_affinity_group()
if affinity_group:
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'projectid': self.get_project(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
}
if not self.module.check_mode:
res = self.query_api('deleteAffinityGroup', **args)
poll_async = self.module.params.get('poll_async')
if res and poll_async:
self.poll_job(res, 'affinitygroup')
return affinity_group
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
affinity_type=dict(),
description=dict(),
state=dict(choices=['present', 'absent'], default='present'),
domain=dict(),
account=dict(),
project=dict(),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_ag = AnsibleCloudStackAffinityGroup(module)
state = module.params.get('state')
if state in ['absent']:
affinity_group = acs_ag.remove_affinity_group()
else:
affinity_group = acs_ag.create_affinity_group()
result = acs_ag.get_result(affinity_group)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,393 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_cluster
short_description: Manages host clusters on Apache CloudStack based clouds.
description:
- Create, update and remove clusters.
author: René Moser (@resmo)
options:
name:
description:
- name of the cluster.
type: str
required: true
zone:
description:
- Name of the zone in which the cluster belongs to.
- If not set, default zone is used.
type: str
pod:
description:
- Name of the pod in which the cluster belongs to.
type: str
cluster_type:
description:
- Type of the cluster.
- Required if I(state=present)
type: str
choices: [ CloudManaged, ExternalManaged ]
hypervisor:
description:
- Name the hypervisor to be used.
- Required if I(state=present).
- Possible values are C(KVM), C(VMware), C(BareMetal), C(XenServer), C(LXC), C(HyperV), C(UCS), C(OVM), C(Simulator).
type: str
url:
description:
- URL for the cluster
type: str
username:
description:
- Username for the cluster.
type: str
password:
description:
- Password for the cluster.
type: str
guest_vswitch_name:
description:
- Name of virtual switch used for guest traffic in the cluster.
- This would override zone wide traffic label setting.
type: str
guest_vswitch_type:
description:
- Type of virtual switch used for guest traffic in the cluster.
- Allowed values are, vmwaresvs (for VMware standard vSwitch) and vmwaredvs (for VMware distributed vSwitch)
type: str
choices: [ vmwaresvs, vmwaredvs ]
public_vswitch_name:
description:
- Name of virtual switch used for public traffic in the cluster.
- This would override zone wide traffic label setting.
type: str
public_vswitch_type:
description:
- Type of virtual switch used for public traffic in the cluster.
- Allowed values are, vmwaresvs (for VMware standard vSwitch) and vmwaredvs (for VMware distributed vSwitch)
type: str
choices: [ vmwaresvs, vmwaredvs ]
vms_ip_address:
description:
- IP address of the VSM associated with this cluster.
type: str
vms_username:
description:
- Username for the VSM associated with this cluster.
type: str
vms_password:
description:
- Password for the VSM associated with this cluster.
type: str
ovm3_cluster:
description:
- Ovm3 native OCFS2 clustering enabled for cluster.
type: str
ovm3_pool:
description:
- Ovm3 native pooling enabled for cluster.
type: str
ovm3_vip:
description:
- Ovm3 vip to use for pool (and cluster).
type: str
state:
description:
- State of the cluster.
type: str
choices: [ present, absent, disabled, enabled ]
default: present
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Ensure a cluster is present
cs_cluster:
name: kvm-cluster-01
zone: ch-zrh-ix-01
hypervisor: KVM
cluster_type: CloudManaged
delegate_to: localhost
- name: Ensure a cluster is disabled
cs_cluster:
name: kvm-cluster-01
zone: ch-zrh-ix-01
state: disabled
delegate_to: localhost
- name: Ensure a cluster is enabled
cs_cluster:
name: kvm-cluster-01
zone: ch-zrh-ix-01
state: enabled
delegate_to: localhost
- name: Ensure a cluster is absent
cs_cluster:
name: kvm-cluster-01
zone: ch-zrh-ix-01
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the cluster.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the cluster.
returned: success
type: str
sample: cluster01
allocation_state:
description: State of the cluster.
returned: success
type: str
sample: Enabled
cluster_type:
description: Type of the cluster.
returned: success
type: str
sample: ExternalManaged
cpu_overcommit_ratio:
description: The CPU overcommit ratio of the cluster.
returned: success
type: str
sample: 1.0
memory_overcommit_ratio:
description: The memory overcommit ratio of the cluster.
returned: success
type: str
sample: 1.0
managed_state:
description: Whether this cluster is managed by CloudStack.
returned: success
type: str
sample: Managed
ovm3_vip:
description: Ovm3 VIP to use for pooling and/or clustering
returned: success
type: str
sample: 10.10.10.101
hypervisor:
description: Hypervisor of the cluster
returned: success
type: str
sample: VMware
zone:
description: Name of zone the cluster is in.
returned: success
type: str
sample: ch-gva-2
pod:
description: Name of pod the cluster is in.
returned: success
type: str
sample: pod01
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackCluster(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackCluster, self).__init__(module)
self.returns = {
'allocationstate': 'allocation_state',
'hypervisortype': 'hypervisor',
'clustertype': 'cluster_type',
'podname': 'pod',
'managedstate': 'managed_state',
'memoryovercommitratio': 'memory_overcommit_ratio',
'cpuovercommitratio': 'cpu_overcommit_ratio',
'ovm3vip': 'ovm3_vip',
}
self.cluster = None
def _get_common_cluster_args(self):
args = {
'clustername': self.module.params.get('name'),
'hypervisor': self.module.params.get('hypervisor'),
'clustertype': self.module.params.get('cluster_type'),
}
state = self.module.params.get('state')
if state in ['enabled', 'disabled']:
args['allocationstate'] = state.capitalize()
return args
def get_pod(self, key=None):
args = {
'name': self.module.params.get('pod'),
'zoneid': self.get_zone(key='id'),
}
pods = self.query_api('listPods', **args)
if pods:
return self._get_by_key(key, pods['pod'][0])
self.module.fail_json(msg="Pod %s not found in zone %s" % (self.module.params.get('pod'), self.get_zone(key='name')))
def get_cluster(self):
if not self.cluster:
args = {}
uuid = self.module.params.get('id')
if uuid:
args['id'] = uuid
clusters = self.query_api('listClusters', **args)
if clusters:
self.cluster = clusters['cluster'][0]
return self.cluster
args['name'] = self.module.params.get('name')
clusters = self.query_api('listClusters', **args)
if clusters:
self.cluster = clusters['cluster'][0]
# fix different return from API then request argument given
self.cluster['hypervisor'] = self.cluster['hypervisortype']
self.cluster['clustername'] = self.cluster['name']
return self.cluster
def present_cluster(self):
cluster = self.get_cluster()
if cluster:
cluster = self._update_cluster()
else:
cluster = self._create_cluster()
return cluster
def _create_cluster(self):
required_params = [
'cluster_type',
'hypervisor',
]
self.module.fail_on_missing_params(required_params=required_params)
args = self._get_common_cluster_args()
args['zoneid'] = self.get_zone(key='id')
args['podid'] = self.get_pod(key='id')
args['url'] = self.module.params.get('url')
args['username'] = self.module.params.get('username')
args['password'] = self.module.params.get('password')
args['guestvswitchname'] = self.module.params.get('guest_vswitch_name')
args['guestvswitchtype'] = self.module.params.get('guest_vswitch_type')
args['publicvswitchtype'] = self.module.params.get('public_vswitch_name')
args['publicvswitchtype'] = self.module.params.get('public_vswitch_type')
args['vsmipaddress'] = self.module.params.get('vms_ip_address')
args['vsmusername'] = self.module.params.get('vms_username')
args['vmspassword'] = self.module.params.get('vms_password')
args['ovm3cluster'] = self.module.params.get('ovm3_cluster')
args['ovm3pool'] = self.module.params.get('ovm3_pool')
args['ovm3vip'] = self.module.params.get('ovm3_vip')
self.result['changed'] = True
cluster = None
if not self.module.check_mode:
res = self.query_api('addCluster', **args)
# API returns a list as result CLOUDSTACK-9205
if isinstance(res['cluster'], list):
cluster = res['cluster'][0]
else:
cluster = res['cluster']
return cluster
def _update_cluster(self):
cluster = self.get_cluster()
args = self._get_common_cluster_args()
args['id'] = cluster['id']
if self.has_changed(args, cluster):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateCluster', **args)
cluster = res['cluster']
return cluster
def absent_cluster(self):
cluster = self.get_cluster()
if cluster:
self.result['changed'] = True
args = {
'id': cluster['id'],
}
if not self.module.check_mode:
self.query_api('deleteCluster', **args)
return cluster
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
zone=dict(),
pod=dict(),
cluster_type=dict(choices=['CloudManaged', 'ExternalManaged']),
hypervisor=dict(),
state=dict(choices=['present', 'enabled', 'disabled', 'absent'], default='present'),
url=dict(),
username=dict(),
password=dict(no_log=True),
guest_vswitch_name=dict(),
guest_vswitch_type=dict(choices=['vmwaresvs', 'vmwaredvs']),
public_vswitch_name=dict(),
public_vswitch_type=dict(choices=['vmwaresvs', 'vmwaredvs']),
vms_ip_address=dict(),
vms_username=dict(),
vms_password=dict(no_log=True),
ovm3_cluster=dict(),
ovm3_pool=dict(),
ovm3_vip=dict(),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_cluster = AnsibleCloudStackCluster(module)
state = module.params.get('state')
if state in ['absent']:
cluster = acs_cluster.absent_cluster()
else:
cluster = acs_cluster.present_cluster()
result = acs_cluster.get_result(cluster)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,277 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_configuration
short_description: Manages configuration on Apache CloudStack based clouds.
description:
- Manages global, zone, account, storage and cluster configurations.
author: René Moser (@resmo)
options:
name:
description:
- Name of the configuration.
type: str
required: true
value:
description:
- Value of the configuration.
type: str
required: true
account:
description:
- Ensure the value for corresponding account.
type: str
domain:
description:
- Domain the account is related to.
- Only considered if I(account) is used.
type: str
default: ROOT
zone:
description:
- Ensure the value for corresponding zone.
type: str
storage:
description:
- Ensure the value for corresponding storage pool.
type: str
cluster:
description:
- Ensure the value for corresponding cluster.
type: str
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Ensure global configuration
cs_configuration:
name: router.reboot.when.outofband.migrated
value: false
delegate_to: localhost
- name: Ensure zone configuration
cs_configuration:
name: router.reboot.when.outofband.migrated
zone: ch-gva-01
value: true
delegate_to: localhost
- name: Ensure storage configuration
cs_configuration:
name: storage.overprovisioning.factor
storage: storage01
value: 2.0
delegate_to: localhost
- name: Ensure account configuration
cs_configuration:
name: allow.public.user.templates
value: false
account: acme inc
domain: customers
delegate_to: localhost
'''
RETURN = '''
---
category:
description: Category of the configuration.
returned: success
type: str
sample: Advanced
scope:
description: Scope (zone/cluster/storagepool/account) of the parameter that needs to be updated.
returned: success
type: str
sample: storagepool
description:
description: Description of the configuration.
returned: success
type: str
sample: Setup the host to do multipath
name:
description: Name of the configuration.
returned: success
type: str
sample: zone.vlan.capacity.notificationthreshold
value:
description: Value of the configuration.
returned: success
type: str
sample: "0.75"
account:
description: Account of the configuration.
returned: success
type: str
sample: admin
Domain:
description: Domain of account of the configuration.
returned: success
type: str
sample: ROOT
zone:
description: Zone of the configuration.
returned: success
type: str
sample: ch-gva-01
cluster:
description: Cluster of the configuration.
returned: success
type: str
sample: cluster01
storage:
description: Storage of the configuration.
returned: success
type: str
sample: storage01
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackConfiguration(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackConfiguration, self).__init__(module)
self.returns = {
'category': 'category',
'scope': 'scope',
'value': 'value',
}
self.storage = None
self.account = None
self.cluster = None
def _get_common_configuration_args(self):
args = {
'name': self.module.params.get('name'),
'accountid': self.get_account(key='id'),
'storageid': self.get_storage(key='id'),
'zoneid': self.get_zone(key='id'),
'clusterid': self.get_cluster(key='id'),
}
return args
def get_zone(self, key=None):
# make sure we do net use the default zone
zone = self.module.params.get('zone')
if zone:
return super(AnsibleCloudStackConfiguration, self).get_zone(key=key)
def get_cluster(self, key=None):
if not self.cluster:
cluster_name = self.module.params.get('cluster')
if not cluster_name:
return None
args = {
'name': cluster_name,
}
clusters = self.query_api('listClusters', **args)
if clusters:
self.cluster = clusters['cluster'][0]
self.result['cluster'] = self.cluster['name']
else:
self.module.fail_json(msg="Cluster %s not found." % cluster_name)
return self._get_by_key(key=key, my_dict=self.cluster)
def get_storage(self, key=None):
if not self.storage:
storage_pool_name = self.module.params.get('storage')
if not storage_pool_name:
return None
args = {
'name': storage_pool_name,
}
storage_pools = self.query_api('listStoragePools', **args)
if storage_pools:
self.storage = storage_pools['storagepool'][0]
self.result['storage'] = self.storage['name']
else:
self.module.fail_json(msg="Storage pool %s not found." % storage_pool_name)
return self._get_by_key(key=key, my_dict=self.storage)
def get_configuration(self):
configuration = None
args = self._get_common_configuration_args()
args['fetch_list'] = True
configurations = self.query_api('listConfigurations', **args)
if not configurations:
self.module.fail_json(msg="Configuration %s not found." % args['name'])
for config in configurations:
if args['name'] == config['name']:
configuration = config
return configuration
def get_value(self):
value = str(self.module.params.get('value'))
if value in ('True', 'False'):
value = value.lower()
return value
def present_configuration(self):
configuration = self.get_configuration()
args = self._get_common_configuration_args()
args['value'] = self.get_value()
if self.has_changed(args, configuration, ['value']):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateConfiguration', **args)
configuration = res['configuration']
return configuration
def get_result(self, configuration):
self.result = super(AnsibleCloudStackConfiguration, self).get_result(configuration)
if self.account:
self.result['account'] = self.account['name']
self.result['domain'] = self.domain['path']
elif self.zone:
self.result['zone'] = self.zone['name']
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
value=dict(type='str', required=True),
zone=dict(),
storage=dict(),
cluster=dict(),
account=dict(),
domain=dict(default='ROOT')
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_configuration = AnsibleCloudStackConfiguration(module)
configuration = acs_configuration.present_configuration()
result = acs_configuration.get_result(configuration)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,381 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2018, David Passante <@dpassante>
# (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_disk_offering
description:
- Create and delete disk offerings for guest VMs.
- Update display_text or display_offering of existing disk offering.
short_description: Manages disk offerings on Apache CloudStack based clouds.
author:
- David Passante (@dpassante)
- René Moser (@resmo)
options:
disk_size:
description:
- Size of the disk offering in GB (1GB = 1,073,741,824 bytes).
type: int
bytes_read_rate:
description:
- Bytes read rate of the disk offering.
type: int
bytes_write_rate:
description:
- Bytes write rate of the disk offering.
type: int
display_text:
description:
- Display text of the disk offering.
- If not set, C(name) will be used as C(display_text) while creating.
type: str
domain:
description:
- Domain the disk offering is related to.
- Public for all domains and subdomains if not set.
type: str
hypervisor_snapshot_reserve:
description:
- Hypervisor snapshot reserve space as a percent of a volume.
- Only for managed storage using Xen or VMware.
type: int
customized:
description:
- Whether disk offering iops is custom or not.
type: bool
default: no
iops_read_rate:
description:
- IO requests read rate of the disk offering.
type: int
iops_write_rate:
description:
- IO requests write rate of the disk offering.
type: int
iops_max:
description:
- Max. iops of the disk offering.
type: int
iops_min:
description:
- Min. iops of the disk offering.
type: int
name:
description:
- Name of the disk offering.
type: str
required: true
provisioning_type:
description:
- Provisioning type used to create volumes.
type: str
choices: [ thin, sparse, fat ]
state:
description:
- State of the disk offering.
type: str
choices: [ present, absent ]
default: present
storage_type:
description:
- The storage type of the disk offering.
type: str
choices: [ local, shared ]
storage_tags:
description:
- The storage tags for this disk offering.
type: list
aliases: [ storage_tag ]
display_offering:
description:
- An optional field, whether to display the offering to the end user or not.
type: bool
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a disk offering with local storage
cs_disk_offering:
name: small
display_text: Small 10GB
disk_size: 10
storage_type: local
delegate_to: localhost
- name: Create or update a disk offering with shared storage
cs_disk_offering:
name: small
display_text: Small 10GB
disk_size: 10
storage_type: shared
storage_tags: SAN01
delegate_to: localhost
- name: Remove a disk offering
cs_disk_offering:
name: small
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the disk offering
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
disk_size:
description: Size of the disk offering in GB
returned: success
type: int
sample: 10
iops_max:
description: Max iops of the disk offering
returned: success
type: int
sample: 1000
iops_min:
description: Min iops of the disk offering
returned: success
type: int
sample: 500
bytes_read_rate:
description: Bytes read rate of the disk offering
returned: success
type: int
sample: 1000
bytes_write_rate:
description: Bytes write rate of the disk offering
returned: success
type: int
sample: 1000
iops_read_rate:
description: IO requests per second read rate of the disk offering
returned: success
type: int
sample: 1000
iops_write_rate:
description: IO requests per second write rate of the disk offering
returned: success
type: int
sample: 1000
created:
description: Date the offering was created
returned: success
type: str
sample: 2017-11-19T10:48:59+0000
display_text:
description: Display text of the offering
returned: success
type: str
sample: Small 10GB
domain:
description: Domain the offering is into
returned: success
type: str
sample: ROOT
storage_tags:
description: List of storage tags
returned: success
type: list
sample: [ 'eco' ]
customized:
description: Whether the offering uses custom IOPS or not
returned: success
type: bool
sample: false
name:
description: Name of the system offering
returned: success
type: str
sample: Micro
provisioning_type:
description: Provisioning type used to create volumes
returned: success
type: str
sample: thin
storage_type:
description: Storage type used to create volumes
returned: success
type: str
sample: shared
display_offering:
description: Whether to display the offering to the end user or not.
returned: success
type: bool
sample: false
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackDiskOffering(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackDiskOffering, self).__init__(module)
self.returns = {
'disksize': 'disk_size',
'diskBytesReadRate': 'bytes_read_rate',
'diskBytesWriteRate': 'bytes_write_rate',
'diskIopsReadRate': 'iops_read_rate',
'diskIopsWriteRate': 'iops_write_rate',
'maxiops': 'iops_max',
'miniops': 'iops_min',
'hypervisorsnapshotreserve': 'hypervisor_snapshot_reserve',
'customized': 'customized',
'provisioningtype': 'provisioning_type',
'storagetype': 'storage_type',
'tags': 'storage_tags',
'displayoffering': 'display_offering',
}
self.disk_offering = None
def get_disk_offering(self):
args = {
'name': self.module.params.get('name'),
'domainid': self.get_domain(key='id'),
}
disk_offerings = self.query_api('listDiskOfferings', **args)
if disk_offerings:
for disk_offer in disk_offerings['diskoffering']:
if args['name'] == disk_offer['name']:
self.disk_offering = disk_offer
return self.disk_offering
def present_disk_offering(self):
disk_offering = self.get_disk_offering()
if not disk_offering:
disk_offering = self._create_offering(disk_offering)
else:
disk_offering = self._update_offering(disk_offering)
return disk_offering
def absent_disk_offering(self):
disk_offering = self.get_disk_offering()
if disk_offering:
self.result['changed'] = True
if not self.module.check_mode:
args = {
'id': disk_offering['id'],
}
self.query_api('deleteDiskOffering', **args)
return disk_offering
def _create_offering(self, disk_offering):
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'displaytext': self.get_or_fallback('display_text', 'name'),
'disksize': self.module.params.get('disk_size'),
'bytesreadrate': self.module.params.get('bytes_read_rate'),
'byteswriterate': self.module.params.get('bytes_write_rate'),
'customized': self.module.params.get('customized'),
'domainid': self.get_domain(key='id'),
'hypervisorsnapshotreserve': self.module.params.get('hypervisor_snapshot_reserve'),
'iopsreadrate': self.module.params.get('iops_read_rate'),
'iopswriterate': self.module.params.get('iops_write_rate'),
'maxiops': self.module.params.get('iops_max'),
'miniops': self.module.params.get('iops_min'),
'provisioningtype': self.module.params.get('provisioning_type'),
'diskofferingdetails': self.module.params.get('disk_offering_details'),
'storagetype': self.module.params.get('storage_type'),
'tags': self.module.params.get('storage_tags'),
'displayoffering': self.module.params.get('display_offering'),
}
if not self.module.check_mode:
res = self.query_api('createDiskOffering', **args)
disk_offering = res['diskoffering']
return disk_offering
def _update_offering(self, disk_offering):
args = {
'id': disk_offering['id'],
'name': self.module.params.get('name'),
'displaytext': self.get_or_fallback('display_text', 'name'),
'displayoffering': self.module.params.get('display_offering'),
}
if self.has_changed(args, disk_offering):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateDiskOffering', **args)
disk_offering = res['diskoffering']
return disk_offering
def get_result(self, disk_offering):
super(AnsibleCloudStackDiskOffering, self).get_result(disk_offering)
if disk_offering:
# Prevent confusion, the api returns a tags key for storage tags.
if 'tags' in disk_offering:
self.result['storage_tags'] = disk_offering['tags'].split(',') or [disk_offering['tags']]
if 'tags' in self.result:
del self.result['tags']
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
display_text=dict(),
domain=dict(),
disk_size=dict(type='int'),
display_offering=dict(type='bool'),
hypervisor_snapshot_reserve=dict(type='int'),
bytes_read_rate=dict(type='int'),
bytes_write_rate=dict(type='int'),
customized=dict(type='bool'),
iops_read_rate=dict(type='int'),
iops_write_rate=dict(type='int'),
iops_max=dict(type='int'),
iops_min=dict(type='int'),
provisioning_type=dict(choices=['thin', 'sparse', 'fat']),
storage_type=dict(choices=['local', 'shared']),
storage_tags=dict(type='list', aliases=['storage_tag']),
state=dict(choices=['present', 'absent'], default='present'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_do = AnsibleCloudStackDiskOffering(module)
state = module.params.get('state')
if state == "absent":
disk_offering = acs_do.absent_disk_offering()
else:
disk_offering = acs_do.present_disk_offering()
result = acs_do.get_result(disk_offering)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,251 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_domain
short_description: Manages domains on Apache CloudStack based clouds.
description:
- Create, update and remove domains.
author: René Moser (@resmo)
options:
path:
description:
- Path of the domain.
- Prefix C(ROOT/) or C(/ROOT/) in path is optional.
type: str
required: true
network_domain:
description:
- Network domain for networks in the domain.
type: str
clean_up:
description:
- Clean up all domain resources like child domains and accounts.
- Considered on I(state=absent).
type: bool
default: no
state:
description:
- State of the domain.
type: str
choices: [ present, absent ]
default: present
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a domain
cs_domain:
path: ROOT/customers
network_domain: customers.example.com
delegate_to: localhost
- name: Create another subdomain
cs_domain:
path: ROOT/customers/xy
network_domain: xy.customers.example.com
delegate_to: localhost
- name: Remove a domain
cs_domain:
path: ROOT/customers/xy
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the domain.
returned: success
type: str
sample: 87b1e0ce-4e01-11e4-bb66-0050569e64b8
name:
description: Name of the domain.
returned: success
type: str
sample: customers
path:
description: Domain path.
returned: success
type: str
sample: /ROOT/customers
parent_domain:
description: Parent domain of the domain.
returned: success
type: str
sample: ROOT
network_domain:
description: Network domain of the domain.
returned: success
type: str
sample: example.local
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackDomain(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackDomain, self).__init__(module)
self.returns = {
'path': 'path',
'networkdomain': 'network_domain',
'parentdomainname': 'parent_domain',
}
self.domain = None
def _get_domain_internal(self, path=None):
if not path:
path = self.module.params.get('path')
if path.endswith('/'):
self.module.fail_json(msg="Path '%s' must not end with /" % path)
path = path.lower()
if path.startswith('/') and not path.startswith('/root/'):
path = "root" + path
elif not path.startswith('root/'):
path = "root/" + path
args = {
'listall': True,
'fetch_list': True,
}
domains = self.query_api('listDomains', **args)
if domains:
for d in domains:
if path == d['path'].lower():
return d
return None
def get_name(self):
# last part of the path is the name
name = self.module.params.get('path').split('/')[-1:]
return name
def get_domain(self, key=None):
if not self.domain:
self.domain = self._get_domain_internal()
return self._get_by_key(key, self.domain)
def get_parent_domain(self, key=None):
path = self.module.params.get('path')
# cut off last /*
path = '/'.join(path.split('/')[:-1])
if not path:
return None
parent_domain = self._get_domain_internal(path=path)
if not parent_domain:
self.module.fail_json(msg="Parent domain path %s does not exist" % path)
return self._get_by_key(key, parent_domain)
def present_domain(self):
domain = self.get_domain()
if not domain:
domain = self.create_domain(domain)
else:
domain = self.update_domain(domain)
return domain
def create_domain(self, domain):
self.result['changed'] = True
args = {
'name': self.get_name(),
'parentdomainid': self.get_parent_domain(key='id'),
'networkdomain': self.module.params.get('network_domain')
}
if not self.module.check_mode:
res = self.query_api('createDomain', **args)
domain = res['domain']
return domain
def update_domain(self, domain):
args = {
'id': domain['id'],
'networkdomain': self.module.params.get('network_domain')
}
if self.has_changed(args, domain):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateDomain', **args)
domain = res['domain']
return domain
def absent_domain(self):
domain = self.get_domain()
if domain:
self.result['changed'] = True
if not self.module.check_mode:
args = {
'id': domain['id'],
'cleanup': self.module.params.get('clean_up')
}
res = self.query_api('deleteDomain', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
res = self.poll_job(res, 'domain')
return domain
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
path=dict(required=True),
state=dict(choices=['present', 'absent'], default='present'),
network_domain=dict(),
clean_up=dict(type='bool', default=False),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_dom = AnsibleCloudStackDomain(module)
state = module.params.get('state')
if state in ['absent']:
domain = acs_dom.absent_domain()
else:
domain = acs_dom.present_domain()
result = acs_dom.get_result(domain)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,234 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_facts
short_description: Gather facts on instances of Apache CloudStack based clouds.
description:
- This module fetches data from the metadata API in CloudStack. The module must be called from within the instance itself.
author: René Moser (@resmo)
options:
filter:
description:
- Filter for a specific fact.
type: str
choices:
- cloudstack_service_offering
- cloudstack_availability_zone
- cloudstack_public_hostname
- cloudstack_public_ipv4
- cloudstack_local_hostname
- cloudstack_local_ipv4
- cloudstack_instance_id
- cloudstack_user_data
meta_data_host:
description:
- Host or IP of the meta data API service.
- If not set, determination by parsing the dhcp lease file.
type: str
requirements: [ yaml ]
'''
EXAMPLES = '''
# Gather all facts on instances
- name: Gather cloudstack facts
cs_facts:
# Gather specific fact on instances
- name: Gather cloudstack facts
cs_facts: filter=cloudstack_instance_id
# Gather specific fact on instances with a given meta_data_host
- name: Gather cloudstack facts
cs_facts:
filter: cloudstack_instance_id
meta_data_host: 169.254.169.254
'''
RETURN = '''
---
cloudstack_availability_zone:
description: zone the instance is deployed in.
returned: success
type: str
sample: ch-gva-2
cloudstack_instance_id:
description: UUID of the instance.
returned: success
type: str
sample: ab4e80b0-3e7e-4936-bdc5-e334ba5b0139
cloudstack_local_hostname:
description: local hostname of the instance.
returned: success
type: str
sample: VM-ab4e80b0-3e7e-4936-bdc5-e334ba5b0139
cloudstack_local_ipv4:
description: local IPv4 of the instance.
returned: success
type: str
sample: 185.19.28.35
cloudstack_public_hostname:
description: public IPv4 of the router. Same as I(cloudstack_public_ipv4).
returned: success
type: str
sample: VM-ab4e80b0-3e7e-4936-bdc5-e334ba5b0139
cloudstack_public_ipv4:
description: public IPv4 of the router.
returned: success
type: str
sample: 185.19.28.35
cloudstack_service_offering:
description: service offering of the instance.
returned: success
type: str
sample: Micro 512mb 1cpu
cloudstack_user_data:
description: data of the instance provided by users.
returned: success
type: dict
sample: { "bla": "foo" }
'''
import os
import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.urls import fetch_url
from ansible.module_utils.facts import ansible_collector, default_collectors
YAML_IMP_ERR = None
try:
import yaml
HAS_LIB_YAML = True
except ImportError:
YAML_IMP_ERR = traceback.format_exc()
HAS_LIB_YAML = False
CS_METADATA_BASE_URL = "http://%s/latest/meta-data"
CS_USERDATA_BASE_URL = "http://%s/latest/user-data"
class CloudStackFacts(object):
def __init__(self):
collector = ansible_collector.get_ansible_collector(all_collector_classes=default_collectors.collectors,
filter_spec='default_ipv4',
gather_subset=['!all', 'network'],
gather_timeout=10)
self.facts = collector.collect(module)
self.api_ip = None
self.fact_paths = {
'cloudstack_service_offering': 'service-offering',
'cloudstack_availability_zone': 'availability-zone',
'cloudstack_public_hostname': 'public-hostname',
'cloudstack_public_ipv4': 'public-ipv4',
'cloudstack_local_hostname': 'local-hostname',
'cloudstack_local_ipv4': 'local-ipv4',
'cloudstack_instance_id': 'instance-id'
}
def run(self):
result = {}
filter = module.params.get('filter')
if not filter:
for key, path in self.fact_paths.items():
result[key] = self._fetch(CS_METADATA_BASE_URL + "/" + path)
result['cloudstack_user_data'] = self._get_user_data_json()
else:
if filter == 'cloudstack_user_data':
result['cloudstack_user_data'] = self._get_user_data_json()
elif filter in self.fact_paths:
result[filter] = self._fetch(CS_METADATA_BASE_URL + "/" + self.fact_paths[filter])
return result
def _get_user_data_json(self):
try:
# this data come form users, we try what we can to parse it...
return yaml.safe_load(self._fetch(CS_USERDATA_BASE_URL))
except Exception:
return None
def _fetch(self, path):
api_ip = self._get_api_ip()
if not api_ip:
return None
api_url = path % api_ip
(response, info) = fetch_url(module, api_url, force=True)
if response:
data = response.read()
else:
data = None
return data
def _get_dhcp_lease_file(self):
"""Return the path of the lease file."""
default_iface = self.facts['default_ipv4']['interface']
dhcp_lease_file_locations = [
'/var/lib/dhcp/dhclient.%s.leases' % default_iface, # debian / ubuntu
'/var/lib/dhclient/dhclient-%s.leases' % default_iface, # centos 6
'/var/lib/dhclient/dhclient--%s.lease' % default_iface, # centos 7
'/var/db/dhclient.leases.%s' % default_iface, # openbsd
]
for file_path in dhcp_lease_file_locations:
if os.path.exists(file_path):
return file_path
module.fail_json(msg="Could not find dhclient leases file.")
def _get_api_ip(self):
"""Return the IP of the DHCP server."""
if module.params.get('meta_data_host'):
return module.params.get('meta_data_host')
elif not self.api_ip:
dhcp_lease_file = self._get_dhcp_lease_file()
for line in open(dhcp_lease_file):
if 'dhcp-server-identifier' in line:
# get IP of string "option dhcp-server-identifier 185.19.28.176;"
line = line.translate(None, ';')
self.api_ip = line.split()[2]
break
if not self.api_ip:
module.fail_json(msg="No dhcp-server-identifier found in leases file.")
return self.api_ip
def main():
global module
module = AnsibleModule(
argument_spec=dict(
filter=dict(default=None, choices=[
'cloudstack_service_offering',
'cloudstack_availability_zone',
'cloudstack_public_hostname',
'cloudstack_public_ipv4',
'cloudstack_local_hostname',
'cloudstack_local_ipv4',
'cloudstack_instance_id',
'cloudstack_user_data',
]),
meta_data_host=dict(),
),
supports_check_mode=True
)
if not HAS_LIB_YAML:
module.fail_json(msg=missing_required_lib("PyYAML"), exception=YAML_IMP_ERR)
cs_facts = CloudStackFacts().run()
cs_facts_result = dict(changed=False, ansible_facts=cs_facts)
module.exit_json(**cs_facts_result)
if __name__ == '__main__':
main()

View file

@ -1,447 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright: (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_firewall
short_description: Manages firewall rules on Apache CloudStack based clouds.
description:
- Creates and removes firewall rules.
author: René Moser (@resmo)
options:
ip_address:
description:
- Public IP address the ingress rule is assigned to.
- Required if I(type=ingress).
type: str
network:
description:
- Network the egress rule is related to.
- Required if I(type=egress).
type: str
state:
description:
- State of the firewall rule.
type: str
default: present
choices: [ present, absent ]
type:
description:
- Type of the firewall rule.
type: str
default: ingress
choices: [ ingress, egress ]
protocol:
description:
- Protocol of the firewall rule.
- C(all) is only available if I(type=egress).
type: str
default: tcp
choices: [ tcp, udp, icmp, all ]
cidrs:
description:
- List of CIDRs (full notation) to be used for firewall rule.
- Since version 2.5, it is a list of CIDR.
type: list
default: 0.0.0.0/0
aliases: [ cidr ]
start_port:
description:
- Start port for this rule.
- Considered if I(protocol=tcp) or I(protocol=udp).
type: int
aliases: [ port ]
end_port:
description:
- End port for this rule. Considered if I(protocol=tcp) or I(protocol=udp).
- If not specified, equal I(start_port).
type: int
icmp_type:
description:
- Type of the icmp message being sent.
- Considered if I(protocol=icmp).
type: int
icmp_code:
description:
- Error code for this icmp message.
- Considered if I(protocol=icmp).
type: int
domain:
description:
- Domain the firewall rule is related to.
type: str
account:
description:
- Account the firewall rule is related to.
type: str
project:
description:
- Name of the project the firewall rule is related to.
type: str
zone:
description:
- Name of the zone in which the virtual machine is in.
- If not set, default zone is used.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- "To delete all tags, set an empty list e.g. I(tags: [])."
type: list
aliases: [ tag ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Allow inbound port 80/tcp from 1.2.3.4 to 4.3.2.1
cs_firewall:
ip_address: 4.3.2.1
port: 80
cidr: 1.2.3.4/32
delegate_to: localhost
- name: Allow inbound tcp/udp port 53 to 4.3.2.1
cs_firewall:
ip_address: 4.3.2.1
port: 53
protocol: '{{ item }}'
with_items:
- tcp
- udp
delegate_to: localhost
- name: Ensure firewall rule is removed
cs_firewall:
ip_address: 4.3.2.1
start_port: 8000
end_port: 8888
cidr: 17.0.0.0/8
state: absent
delegate_to: localhost
- name: Allow all outbound traffic
cs_firewall:
network: my_network
type: egress
protocol: all
delegate_to: localhost
- name: Allow only HTTP outbound traffic for an IP
cs_firewall:
network: my_network
type: egress
port: 80
cidr: 10.101.1.20
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the rule.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
ip_address:
description: IP address of the rule if C(type=ingress)
returned: success
type: str
sample: 10.100.212.10
type:
description: Type of the rule.
returned: success
type: str
sample: ingress
cidr:
description: CIDR string of the rule.
returned: success
type: str
sample: 0.0.0.0/0
cidrs:
description: CIDR list of the rule.
returned: success
type: list
sample: [ '0.0.0.0/0' ]
protocol:
description: Protocol of the rule.
returned: success
type: str
sample: tcp
start_port:
description: Start port of the rule.
returned: success
type: int
sample: 80
end_port:
description: End port of the rule.
returned: success
type: int
sample: 80
icmp_code:
description: ICMP code of the rule.
returned: success
type: int
sample: 1
icmp_type:
description: ICMP type of the rule.
returned: success
type: int
sample: 1
network:
description: Name of the network if C(type=egress)
returned: success
type: str
sample: my_network
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackFirewall(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackFirewall, self).__init__(module)
self.returns = {
'cidrlist': 'cidr',
'startport': 'start_port',
'endport': 'end_port',
'protocol': 'protocol',
'ipaddress': 'ip_address',
'icmpcode': 'icmp_code',
'icmptype': 'icmp_type',
}
self.firewall_rule = None
self.network = None
def get_firewall_rule(self):
if not self.firewall_rule:
cidrs = self.module.params.get('cidrs')
protocol = self.module.params.get('protocol')
start_port = self.module.params.get('start_port')
end_port = self.get_or_fallback('end_port', 'start_port')
icmp_code = self.module.params.get('icmp_code')
icmp_type = self.module.params.get('icmp_type')
fw_type = self.module.params.get('type')
if protocol in ['tcp', 'udp'] and not (start_port and end_port):
self.module.fail_json(msg="missing required argument for protocol '%s': start_port or end_port" % protocol)
if protocol == 'icmp' and not icmp_type:
self.module.fail_json(msg="missing required argument for protocol 'icmp': icmp_type")
if protocol == 'all' and fw_type != 'egress':
self.module.fail_json(msg="protocol 'all' could only be used for type 'egress'")
args = {
'account': self.get_account('name'),
'domainid': self.get_domain('id'),
'projectid': self.get_project('id'),
'fetch_list': True,
}
if fw_type == 'egress':
args['networkid'] = self.get_network(key='id')
if not args['networkid']:
self.module.fail_json(msg="missing required argument for type egress: network")
# CloudStack 4.11 use the network cidr for 0.0.0.0/0 in egress
# That is why we need to replace it.
network_cidr = self.get_network(key='cidr')
egress_cidrs = [network_cidr if cidr == '0.0.0.0/0' else cidr for cidr in cidrs]
firewall_rules = self.query_api('listEgressFirewallRules', **args)
else:
args['ipaddressid'] = self.get_ip_address('id')
if not args['ipaddressid']:
self.module.fail_json(msg="missing required argument for type ingress: ip_address")
egress_cidrs = None
firewall_rules = self.query_api('listFirewallRules', **args)
if firewall_rules:
for rule in firewall_rules:
type_match = self._type_cidrs_match(rule, cidrs, egress_cidrs)
protocol_match = (
self._tcp_udp_match(rule, protocol, start_port, end_port) or
self._icmp_match(rule, protocol, icmp_code, icmp_type) or
self._egress_all_match(rule, protocol, fw_type)
)
if type_match and protocol_match:
self.firewall_rule = rule
break
return self.firewall_rule
def _tcp_udp_match(self, rule, protocol, start_port, end_port):
return (
protocol in ['tcp', 'udp'] and
protocol == rule['protocol'] and
start_port == int(rule['startport']) and
end_port == int(rule['endport'])
)
def _egress_all_match(self, rule, protocol, fw_type):
return (
protocol in ['all'] and
protocol == rule['protocol'] and
fw_type == 'egress'
)
def _icmp_match(self, rule, protocol, icmp_code, icmp_type):
return (
protocol == 'icmp' and
protocol == rule['protocol'] and
icmp_code == rule['icmpcode'] and
icmp_type == rule['icmptype']
)
def _type_cidrs_match(self, rule, cidrs, egress_cidrs):
if egress_cidrs is not None:
return ",".join(egress_cidrs) == rule['cidrlist'] or ",".join(cidrs) == rule['cidrlist']
else:
return ",".join(cidrs) == rule['cidrlist']
def create_firewall_rule(self):
firewall_rule = self.get_firewall_rule()
if not firewall_rule:
self.result['changed'] = True
args = {
'cidrlist': self.module.params.get('cidrs'),
'protocol': self.module.params.get('protocol'),
'startport': self.module.params.get('start_port'),
'endport': self.get_or_fallback('end_port', 'start_port'),
'icmptype': self.module.params.get('icmp_type'),
'icmpcode': self.module.params.get('icmp_code')
}
fw_type = self.module.params.get('type')
if not self.module.check_mode:
if fw_type == 'egress':
args['networkid'] = self.get_network(key='id')
res = self.query_api('createEgressFirewallRule', **args)
else:
args['ipaddressid'] = self.get_ip_address('id')
res = self.query_api('createFirewallRule', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
firewall_rule = self.poll_job(res, 'firewallrule')
if firewall_rule:
firewall_rule = self.ensure_tags(resource=firewall_rule, resource_type='Firewallrule')
self.firewall_rule = firewall_rule
return firewall_rule
def remove_firewall_rule(self):
firewall_rule = self.get_firewall_rule()
if firewall_rule:
self.result['changed'] = True
args = {
'id': firewall_rule['id']
}
fw_type = self.module.params.get('type')
if not self.module.check_mode:
if fw_type == 'egress':
res = self.query_api('deleteEgressFirewallRule', **args)
else:
res = self.query_api('deleteFirewallRule', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'firewallrule')
return firewall_rule
def get_result(self, firewall_rule):
super(AnsibleCloudStackFirewall, self).get_result(firewall_rule)
if firewall_rule:
self.result['type'] = self.module.params.get('type')
if self.result['type'] == 'egress':
self.result['network'] = self.get_network(key='displaytext')
if 'cidrlist' in firewall_rule:
self.result['cidrs'] = firewall_rule['cidrlist'].split(',') or [firewall_rule['cidrlist']]
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
ip_address=dict(),
network=dict(),
cidrs=dict(type='list', default='0.0.0.0/0', aliases=['cidr']),
protocol=dict(choices=['tcp', 'udp', 'icmp', 'all'], default='tcp'),
type=dict(choices=['ingress', 'egress'], default='ingress'),
icmp_type=dict(type='int'),
icmp_code=dict(type='int'),
start_port=dict(type='int', aliases=['port']),
end_port=dict(type='int'),
state=dict(choices=['present', 'absent'], default='present'),
zone=dict(),
domain=dict(),
account=dict(),
project=dict(),
poll_async=dict(type='bool', default=True),
tags=dict(type='list', aliases=['tag'], default=None),
))
required_together = cs_required_together()
required_together.extend([
['icmp_type', 'icmp_code'],
])
module = AnsibleModule(
argument_spec=argument_spec,
required_together=required_together,
required_one_of=(
['ip_address', 'network'],
),
mutually_exclusive=(
['icmp_type', 'start_port'],
['icmp_type', 'end_port'],
['ip_address', 'network'],
),
supports_check_mode=True
)
acs_fw = AnsibleCloudStackFirewall(module)
state = module.params.get('state')
if state in ['absent']:
fw_rule = acs_fw.remove_firewall_rule()
else:
fw_rule = acs_fw.create_firewall_rule()
result = acs_fw.get_result(fw_rule)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,627 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_host
short_description: Manages hosts on Apache CloudStack based clouds.
description:
- Create, update and remove hosts.
author: René Moser (@resmo)
options:
name:
description:
- Name of the host.
type: str
required: true
aliases: [ ip_address ]
url:
description:
- Url of the host used to create a host.
- If not provided, C(http://) and param I(name) is used as url.
- Only considered if I(state=present) and host does not yet exist.
type: str
username:
description:
- Username for the host.
- Required if I(state=present) and host does not yet exist.
type: str
password:
description:
- Password for the host.
- Required if I(state=present) and host does not yet exist.
type: str
pod:
description:
- Name of the pod.
- Required if I(state=present) and host does not yet exist.
type: str
cluster:
description:
- Name of the cluster.
type: str
hypervisor:
description:
- Name of the cluster.
- Required if I(state=present) and host does not yet exist.
- Possible values are C(KVM), C(VMware), C(BareMetal), C(XenServer), C(LXC), C(HyperV), C(UCS), C(OVM), C(Simulator).
type: str
allocation_state:
description:
- Allocation state of the host.
type: str
choices: [ enabled, disabled, maintenance ]
host_tags:
description:
- Tags of the host.
type: list
aliases: [ host_tag ]
state:
description:
- State of the host.
type: str
default: present
choices: [ present, absent ]
zone:
description:
- Name of the zone in which the host should be deployed.
- If not set, default zone is used.
type: str
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Ensure a host is present but disabled
cs_host:
name: pod01.zone01.example.com
cluster: vcenter.example.com/zone01/cluster01
pod: pod01
zone: zone01
hypervisor: VMware
allocation_state: disabled
host_tags:
- perf
- gpu
delegate_to: localhost
- name: Ensure an existing host is disabled
cs_host:
name: pod01.zone01.example.com
zone: zone01
allocation_state: disabled
delegate_to: localhost
- name: Ensure an existing host is enabled
cs_host:
name: pod01.zone01.example.com
zone: zone01
allocation_state: enabled
delegate_to: localhost
- name: Ensure a host is absent
cs_host:
name: pod01.zone01.example.com
zone: zone01
state: absent
delegate_to: localhost
'''
RETURN = '''
---
capabilities:
description: Capabilities of the host.
returned: success
type: str
sample: hvm
cluster:
description: Cluster of the host.
returned: success
type: str
sample: vcenter.example.com/zone/cluster01
cluster_type:
description: Type of the cluster of the host.
returned: success
type: str
sample: ExternalManaged
cpu_allocated:
description: Amount in percent of the host's CPU currently allocated.
returned: success
type: str
sample: 166.25%
cpu_number:
description: Number of CPUs of the host.
returned: success
type: str
sample: 24
cpu_sockets:
description: Number of CPU sockets of the host.
returned: success
type: int
sample: 2
cpu_speed:
description: CPU speed in Mhz
returned: success
type: int
sample: 1999
cpu_used:
description: Amount of the host's CPU currently used.
returned: success
type: str
sample: 33.6%
cpu_with_overprovisioning:
description: Amount of the host's CPU after applying the cpu.overprovisioning.factor.
returned: success
type: str
sample: 959520.0
created:
description: Date when the host was created.
returned: success
type: str
sample: 2015-05-03T15:05:51+0200
disconnected:
description: Date when the host was disconnected.
returned: success
type: str
sample: 2015-05-03T15:05:51+0200
disk_size_allocated:
description: Host's currently allocated disk size.
returned: success
type: int
sample: 2593
disk_size_total:
description: Total disk size of the host
returned: success
type: int
sample: 259300
events:
description: Events available for the host
returned: success
type: str
sample: "Ping; HostDown; AgentConnected; AgentDisconnected; PingTimeout; ShutdownRequested; Remove; StartAgentRebalance; ManagementServerDown"
ha_host:
description: Whether the host is a HA host.
returned: success
type: bool
sample: false
has_enough_capacity:
description: Whether the host has enough CPU and RAM capacity to migrate a VM to it.
returned: success
type: bool
sample: true
host_tags:
description: Comma-separated list of tags for the host.
returned: success
type: str
sample: "perf"
hypervisor:
description: Host's hypervisor.
returned: success
type: str
sample: VMware
hypervisor_version:
description: Hypervisor version.
returned: success
type: str
sample: 5.1
ip_address:
description: IP address of the host
returned: success
type: str
sample: 10.10.10.1
is_local_storage_active:
description: Whether the local storage is available or not.
returned: success
type: bool
sample: false
last_pinged:
description: Date and time the host was last pinged.
returned: success
type: str
sample: "1970-01-17T17:27:32+0100"
management_server_id:
description: Management server ID of the host.
returned: success
type: int
sample: 345050593418
memory_allocated:
description: Amount of the host's memory currently allocated.
returned: success
type: int
sample: 69793218560
memory_total:
description: Total of memory of the host.
returned: success
type: int
sample: 206085263360
memory_used:
description: Amount of the host's memory currently used.
returned: success
type: int
sample: 65504776192
name:
description: Name of the host.
returned: success
type: str
sample: esx32.example.com
network_kbs_read:
description: Incoming network traffic on the host.
returned: success
type: int
sample: 0
network_kbs_write:
description: Outgoing network traffic on the host.
returned: success
type: int
sample: 0
os_category:
description: OS category name of the host.
returned: success
type: str
sample: ...
out_of_band_management:
description: Host out-of-band management information.
returned: success
type: str
sample: ...
pod:
description: Pod name of the host.
returned: success
type: str
sample: Pod01
removed:
description: Date and time the host was removed.
returned: success
type: str
sample: "1970-01-17T17:27:32+0100"
resource_state:
description: Resource state of the host.
returned: success
type: str
sample: Enabled
allocation_state::
description: Allocation state of the host.
returned: success
type: str
sample: enabled
state:
description: State of the host.
returned: success
type: str
sample: Up
suitable_for_migration:
description: Whether this host is suitable (has enough capacity and satisfies all conditions like hosttags, max guests VM limit, etc) to migrate a VM
to it or not.
returned: success
type: str
sample: true
host_type:
description: Type of the host.
returned: success
type: str
sample: Routing
host_version:
description: Version of the host.
returned: success
type: str
sample: 4.5.2
gpu_group:
description: GPU cards present in the host.
returned: success
type: list
sample: []
zone:
description: Zone of the host.
returned: success
type: str
sample: zone01
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
import time
class AnsibleCloudStackHost(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackHost, self).__init__(module)
self.returns = {
'averageload': 'average_load',
'capabilities': 'capabilities',
'clustername': 'cluster',
'clustertype': 'cluster_type',
'cpuallocated': 'cpu_allocated',
'cpunumber': 'cpu_number',
'cpusockets': 'cpu_sockets',
'cpuspeed': 'cpu_speed',
'cpuused': 'cpu_used',
'cpuwithoverprovisioning': 'cpu_with_overprovisioning',
'disconnected': 'disconnected',
'details': 'details',
'disksizeallocated': 'disk_size_allocated',
'disksizetotal': 'disk_size_total',
'events': 'events',
'hahost': 'ha_host',
'hasenoughcapacity': 'has_enough_capacity',
'hypervisor': 'hypervisor',
'hypervisorversion': 'hypervisor_version',
'ipaddress': 'ip_address',
'islocalstorageactive': 'is_local_storage_active',
'lastpinged': 'last_pinged',
'managementserverid': 'management_server_id',
'memoryallocated': 'memory_allocated',
'memorytotal': 'memory_total',
'memoryused': 'memory_used',
'networkkbsread': 'network_kbs_read',
'networkkbswrite': 'network_kbs_write',
'oscategoryname': 'os_category',
'outofbandmanagement': 'out_of_band_management',
'podname': 'pod',
'removed': 'removed',
'resourcestate': 'resource_state',
'suitableformigration': 'suitable_for_migration',
'type': 'host_type',
'version': 'host_version',
'gpugroup': 'gpu_group',
}
# States only usable by the updateHost API
self.allocation_states_for_update = {
'enabled': 'Enable',
'disabled': 'Disable',
}
self.host = None
def get_pod(self, key=None):
pod_name = self.module.params.get('pod')
if not pod_name:
return None
args = {
'name': pod_name,
'zoneid': self.get_zone(key='id'),
}
pods = self.query_api('listPods', **args)
if pods:
return self._get_by_key(key, pods['pod'][0])
self.module.fail_json(msg="Pod %s not found" % pod_name)
def get_cluster(self, key=None):
cluster_name = self.module.params.get('cluster')
if not cluster_name:
return None
args = {
'name': cluster_name,
'zoneid': self.get_zone(key='id'),
}
clusters = self.query_api('listClusters', **args)
if clusters:
return self._get_by_key(key, clusters['cluster'][0])
self.module.fail_json(msg="Cluster %s not found" % cluster_name)
def get_host_tags(self):
host_tags = self.module.params.get('host_tags')
if host_tags is None:
return None
return ','.join(host_tags)
def get_host(self, refresh=False):
if self.host is not None and not refresh:
return self.host
name = self.module.params.get('name')
args = {
'zoneid': self.get_zone(key='id'),
'fetch_list': True,
}
res = self.query_api('listHosts', **args)
if res:
for h in res:
if name in [h['ipaddress'], h['name']]:
self.host = h
return self.host
def _handle_allocation_state(self, host):
allocation_state = self.module.params.get('allocation_state')
if not allocation_state:
return host
host = self._set_host_allocation_state(host)
# In case host in maintenance and target is maintenance
if host['allocationstate'].lower() == allocation_state and allocation_state == 'maintenance':
return host
# Cancel maintenance if target state is enabled/disabled
elif allocation_state in list(self.allocation_states_for_update.keys()):
host = self.disable_maintenance(host)
host = self._update_host(host, self.allocation_states_for_update[allocation_state])
# Only an enabled host can put in maintenance
elif allocation_state == 'maintenance':
host = self._update_host(host, 'Enable')
host = self.enable_maintenance(host)
return host
def _set_host_allocation_state(self, host):
if host is None:
host['allocationstate'] = 'Enable'
# Set host allocationstate to be disabled/enabled
elif host['resourcestate'].lower() in list(self.allocation_states_for_update.keys()):
host['allocationstate'] = self.allocation_states_for_update[host['resourcestate'].lower()]
else:
host['allocationstate'] = host['resourcestate']
return host
def present_host(self):
host = self.get_host()
if not host:
host = self._create_host(host)
else:
host = self._update_host(host)
if host:
host = self._handle_allocation_state(host)
return host
def _get_url(self):
url = self.module.params.get('url')
if url:
return url
else:
return "http://%s" % self.module.params.get('name')
def _create_host(self, host):
required_params = [
'password',
'username',
'hypervisor',
'pod',
]
self.module.fail_on_missing_params(required_params=required_params)
self.result['changed'] = True
args = {
'hypervisor': self.module.params.get('hypervisor'),
'url': self._get_url(),
'username': self.module.params.get('username'),
'password': self.module.params.get('password'),
'podid': self.get_pod(key='id'),
'zoneid': self.get_zone(key='id'),
'clusterid': self.get_cluster(key='id'),
'hosttags': self.get_host_tags(),
}
if not self.module.check_mode:
host = self.query_api('addHost', **args)
host = host['host'][0]
return host
def _update_host(self, host, allocation_state=None):
args = {
'id': host['id'],
'hosttags': self.get_host_tags(),
'allocationstate': allocation_state,
}
if allocation_state is not None:
host = self._set_host_allocation_state(host)
if self.has_changed(args, host):
self.result['changed'] = True
if not self.module.check_mode:
host = self.query_api('updateHost', **args)
host = host['host']
return host
def absent_host(self):
host = self.get_host()
if host:
self.result['changed'] = True
args = {
'id': host['id'],
}
if not self.module.check_mode:
res = self.enable_maintenance(host)
if res:
res = self.query_api('deleteHost', **args)
return host
def enable_maintenance(self, host):
if host['resourcestate'] not in ['PrepareForMaintenance', 'Maintenance']:
self.result['changed'] = True
args = {
'id': host['id'],
}
if not self.module.check_mode:
res = self.query_api('prepareHostForMaintenance', **args)
self.poll_job(res, 'host')
host = self._poll_for_maintenance()
return host
def disable_maintenance(self, host):
if host['resourcestate'] in ['PrepareForMaintenance', 'Maintenance']:
self.result['changed'] = True
args = {
'id': host['id'],
}
if not self.module.check_mode:
res = self.query_api('cancelHostMaintenance', **args)
host = self.poll_job(res, 'host')
return host
def _poll_for_maintenance(self):
for i in range(0, 300):
time.sleep(2)
host = self.get_host(refresh=True)
if not host:
return None
elif host['resourcestate'] != 'PrepareForMaintenance':
return host
self.fail_json(msg="Polling for maintenance timed out")
def get_result(self, host):
super(AnsibleCloudStackHost, self).get_result(host)
if host:
self.result['allocation_state'] = host['resourcestate'].lower()
self.result['host_tags'] = host['hosttags'].split(',') if host.get('hosttags') else []
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True, aliases=['ip_address']),
url=dict(),
password=dict(no_log=True),
username=dict(),
hypervisor=dict(),
allocation_state=dict(choices=['enabled', 'disabled', 'maintenance']),
pod=dict(),
cluster=dict(),
host_tags=dict(type='list', aliases=['host_tag']),
zone=dict(),
state=dict(choices=['present', 'absent'], default='present'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_host = AnsibleCloudStackHost(module)
state = module.params.get('state')
if state == 'absent':
host = acs_host.absent_host()
else:
host = acs_host.present_host()
result = acs_host.get_result(host)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,252 +0,0 @@
#!/usr/bin/python
# Copyright: (c) 2019, Patryk Cichy @PatTheSilent
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
---
module: cs_image_store
short_description: Manages CloudStack Image Stores.
description:
- Deploy, remove, recreate CloudStack Image Stores.
options:
url:
description:
- The URL for the Image Store.
- Required when I(state=present).
type: str
name:
description:
- The ID of the Image Store. Required when deleting a Image Store.
required: true
type: str
zone:
description:
- The Zone name for the Image Store.
required: true
type: str
state:
description:
- Stage of the Image Store
choices: [present, absent]
default: present
type: str
provider:
description:
- The image store provider name. Required when creating a new Image Store
type: str
force_recreate:
description:
- Set to C(yes) if you're changing an existing Image Store.
- This will force the recreation of the Image Store.
- Recreation might fail if there are snapshots present on the Image Store. Delete them before running the recreation.
type: bool
default: no
extends_documentation_fragment:
- community.general.cloudstack
author:
- Patryk Cichy (@PatTheSilent)
'''
EXAMPLES = '''
- name: Add a Image Store (NFS)
cs_image_store:
zone: zone-01
name: nfs-01
provider: NFS
url: nfs://192.168.21.16/exports/secondary
delegate_to: localhost
# Change the NFS share URL and force a Image Store recreation
- name: Change the NFS url
cs_image_store:
zone: zone-01
name: nfs-01
provider: NFS
force_recreate: yes
url: nfs://192.168.21.10/shares/secondary
delegate_to: localhost
- name: delete the image store
cs_image_store:
name: nfs-01
zone: zone-01
state: absent
delegate_to: localhost
'''
RETURN = '''
id:
description: the ID of the image store
type: str
returned: success
sample: feb11a84-a093-45eb-b84d-7f680313c40b
name:
description: the name of the image store
type: str
returned: success
sample: nfs-01
protocol:
description: the protocol of the image store
type: str
returned: success
sample: nfs
provider_name:
description: the provider name of the image store
type: str
returned: success
sample: NFS
scope:
description: the scope of the image store
type: str
returned: success
sample: ZONE
url:
description: the url of the image store
type: str
sample: nfs://192.168.21.16/exports/secondary
returned: success
zone:
description: the Zone name of the image store
type: str
returned: success
sample: zone-01
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import AnsibleCloudStack, cs_argument_spec, cs_required_together
class AnsibleCloudstackImageStore(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudstackImageStore, self).__init__(module)
self.returns = {
'protocol': 'protocol',
'providername': 'provider_name',
'scope': 'scope',
'url': 'url'
}
self.image_store = None
def get_storage_providers(self, storage_type="image"):
args = {
'type': storage_type
}
storage_provides = self.query_api('listStorageProviders', **args)
return [provider.get('name') for provider in storage_provides.get('dataStoreProvider')]
def get_image_store(self):
if self.image_store:
return self.image_store
image_store = self.module.params.get('name')
args = {
'name': self.module.params.get('name'),
'zoneid': self.get_zone(key='id')
}
image_stores = self.query_api('listImageStores', **args)
if image_stores:
for img_s in image_stores.get('imagestore'):
if image_store.lower() in [img_s['name'].lower(), img_s['id']]:
self.image_store = img_s
break
return self.image_store
def present_image_store(self):
provider_list = self.get_storage_providers()
image_store = self.get_image_store()
if self.module.params.get('provider') not in provider_list:
self.module.fail_json(
msg='Provider %s is not in the provider list (%s). Please specify a correct provider' % (
self.module.params.get('provider'), provider_list))
args = {
'name': self.module.params.get('name'),
'url': self.module.params.get('url'),
'zoneid': self.get_zone(key='id'),
'provider': self.module.params.get('provider')
}
if not image_store:
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('addImageStore', **args)
self.image_store = res.get('imagestore')
else:
# Cloudstack API expects 'provider' but returns 'providername'
args['providername'] = args.pop('provider')
if self.has_changed(args, image_store):
if self.module.params.get('force_recreate'):
self.absent_image_store()
self.image_store = None
self.image_store = self.present_image_store()
else:
self.module.warn("Changes to the Image Store won't be applied"
"Use force_recreate=yes to allow the store to be recreated")
return self.image_store
def absent_image_store(self):
image_store = self.get_image_store()
if image_store:
self.result['changed'] = True
if not self.module.check_mode:
args = {
'id': image_store.get('id')
}
self.query_api('deleteImageStore', **args)
return image_store
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
url=dict(),
name=dict(required=True),
zone=dict(required=True),
provider=dict(),
force_recreate=dict(type='bool', default=False),
state=dict(choices=['present', 'absent'], default='present'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
required_if=[
('state', 'present', ['url', 'provider']),
],
supports_check_mode=True
)
acis_do = AnsibleCloudstackImageStore(module)
state = module.params.get('state')
if state == "absent":
image_store = acis_do.absent_image_store()
else:
image_store = acis_do.present_image_store()
result = acis_do.get_result(image_store)
module.exit_json(**result)
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load diff

View file

@ -1,373 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_instance_facts
short_description: Gathering facts from the API of instances from Apache CloudStack based clouds.
description:
- Gathering facts from the API of an instance.
deprecated:
removed_in: "2.13"
why: Transformed into an info module.
alternative: Use M(cs_instance_info) instead.
author: René Moser (@resmo)
options:
name:
description:
- Name or display name of the instance.
type: str
required: true
domain:
description:
- Domain the instance is related to.
type: str
account:
description:
- Account the instance is related to.
type: str
project:
description:
- Project the instance is related to.
type: str
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: gather instance facts
cs_instance_facts:
name: web-vm-1
delegate_to: localhost
register: vm
- debug:
var: cloudstack_instance
- debug:
var: vm
'''
RETURN = '''
---
id:
description: UUID of the instance.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the instance.
returned: success
type: str
sample: web-01
display_name:
description: Display name of the instance.
returned: success
type: str
sample: web-01
group:
description: Group name of the instance is related.
returned: success
type: str
sample: web
created:
description: Date of the instance was created.
returned: success
type: str
sample: 2014-12-01T14:57:57+0100
password_enabled:
description: True if password setting is enabled.
returned: success
type: bool
sample: true
password:
description: The password of the instance if exists.
returned: success
type: str
sample: Ge2oe7Do
ssh_key:
description: Name of SSH key deployed to instance.
returned: success
type: str
sample: key@work
domain:
description: Domain the instance is related to.
returned: success
type: str
sample: example domain
account:
description: Account the instance is related to.
returned: success
type: str
sample: example account
project:
description: Name of project the instance is related to.
returned: success
type: str
sample: Production
default_ip:
description: Default IP address of the instance.
returned: success
type: str
sample: 10.23.37.42
public_ip:
description: Public IP address with instance via static NAT rule.
returned: success
type: str
sample: 1.2.3.4
iso:
description: Name of ISO the instance was deployed with.
returned: success
type: str
sample: Debian-8-64bit
template:
description: Name of template the instance was deployed with.
returned: success
type: str
sample: Debian-8-64bit
service_offering:
description: Name of the service offering the instance has.
returned: success
type: str
sample: 2cpu_2gb
zone:
description: Name of zone the instance is in.
returned: success
type: str
sample: ch-gva-2
state:
description: State of the instance.
returned: success
type: str
sample: Running
security_groups:
description: Security groups the instance is in.
returned: success
type: list
sample: '[ "default" ]'
affinity_groups:
description: Affinity groups the instance is in.
returned: success
type: list
sample: '[ "webservers" ]'
tags:
description: List of resource tags associated with the instance.
returned: success
type: list
sample: '[ { "key": "foo", "value": "bar" } ]'
hypervisor:
description: Hypervisor related to this instance.
returned: success
type: str
sample: KVM
host:
description: Host the instance is running on.
returned: success and instance is running
type: str
sample: host01.example.com
instance_name:
description: Internal name of the instance (ROOT admin only).
returned: success
type: str
sample: i-44-3992-VM
volumes:
description: List of dictionaries of the volumes attached to the instance.
returned: success
type: list
sample: '[ { name: "ROOT-1369", type: "ROOT", size: 10737418240 }, { name: "data01, type: "DATADISK", size: 10737418240 } ]'
nic:
description: List of dictionaries of the instance nics.
returned: success
type: complex
contains:
broadcasturi:
description: The broadcast uri of the nic.
returned: success
type: str
sample: vlan://2250
gateway:
description: The gateway of the nic.
returned: success
type: str
sample: 10.1.2.1
id:
description: The ID of the nic.
returned: success
type: str
sample: 5dc74fa3-2ec3-48a0-9e0d-6f43365336a9
ipaddress:
description: The ip address of the nic.
returned: success
type: str
sample: 10.1.2.3
isdefault:
description: True if nic is default, false otherwise.
returned: success
type: bool
sample: true
isolationuri:
description: The isolation uri of the nic.
returned: success
type: str
sample: vlan://2250
macaddress:
description: The mac address of the nic.
returned: success
type: str
sample: 06:a2:03:00:08:12
netmask:
description: The netmask of the nic.
returned: success
type: str
sample: 255.255.255.0
networkid:
description: The ID of the corresponding network.
returned: success
type: str
sample: 432ce27b-c2bb-4e12-a88c-a919cd3a3017
networkname:
description: The name of the corresponding network.
returned: success
type: str
sample: network1
traffictype:
description: The traffic type of the nic.
returned: success
type: str
sample: Guest
type:
description: The type of the network.
returned: success
type: str
sample: Shared
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import AnsibleCloudStack, cs_argument_spec
class AnsibleCloudStackInstanceFacts(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackInstanceFacts, self).__init__(module)
self.instance = None
self.returns = {
'group': 'group',
'hypervisor': 'hypervisor',
'instancename': 'instance_name',
'publicip': 'public_ip',
'passwordenabled': 'password_enabled',
'password': 'password',
'serviceofferingname': 'service_offering',
'isoname': 'iso',
'templatename': 'template',
'keypair': 'ssh_key',
'hostname': 'host',
}
self.facts = {
'cloudstack_instance': None,
}
def get_instance(self):
instance = self.instance
if not instance:
instance_name = self.module.params.get('name')
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'fetch_list': True,
}
# Do not pass zoneid, as the instance name must be unique across zones.
instances = self.query_api('listVirtualMachines', **args)
if instances:
for v in instances:
if instance_name.lower() in [v['name'].lower(), v['displayname'].lower(), v['id']]:
self.instance = v
break
return self.instance
def get_volumes(self, instance):
volume_details = []
if instance:
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'virtualmachineid': instance['id'],
'fetch_list': True,
}
volumes = self.query_api('listVolumes', **args)
if volumes:
for vol in volumes:
volume_details.append({'size': vol['size'], 'type': vol['type'], 'name': vol['name']})
return volume_details
def run(self):
instance = self.get_instance()
if not instance:
self.module.fail_json(msg="Instance not found: %s" % self.module.params.get('name'))
return instance
def get_result(self, instance):
super(AnsibleCloudStackInstanceFacts, self).get_result(instance)
if instance:
if 'securitygroup' in instance:
security_groups = []
for securitygroup in instance['securitygroup']:
security_groups.append(securitygroup['name'])
self.result['security_groups'] = security_groups
if 'affinitygroup' in instance:
affinity_groups = []
for affinitygroup in instance['affinitygroup']:
affinity_groups.append(affinitygroup['name'])
self.result['affinity_groups'] = affinity_groups
if 'nic' in instance:
for nic in instance['nic']:
if nic['isdefault'] and 'ipaddress' in nic:
self.result['default_ip'] = nic['ipaddress']
self.result['nic'] = instance['nic']
volumes = self.get_volumes(instance)
if volumes:
self.result['volumes'] = volumes
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
domain=dict(),
account=dict(),
project=dict(),
))
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)
acs_instance_facts = AnsibleCloudStackInstanceFacts(module=module)
cs_instance_facts = acs_instance_facts.get_result_and_facts(
facts_name='cloudstack_instance',
resource=acs_instance_facts.run()
)
module.exit_json(**cs_instance_facts)
if __name__ == '__main__':
main()

View file

@ -1,377 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_instance_info
short_description: Gathering information from the API of instances from Apache CloudStack based clouds.
description:
- Gathering information from the API of an instance.
author: René Moser (@resmo)
options:
name:
description:
- Name or display name of the instance.
- If not specified, all instances are returned
type: str
required: false
domain:
description:
- Domain the instance is related to.
type: str
account:
description:
- Account the instance is related to.
type: str
project:
description:
- Project the instance is related to.
type: str
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Gather instance information
cs_instance_info:
name: web-vm-1
delegate_to: localhost
register: vm
- name: Show the returned results of the registered variable
debug:
msg: "{{ vm }}"
- name: Gather information from all instances
cs_instance_info:
delegate_to: localhost
register: vms
- name: Show information on all instances
debug:
msg: "{{ vms }}"
'''
RETURN = '''
---
instances:
description: A list of matching instances.
type: list
returned: success
contains:
id:
description: UUID of the instance.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the instance.
returned: success
type: str
sample: web-01
display_name:
description: Display name of the instance.
returned: success
type: str
sample: web-01
group:
description: Group name of the instance is related.
returned: success
type: str
sample: web
created:
description: Date of the instance was created.
returned: success
type: str
sample: 2014-12-01T14:57:57+0100
password_enabled:
description: True if password setting is enabled.
returned: success
type: bool
sample: true
password:
description: The password of the instance if exists.
returned: success
type: str
sample: Ge2oe7Do
ssh_key:
description: Name of SSH key deployed to instance.
returned: success
type: str
sample: key@work
domain:
description: Domain the instance is related to.
returned: success
type: str
sample: example domain
account:
description: Account the instance is related to.
returned: success
type: str
sample: example account
project:
description: Name of project the instance is related to.
returned: success
type: str
sample: Production
default_ip:
description: Default IP address of the instance.
returned: success
type: str
sample: 10.23.37.42
public_ip:
description: Public IP address with instance via static NAT rule.
returned: success
type: str
sample: 1.2.3.4
iso:
description: Name of ISO the instance was deployed with.
returned: success
type: str
sample: Debian-8-64bit
template:
description: Name of template the instance was deployed with.
returned: success
type: str
sample: Debian-8-64bit
service_offering:
description: Name of the service offering the instance has.
returned: success
type: str
sample: 2cpu_2gb
zone:
description: Name of zone the instance is in.
returned: success
type: str
sample: ch-gva-2
state:
description: State of the instance.
returned: success
type: str
sample: Running
security_groups:
description: Security groups the instance is in.
returned: success
type: list
sample: '[ "default" ]'
affinity_groups:
description: Affinity groups the instance is in.
returned: success
type: list
sample: '[ "webservers" ]'
tags:
description: List of resource tags associated with the instance.
returned: success
type: list
sample: '[ { "key": "foo", "value": "bar" } ]'
hypervisor:
description: Hypervisor related to this instance.
returned: success
type: str
sample: KVM
host:
description: Host the instance is running on.
returned: success and instance is running
type: str
sample: host01.example.com
instance_name:
description: Internal name of the instance (ROOT admin only).
returned: success
type: str
sample: i-44-3992-VM
volumes:
description: List of dictionaries of the volumes attached to the instance.
returned: success
type: list
sample: '[ { name: "ROOT-1369", type: "ROOT", size: 10737418240 }, { name: "data01, type: "DATADISK", size: 10737418240 } ]'
nic:
description: List of dictionaries of the instance nics.
returned: success
type: complex
contains:
broadcasturi:
description: The broadcast uri of the nic.
returned: success
type: str
sample: vlan://2250
gateway:
description: The gateway of the nic.
returned: success
type: str
sample: 10.1.2.1
id:
description: The ID of the nic.
returned: success
type: str
sample: 5dc74fa3-2ec3-48a0-9e0d-6f43365336a9
ipaddress:
description: The ip address of the nic.
returned: success
type: str
sample: 10.1.2.3
isdefault:
description: True if nic is default, false otherwise.
returned: success
type: bool
sample: true
isolationuri:
description: The isolation uri of the nic.
returned: success
type: str
sample: vlan://2250
macaddress:
description: The mac address of the nic.
returned: success
type: str
sample: 06:a2:03:00:08:12
netmask:
description: The netmask of the nic.
returned: success
type: str
sample: 255.255.255.0
networkid:
description: The ID of the corresponding network.
returned: success
type: str
sample: 432ce27b-c2bb-4e12-a88c-a919cd3a3017
networkname:
description: The name of the corresponding network.
returned: success
type: str
sample: network1
traffictype:
description: The traffic type of the nic.
returned: success
type: str
sample: Guest
type:
description: The type of the network.
returned: success
type: str
sample: Shared
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import AnsibleCloudStack, cs_argument_spec
class AnsibleCloudStackInstanceInfo(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackInstanceInfo, self).__init__(module)
self.returns = {
'group': 'group',
'hypervisor': 'hypervisor',
'instancename': 'instance_name',
'publicip': 'public_ip',
'passwordenabled': 'password_enabled',
'password': 'password',
'serviceofferingname': 'service_offering',
'isoname': 'iso',
'templatename': 'template',
'keypair': 'ssh_key',
'hostname': 'host',
}
def get_instances(self):
instance_name = self.module.params.get('name')
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'fetch_list': True,
}
# Do not pass zoneid, as the instance name must be unique across zones.
instances = self.query_api('listVirtualMachines', **args)
if not instance_name:
return instances or []
if instances:
for v in instances:
if instance_name.lower() in [v['name'].lower(), v['displayname'].lower(), v['id']]:
return [v]
return []
def get_volumes(self, instance):
volume_details = []
if instance:
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'virtualmachineid': instance['id'],
'fetch_list': True,
}
volumes = self.query_api('listVolumes', **args)
if volumes:
for vol in volumes:
volume_details.append({'size': vol['size'], 'type': vol['type'], 'name': vol['name']})
return volume_details
def run(self):
instances = self.get_instances()
if self.module.params.get('name') and not instances:
self.module.fail_json(msg="Instance not found: %s" % self.module.params.get('name'))
return {
'instances': [self.update_result(resource) for resource in instances]
}
def update_result(self, instance, result=None):
result = super(AnsibleCloudStackInstanceInfo, self).update_result(instance, result)
if instance:
if 'securitygroup' in instance:
security_groups = []
for securitygroup in instance['securitygroup']:
security_groups.append(securitygroup['name'])
result['security_groups'] = security_groups
if 'affinitygroup' in instance:
affinity_groups = []
for affinitygroup in instance['affinitygroup']:
affinity_groups.append(affinitygroup['name'])
result['affinity_groups'] = affinity_groups
if 'nic' in instance:
for nic in instance['nic']:
if nic['isdefault'] and 'ipaddress' in nic:
result['default_ip'] = nic['ipaddress']
result['nic'] = instance['nic']
volumes = self.get_volumes(instance)
if volumes:
result['volumes'] = volumes
return result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(),
domain=dict(),
account=dict(),
project=dict(),
))
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)
acs_instance_info = AnsibleCloudStackInstanceInfo(module=module)
cs_instance_info = acs_instance_info.run()
module.exit_json(**cs_instance_info)
if __name__ == '__main__':
main()

View file

@ -1,290 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2017, Marc-Aurèle Brothier @marcaurele
# (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_instance_nic
short_description: Manages NICs of an instance on Apache CloudStack based clouds.
description:
- Add and remove nic to and from network
author:
- Marc-Aurèle Brothier (@marcaurele)
- René Moser (@resmo)
options:
vm:
description:
- Name of instance.
required: true
type: str
aliases: [ name ]
network:
description:
- Name of the network.
type: str
required: true
ip_address:
description:
- IP address to be used for the nic.
type: str
vpc:
description:
- Name of the VPC the I(vm) is related to.
type: str
domain:
description:
- Domain the instance is related to.
type: str
account:
description:
- Account the instance is related to.
type: str
project:
description:
- Name of the project the instance is deployed in.
type: str
zone:
description:
- Name of the zone in which the instance is deployed in.
- If not set, default zone is used.
type: str
state:
description:
- State of the nic.
type: str
default: present
choices: [ present, absent ]
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Add a nic on another network
cs_instance_nic:
vm: privnet
network: privNetForBasicZone
delegate_to: localhost
- name: Ensure IP address on a nic
cs_instance_nic:
vm: privnet
ip_address: 10.10.11.32
network: privNetForBasicZone
delegate_to: localhost
- name: Remove a secondary nic
cs_instance_nic:
vm: privnet
state: absent
network: privNetForBasicZone
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the nic.
returned: success
type: str
sample: 87b1e0ce-4e01-11e4-bb66-0050569e64b8
vm:
description: Name of the VM.
returned: success
type: str
sample: web-01
ip_address:
description: Primary IP of the NIC.
returned: success
type: str
sample: 10.10.10.10
netmask:
description: Netmask of the NIC.
returned: success
type: str
sample: 255.255.255.0
mac_address:
description: MAC address of the NIC.
returned: success
type: str
sample: 02:00:33:31:00:e4
network:
description: Name of the network if not default.
returned: success
type: str
sample: sync network
domain:
description: Domain the VM is related to.
returned: success
type: str
sample: example domain
account:
description: Account the VM is related to.
returned: success
type: str
sample: example account
project:
description: Name of project the VM is related to.
returned: success
type: str
sample: Production
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (AnsibleCloudStack,
cs_argument_spec,
cs_required_together)
class AnsibleCloudStackInstanceNic(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackInstanceNic, self).__init__(module)
self.nic = None
self.returns = {
'ipaddress': 'ip_address',
'macaddress': 'mac_address',
'netmask': 'netmask',
}
def get_nic(self):
if self.nic:
return self.nic
args = {
'virtualmachineid': self.get_vm(key='id'),
'networkid': self.get_network(key='id'),
}
nics = self.query_api('listNics', **args)
if nics:
self.nic = nics['nic'][0]
return self.nic
return None
def get_nic_from_result(self, result):
for nic in result.get('nic') or []:
if nic['networkid'] == self.get_network(key='id'):
return nic
def add_nic(self):
self.result['changed'] = True
args = {
'virtualmachineid': self.get_vm(key='id'),
'networkid': self.get_network(key='id'),
'ipaddress': self.module.params.get('ip_address'),
}
if not self.module.check_mode:
res = self.query_api('addNicToVirtualMachine', **args)
if self.module.params.get('poll_async'):
vm = self.poll_job(res, 'virtualmachine')
self.nic = self.get_nic_from_result(result=vm)
return self.nic
def update_nic(self, nic):
# Do not try to update if no IP address is given
ip_address = self.module.params.get('ip_address')
if not ip_address:
return nic
args = {
'nicid': nic['id'],
'ipaddress': ip_address,
}
if self.has_changed(args, nic, ['ipaddress']):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateVmNicIp', **args)
if self.module.params.get('poll_async'):
vm = self.poll_job(res, 'virtualmachine')
self.nic = self.get_nic_from_result(result=vm)
return self.nic
def remove_nic(self, nic):
self.result['changed'] = True
args = {
'virtualmachineid': self.get_vm(key='id'),
'nicid': nic['id'],
}
if not self.module.check_mode:
res = self.query_api('removeNicFromVirtualMachine', **args)
if self.module.params.get('poll_async'):
self.poll_job(res, 'virtualmachine')
return nic
def present_nic(self):
nic = self.get_nic()
if not nic:
nic = self.add_nic()
else:
nic = self.update_nic(nic)
return nic
def absent_nic(self):
nic = self.get_nic()
if nic:
return self.remove_nic(nic)
return nic
def get_result(self, nic):
super(AnsibleCloudStackInstanceNic, self).get_result(nic)
if nic and not self.module.params.get('network'):
self.module.params['network'] = nic.get('networkid')
self.result['network'] = self.get_network(key='name')
self.result['vm'] = self.get_vm(key='name')
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
vm=dict(required=True, aliases=['name']),
network=dict(required=True),
vpc=dict(),
ip_address=dict(),
state=dict(choices=['present', 'absent'], default='present'),
domain=dict(),
account=dict(),
project=dict(),
zone=dict(),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True,
)
acs_nic = AnsibleCloudStackInstanceNic(module)
state = module.params.get('state')
if state == 'absent':
nic = acs_nic.absent_nic()
else:
nic = acs_nic.present_nic()
result = acs_nic.get_result(nic)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,273 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_instance_nic_secondaryip
short_description: Manages secondary IPs of an instance on Apache CloudStack based clouds.
description:
- Add and remove secondary IPs to and from a NIC of an instance.
author: René Moser (@resmo)
options:
vm:
description:
- Name of instance.
type: str
required: true
aliases: [ name ]
network:
description:
- Name of the network.
- Required to find the NIC if instance has multiple networks assigned.
type: str
vm_guest_ip:
description:
- Secondary IP address to be added to the instance nic.
- If not set, the API always returns a new IP address and idempotency is not given.
type: str
aliases: [ secondary_ip ]
vpc:
description:
- Name of the VPC the I(vm) is related to.
type: str
domain:
description:
- Domain the instance is related to.
type: str
account:
description:
- Account the instance is related to.
type: str
project:
description:
- Name of the project the instance is deployed in.
type: str
zone:
description:
- Name of the zone in which the instance is deployed in.
- If not set, default zone is used.
type: str
state:
description:
- State of the ipaddress.
type: str
default: present
choices: [ present, absent ]
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Assign a specific IP to the default NIC of the VM
cs_instance_nic_secondaryip:
vm: customer_xy
vm_guest_ip: 10.10.10.10
delegate_to: localhost
# Note: If vm_guest_ip is not set, you will get a new IP address on every run.
- name: Assign an IP to the default NIC of the VM
cs_instance_nic_secondaryip:
vm: customer_xy
delegate_to: localhost
- name: Remove a specific IP from the default NIC
cs_instance_nic_secondaryip:
vm: customer_xy
vm_guest_ip: 10.10.10.10
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the NIC.
returned: success
type: str
sample: 87b1e0ce-4e01-11e4-bb66-0050569e64b8
vm:
description: Name of the VM.
returned: success
type: str
sample: web-01
ip_address:
description: Primary IP of the NIC.
returned: success
type: str
sample: 10.10.10.10
netmask:
description: Netmask of the NIC.
returned: success
type: str
sample: 255.255.255.0
mac_address:
description: MAC address of the NIC.
returned: success
type: str
sample: 02:00:33:31:00:e4
vm_guest_ip:
description: Secondary IP of the NIC.
returned: success
type: str
sample: 10.10.10.10
network:
description: Name of the network if not default.
returned: success
type: str
sample: sync network
domain:
description: Domain the VM is related to.
returned: success
type: str
sample: example domain
account:
description: Account the VM is related to.
returned: success
type: str
sample: example account
project:
description: Name of project the VM is related to.
returned: success
type: str
sample: Production
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackInstanceNicSecondaryIp(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackInstanceNicSecondaryIp, self).__init__(module)
self.vm_guest_ip = self.module.params.get('vm_guest_ip')
self.nic = None
self.returns = {
'ipaddress': 'ip_address',
'macaddress': 'mac_address',
'netmask': 'netmask',
}
def get_nic(self):
if self.nic:
return self.nic
args = {
'virtualmachineid': self.get_vm(key='id'),
'networkid': self.get_network(key='id'),
}
nics = self.query_api('listNics', **args)
if nics:
self.nic = nics['nic'][0]
return self.nic
self.fail_json(msg="NIC for VM %s in network %s not found" % (self.get_vm(key='name'), self.get_network(key='name')))
def get_secondary_ip(self):
nic = self.get_nic()
if self.vm_guest_ip:
secondary_ips = nic.get('secondaryip') or []
for secondary_ip in secondary_ips:
if secondary_ip['ipaddress'] == self.vm_guest_ip:
return secondary_ip
return None
def present_nic_ip(self):
nic = self.get_nic()
if not self.get_secondary_ip():
self.result['changed'] = True
args = {
'nicid': nic['id'],
'ipaddress': self.vm_guest_ip,
}
if not self.module.check_mode:
res = self.query_api('addIpToNic', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
nic = self.poll_job(res, 'nicsecondaryip')
# Save result for RETURNS
self.vm_guest_ip = nic['ipaddress']
return nic
def absent_nic_ip(self):
nic = self.get_nic()
secondary_ip = self.get_secondary_ip()
if secondary_ip:
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('removeIpFromNic', id=secondary_ip['id'])
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'nicsecondaryip')
return nic
def get_result(self, nic):
super(AnsibleCloudStackInstanceNicSecondaryIp, self).get_result(nic)
if nic and not self.module.params.get('network'):
self.module.params['network'] = nic.get('networkid')
self.result['network'] = self.get_network(key='name')
self.result['vm'] = self.get_vm(key='name')
self.result['vm_guest_ip'] = self.vm_guest_ip
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
vm=dict(required=True, aliases=['name']),
vm_guest_ip=dict(aliases=['secondary_ip']),
network=dict(),
vpc=dict(),
state=dict(choices=['present', 'absent'], default='present'),
domain=dict(),
account=dict(),
project=dict(),
zone=dict(),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True,
required_if=([
('state', 'absent', ['vm_guest_ip'])
])
)
acs_instance_nic_secondaryip = AnsibleCloudStackInstanceNicSecondaryIp(module)
state = module.params.get('state')
if state == 'absent':
nic = acs_instance_nic_secondaryip.absent_nic_ip()
else:
nic = acs_instance_nic_secondaryip.present_nic_ip()
result = acs_instance_nic_secondaryip.get_result(nic)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,158 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2018, Gregor Riepl <onitake@gmail.com>
# based on cs_sshkeypair (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_instance_password_reset
short_description: Allows resetting VM the default passwords on Apache CloudStack based clouds.
description:
- Resets the default user account's password on an instance.
- Requires cloud-init to be installed in the virtual machine.
- The passwordenabled flag must be set on the template associated with the VM.
author: Gregor Riepl (@onitake)
options:
vm:
description:
- Name of the virtual machine to reset the password on.
type: str
required: true
domain:
description:
- Name of the domain the virtual machine belongs to.
type: str
account:
description:
- Account the virtual machine belongs to.
type: str
project:
description:
- Name of the project the virtual machine belongs to.
type: str
zone:
description:
- Name of the zone in which the instance is deployed.
- If not set, the default zone is used.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: stop the virtual machine before resetting the password
cs_instance:
name: myvirtualmachine
state: stopped
delegate_to: localhost
- name: reset and get new default password
cs_instance_password_reset:
vm: myvirtualmachine
register: root
delegate_to: localhost
- debug:
msg: "new default password is {{ root.password }}"
- name: boot the virtual machine to activate the new password
cs_instance:
name: myvirtualmachine
state: started
delegate_to: localhost
when: root is changed
'''
RETURN = '''
---
id:
description: ID of the virtual machine.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
password:
description: The new default password.
returned: success
type: str
sample: ahQu5nuNge3keesh
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_required_together,
cs_argument_spec
)
class AnsibleCloudStackPasswordReset(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackPasswordReset, self).__init__(module)
self.returns = {
'password': 'password',
}
self.password = None
def reset_password(self):
args = {
'id': self.get_vm(key='id'),
}
res = None
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('resetPasswordForVirtualMachine', **args)
poll_async = self.module.params.get('poll_async')
if res and poll_async:
res = self.poll_job(res, 'virtualmachine')
if res and 'password' in res:
self.password = res['password']
return self.password
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
vm=dict(required=True),
domain=dict(),
account=dict(),
project=dict(),
zone=dict(),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_password = AnsibleCloudStackPasswordReset(module)
password = acs_password.reset_password()
result = acs_password.get_result({'password': password})
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,187 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_instancegroup
short_description: Manages instance groups on Apache CloudStack based clouds.
description:
- Create and remove instance groups.
author: René Moser (@resmo)
options:
name:
description:
- Name of the instance group.
type: str
required: true
domain:
description:
- Domain the instance group is related to.
type: str
account:
description:
- Account the instance group is related to.
type: str
project:
description:
- Project the instance group is related to.
type: str
state:
description:
- State of the instance group.
type: str
default: present
choices: [ present, absent ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create an instance group
cs_instancegroup:
name: loadbalancers
delegate_to: localhost
- name: Remove an instance group
cs_instancegroup:
name: loadbalancers
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the instance group.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the instance group.
returned: success
type: str
sample: webservers
created:
description: Date when the instance group was created.
returned: success
type: str
sample: 2015-05-03T15:05:51+0200
domain:
description: Domain the instance group is related to.
returned: success
type: str
sample: example domain
account:
description: Account the instance group is related to.
returned: success
type: str
sample: example account
project:
description: Project the instance group is related to.
returned: success
type: str
sample: example project
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackInstanceGroup(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackInstanceGroup, self).__init__(module)
self.instance_group = None
def get_instance_group(self):
if self.instance_group:
return self.instance_group
name = self.module.params.get('name')
args = {
'account': self.get_account('name'),
'domainid': self.get_domain('id'),
'projectid': self.get_project('id'),
'fetch_list': True,
}
instance_groups = self.query_api('listInstanceGroups', **args)
if instance_groups:
for g in instance_groups:
if name in [g['name'], g['id']]:
self.instance_group = g
break
return self.instance_group
def present_instance_group(self):
instance_group = self.get_instance_group()
if not instance_group:
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'account': self.get_account('name'),
'domainid': self.get_domain('id'),
'projectid': self.get_project('id'),
}
if not self.module.check_mode:
res = self.query_api('createInstanceGroup', **args)
instance_group = res['instancegroup']
return instance_group
def absent_instance_group(self):
instance_group = self.get_instance_group()
if instance_group:
self.result['changed'] = True
if not self.module.check_mode:
self.query_api('deleteInstanceGroup', id=instance_group['id'])
return instance_group
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
state=dict(default='present', choices=['present', 'absent']),
domain=dict(),
account=dict(),
project=dict(),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_ig = AnsibleCloudStackInstanceGroup(module)
state = module.params.get('state')
if state in ['absent']:
instance_group = acs_ig.absent_instance_group()
else:
instance_group = acs_ig.present_instance_group()
result = acs_ig.get_result(instance_group)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,284 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015, Darren Worrall <darren@iweb.co.uk>
# Copyright (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_ip_address
short_description: Manages public IP address associations on Apache CloudStack based clouds.
description:
- Acquires and associates a public IP to an account or project.
- Due to API limitations this is not an idempotent call, so be sure to only
conditionally call this when I(state=present).
- Tagging the IP address can also make the call idempotent.
author:
- Darren Worrall (@dazworrall)
- René Moser (@resmo)
options:
ip_address:
description:
- Public IP address.
- Required if I(state=absent) and I(tags) is not set.
type: str
domain:
description:
- Domain the IP address is related to.
type: str
network:
description:
- Network the IP address is related to.
- Mutually exclusive with I(vpc).
type: str
vpc:
description:
- VPC the IP address is related to.
- Mutually exclusive with I(network).
type: str
account:
description:
- Account the IP address is related to.
type: str
project:
description:
- Name of the project the IP address is related to.
type: str
zone:
description:
- Name of the zone in which the IP address is in.
- If not set, default zone is used.
type: str
state:
description:
- State of the IP address.
type: str
default: present
choices: [ present, absent ]
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- Tags can be used as an unique identifier for the IP Addresses.
- In this case, at least one of them must be unique to ensure idempotency.
type: list
aliases: [ tag ]
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Associate an IP address conditionally
cs_ip_address:
network: My Network
register: ip_address
when: instance.public_ip is undefined
delegate_to: localhost
- name: Disassociate an IP address
cs_ip_address:
ip_address: 1.2.3.4
state: absent
delegate_to: localhost
- name: Associate an IP address with tags
cs_ip_address:
network: My Network
tags:
- key: myCustomID
- value: 5510c31a-416e-11e8-9013-02000a6b00bf
register: ip_address
delegate_to: localhost
- name: Disassociate an IP address with tags
cs_ip_address:
state: absent
tags:
- key: myCustomID
- value: 5510c31a-416e-11e8-9013-02000a6b00bf
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the Public IP address.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
ip_address:
description: Public IP address.
returned: success
type: str
sample: 1.2.3.4
zone:
description: Name of zone the IP address is related to.
returned: success
type: str
sample: ch-gva-2
project:
description: Name of project the IP address is related to.
returned: success
type: str
sample: Production
account:
description: Account the IP address is related to.
returned: success
type: str
sample: example account
domain:
description: Domain the IP address is related to.
returned: success
type: str
sample: example domain
tags:
description: List of resource tags associated with the IP address.
returned: success
type: dict
sample: '[ { "key": "myCustomID", "value": "5510c31a-416e-11e8-9013-02000a6b00bf" } ]'
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackIPAddress(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackIPAddress, self).__init__(module)
self.returns = {
'ipaddress': 'ip_address',
}
def get_ip_address(self, key=None):
if self.ip_address:
return self._get_by_key(key, self.ip_address)
args = {
'ipaddress': self.module.params.get('ip_address'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'vpcid': self.get_vpc(key='id'),
}
ip_addresses = self.query_api('listPublicIpAddresses', **args)
if ip_addresses:
tags = self.module.params.get('tags')
for ip_addr in ip_addresses['publicipaddress']:
if ip_addr['ipaddress'] == args['ipaddress'] != '':
self.ip_address = ip_addresses['publicipaddress'][0]
elif tags:
if sorted([tag for tag in tags if tag in ip_addr['tags']]) == sorted(tags):
self.ip_address = ip_addr
return self._get_by_key(key, self.ip_address)
def present_ip_address(self):
ip_address = self.get_ip_address()
if not ip_address:
ip_address = self.associate_ip_address(ip_address)
if ip_address:
ip_address = self.ensure_tags(resource=ip_address, resource_type='publicipaddress')
return ip_address
def associate_ip_address(self, ip_address):
self.result['changed'] = True
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
# For the VPC case networkid is irrelevant, special case and we have to ignore it here.
'networkid': self.get_network(key='id') if not self.module.params.get('vpc') else None,
'zoneid': self.get_zone(key='id'),
'vpcid': self.get_vpc(key='id'),
}
ip_address = None
if not self.module.check_mode:
res = self.query_api('associateIpAddress', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
ip_address = self.poll_job(res, 'ipaddress')
return ip_address
def disassociate_ip_address(self):
ip_address = self.get_ip_address()
if not ip_address:
return None
if ip_address['isstaticnat']:
self.module.fail_json(msg="IP address is allocated via static nat")
self.result['changed'] = True
if not self.module.check_mode:
self.module.params['tags'] = []
ip_address = self.ensure_tags(resource=ip_address, resource_type='publicipaddress')
res = self.query_api('disassociateIpAddress', id=ip_address['id'])
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'ipaddress')
return ip_address
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
ip_address=dict(required=False),
state=dict(choices=['present', 'absent'], default='present'),
vpc=dict(),
network=dict(),
zone=dict(),
domain=dict(),
account=dict(),
project=dict(),
tags=dict(type='list', aliases=['tag']),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
required_if=[
('state', 'absent', ['ip_address', 'tags'], True),
],
mutually_exclusive=(
['vpc', 'network'],
),
supports_check_mode=True
)
acs_ip_address = AnsibleCloudStackIPAddress(module)
state = module.params.get('state')
if state in ['absent']:
ip_address = acs_ip_address.disassociate_ip_address()
else:
ip_address = acs_ip_address.present_ip_address()
result = acs_ip_address.get_result(ip_address)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,443 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_iso
short_description: Manages ISO images on Apache CloudStack based clouds.
description:
- Register and remove ISO images.
author: René Moser (@resmo)
options:
name:
description:
- Name of the ISO.
type: str
required: true
display_text:
description:
- Display text of the ISO.
- If not specified, I(name) will be used.
type: str
url:
description:
- URL where the ISO can be downloaded from. Required if I(state) is present.
type: str
os_type:
description:
- Name of the OS that best represents the OS of this ISO. If the iso is bootable this parameter needs to be passed. Required if I(state) is present.
type: str
is_ready:
description:
- This flag is used for searching existing ISOs. If set to C(yes), it will only list ISO ready for deployment e.g.
successfully downloaded and installed. Recommended to set it to C(no).
type: bool
default: no
is_public:
description:
- Register the ISO to be publicly available to all users. Only used if I(state) is present.
type: bool
is_featured:
description:
- Register the ISO to be featured. Only used if I(state) is present.
type: bool
is_dynamically_scalable:
description:
- Register the ISO having XS/VMware tools installed inorder to support dynamic scaling of VM cpu/memory. Only used if I(state) is present.
type: bool
checksum:
description:
- The MD5 checksum value of this ISO. If set, we search by checksum instead of name.
type: str
bootable:
description:
- Register the ISO to be bootable. Only used if I(state) is present.
type: bool
domain:
description:
- Domain the ISO is related to.
type: str
account:
description:
- Account the ISO is related to.
type: str
project:
description:
- Name of the project the ISO to be registered in.
type: str
zone:
description:
- Name of the zone you wish the ISO to be registered or deleted from.
- If not specified, first zone found will be used.
type: str
cross_zones:
description:
- Whether the ISO should be synced or removed across zones.
- Mutually exclusive with I(zone).
type: bool
default: no
iso_filter:
description:
- Name of the filter used to search for the ISO.
type: str
default: self
choices: [ featured, self, selfexecutable,sharedexecutable,executable, community ]
state:
description:
- State of the ISO.
type: str
default: present
choices: [ present, absent ]
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- "To delete all tags, set a empty list e.g. I(tags: [])."
type: list
aliases: [ tag ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Register an ISO if ISO name does not already exist
cs_iso:
name: Debian 7 64-bit
url: http://mirror.switch.ch/ftp/mirror/debian-cd/current/amd64/iso-cd/debian-7.7.0-amd64-netinst.iso
os_type: Debian GNU/Linux 7(64-bit)
delegate_to: localhost
- name: Register an ISO with given name if ISO md5 checksum does not already exist
cs_iso:
name: Debian 7 64-bit
url: http://mirror.switch.ch/ftp/mirror/debian-cd/current/amd64/iso-cd/debian-7.7.0-amd64-netinst.iso
os_type: Debian GNU/Linux 7(64-bit)
checksum: 0b31bccccb048d20b551f70830bb7ad0
delegate_to: localhost
- name: Remove an ISO by name
cs_iso:
name: Debian 7 64-bit
state: absent
delegate_to: localhost
- name: Remove an ISO by checksum
cs_iso:
name: Debian 7 64-bit
checksum: 0b31bccccb048d20b551f70830bb7ad0
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the ISO.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: Name of the ISO.
returned: success
type: str
sample: Debian 7 64-bit
display_text:
description: Text to be displayed of the ISO.
returned: success
type: str
sample: Debian 7.7 64-bit minimal 2015-03-19
zone:
description: Name of zone the ISO is registered in.
returned: success
type: str
sample: zuerich
status:
description: Status of the ISO.
returned: success
type: str
sample: Successfully Installed
is_ready:
description: True if the ISO is ready to be deployed from.
returned: success
type: bool
sample: true
is_public:
description: True if the ISO is public.
returned: success
type: bool
sample: true
bootable:
description: True if the ISO is bootable.
returned: success
type: bool
sample: true
is_featured:
description: True if the ISO is featured.
returned: success
type: bool
sample: true
format:
description: Format of the ISO.
returned: success
type: str
sample: ISO
os_type:
description: Typo of the OS.
returned: success
type: str
sample: CentOS 6.5 (64-bit)
checksum:
description: MD5 checksum of the ISO.
returned: success
type: str
sample: 0b31bccccb048d20b551f70830bb7ad0
created:
description: Date of registering.
returned: success
type: str
sample: 2015-03-29T14:57:06+0200
cross_zones:
description: true if the ISO is managed across all zones, false otherwise.
returned: success
type: bool
sample: false
domain:
description: Domain the ISO is related to.
returned: success
type: str
sample: example domain
account:
description: Account the ISO is related to.
returned: success
type: str
sample: example account
project:
description: Project the ISO is related to.
returned: success
type: str
sample: example project
tags:
description: List of resource tags associated with the ISO.
returned: success
type: dict
sample: '[ { "key": "foo", "value": "bar" } ]'
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackIso(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackIso, self).__init__(module)
self.returns = {
'checksum': 'checksum',
'status': 'status',
'isready': 'is_ready',
'crossZones': 'cross_zones',
'format': 'format',
'ostypename': 'os_type',
'isfeatured': 'is_featured',
'bootable': 'bootable',
'ispublic': 'is_public',
}
self.iso = None
def _get_common_args(self):
return {
'name': self.module.params.get('name'),
'displaytext': self.get_or_fallback('display_text', 'name'),
'isdynamicallyscalable': self.module.params.get('is_dynamically_scalable'),
'ostypeid': self.get_os_type('id'),
'bootable': self.module.params.get('bootable'),
}
def register_iso(self):
args = self._get_common_args()
args.update({
'domainid': self.get_domain('id'),
'account': self.get_account('name'),
'projectid': self.get_project('id'),
'checksum': self.module.params.get('checksum'),
'isfeatured': self.module.params.get('is_featured'),
'ispublic': self.module.params.get('is_public'),
})
if not self.module.params.get('cross_zones'):
args['zoneid'] = self.get_zone(key='id')
else:
args['zoneid'] = -1
if args['bootable'] and not args['ostypeid']:
self.module.fail_json(msg="OS type 'os_type' is required if 'bootable=true'.")
args['url'] = self.module.params.get('url')
if not args['url']:
self.module.fail_json(msg="URL is required.")
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('registerIso', **args)
self.iso = res['iso'][0]
return self.iso
def present_iso(self):
iso = self.get_iso()
if not iso:
iso = self.register_iso()
else:
iso = self.update_iso(iso)
if iso:
iso = self.ensure_tags(resource=iso, resource_type='ISO')
self.iso = iso
return iso
def update_iso(self, iso):
args = self._get_common_args()
args.update({
'id': iso['id'],
})
if self.has_changed(args, iso):
self.result['changed'] = True
if not self.module.params.get('cross_zones'):
args['zoneid'] = self.get_zone(key='id')
else:
# Workaround API does not return cross_zones=true
self.result['cross_zones'] = True
args['zoneid'] = -1
if not self.module.check_mode:
res = self.query_api('updateIso', **args)
self.iso = res['iso']
return self.iso
def get_iso(self):
if not self.iso:
args = {
'isready': self.module.params.get('is_ready'),
'isofilter': self.module.params.get('iso_filter'),
'domainid': self.get_domain('id'),
'account': self.get_account('name'),
'projectid': self.get_project('id'),
}
if not self.module.params.get('cross_zones'):
args['zoneid'] = self.get_zone(key='id')
# if checksum is set, we only look on that.
checksum = self.module.params.get('checksum')
if not checksum:
args['name'] = self.module.params.get('name')
isos = self.query_api('listIsos', **args)
if isos:
if not checksum:
self.iso = isos['iso'][0]
else:
for i in isos['iso']:
if i['checksum'] == checksum:
self.iso = i
break
return self.iso
def absent_iso(self):
iso = self.get_iso()
if iso:
self.result['changed'] = True
args = {
'id': iso['id'],
'projectid': self.get_project('id'),
}
if not self.module.params.get('cross_zones'):
args['zoneid'] = self.get_zone(key='id')
if not self.module.check_mode:
res = self.query_api('deleteIso', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'iso')
return iso
def get_result(self, iso):
super(AnsibleCloudStackIso, self).get_result(iso)
# Workaround API does not return cross_zones=true
if self.module.params.get('cross_zones'):
self.result['cross_zones'] = True
if 'zone' in self.result:
del self.result['zone']
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
display_text=dict(),
url=dict(),
os_type=dict(),
zone=dict(),
cross_zones=dict(type='bool', default=False),
iso_filter=dict(default='self', choices=['featured', 'self', 'selfexecutable', 'sharedexecutable', 'executable', 'community']),
domain=dict(),
account=dict(),
project=dict(),
checksum=dict(),
is_ready=dict(type='bool', default=False),
bootable=dict(type='bool'),
is_featured=dict(type='bool'),
is_public=dict(type='bool'),
is_dynamically_scalable=dict(type='bool'),
state=dict(choices=['present', 'absent'], default='present'),
poll_async=dict(type='bool', default=True),
tags=dict(type='list', aliases=['tag']),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
mutually_exclusive=(
['zone', 'cross_zones'],
),
supports_check_mode=True
)
acs_iso = AnsibleCloudStackIso(module)
state = module.params.get('state')
if state in ['absent']:
iso = acs_iso.absent_iso()
else:
iso = acs_iso.present_iso()
result = acs_iso.get_result(iso)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,378 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, Darren Worrall <darren@iweb.co.uk>
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_loadbalancer_rule
short_description: Manages load balancer rules on Apache CloudStack based clouds.
description:
- Add, update and remove load balancer rules.
author:
- Darren Worrall (@dazworrall)
- René Moser (@resmo)
options:
name:
description:
- The name of the load balancer rule.
type: str
required: true
description:
description:
- The description of the load balancer rule.
type: str
algorithm:
description:
- Load balancer algorithm
- Required when using I(state=present).
type: str
choices: [ source, roundrobin, leastconn ]
default: source
private_port:
description:
- The private port of the private ip address/virtual machine where the network traffic will be load balanced to.
- Required when using I(state=present).
- Can not be changed once the rule exists due API limitation.
type: int
public_port:
description:
- The public port from where the network traffic will be load balanced from.
- Required when using I(state=present).
- Can not be changed once the rule exists due API limitation.
type: int
required: true
ip_address:
description:
- Public IP address from where the network traffic will be load balanced from.
type: str
required: true
aliases: [ public_ip ]
open_firewall:
description:
- Whether the firewall rule for public port should be created, while creating the new rule.
- Use M(cs_firewall) for managing firewall rules.
type: bool
default: no
cidr:
description:
- CIDR (full notation) to be used for firewall rule if required.
type: str
protocol:
description:
- The protocol to be used on the load balancer
type: str
project:
description:
- Name of the project the load balancer IP address is related to.
type: str
state:
description:
- State of the rule.
type: str
default: present
choices: [ present, absent ]
domain:
description:
- Domain the rule is related to.
type: str
account:
description:
- Account the rule is related to.
type: str
zone:
description:
- Name of the zone in which the rule should be created.
- If not set, default zone is used.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- "To delete all tags, set a empty list e.g. I(tags: [])."
type: list
aliases: [ tag ]
network:
description:
- Name of the network.
type: str
vpc:
description:
- Name of the VPC.
type: str
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a load balancer rule
cs_loadbalancer_rule:
name: balance_http
public_ip: 1.2.3.4
algorithm: leastconn
public_port: 80
private_port: 8080
delegate_to: localhost
- name: Update algorithm of an existing load balancer rule
cs_loadbalancer_rule:
name: balance_http
public_ip: 1.2.3.4
algorithm: roundrobin
public_port: 80
private_port: 8080
delegate_to: localhost
- name: Delete a load balancer rule
cs_loadbalancer_rule:
name: balance_http
public_ip: 1.2.3.4
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the rule.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
zone:
description: Name of zone the rule is related to.
returned: success
type: str
sample: ch-gva-2
project:
description: Name of project the rule is related to.
returned: success
type: str
sample: Production
account:
description: Account the rule is related to.
returned: success
type: str
sample: example account
domain:
description: Domain the rule is related to.
returned: success
type: str
sample: example domain
algorithm:
description: Load balancer algorithm used.
returned: success
type: str
sample: source
cidr:
description: CIDR to forward traffic from.
returned: success
type: str
sample: 0.0.0.0/0
name:
description: Name of the rule.
returned: success
type: str
sample: http-lb
description:
description: Description of the rule.
returned: success
type: str
sample: http load balancer rule
protocol:
description: Protocol of the rule.
returned: success
type: str
sample: tcp
public_port:
description: Public port.
returned: success
type: int
sample: 80
private_port:
description: Private IP address.
returned: success
type: int
sample: 80
public_ip:
description: Public IP address.
returned: success
type: str
sample: 1.2.3.4
tags:
description: List of resource tags associated with the rule.
returned: success
type: list
sample: '[ { "key": "foo", "value": "bar" } ]'
state:
description: State of the rule.
returned: success
type: str
sample: Add
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackLBRule(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackLBRule, self).__init__(module)
self.returns = {
'publicip': 'public_ip',
'algorithm': 'algorithm',
'cidrlist': 'cidr',
'protocol': 'protocol',
}
# these values will be casted to int
self.returns_to_int = {
'publicport': 'public_port',
'privateport': 'private_port',
}
def get_rule(self, **kwargs):
rules = self.query_api('listLoadBalancerRules', **kwargs)
if rules:
return rules['loadbalancerrule'][0]
def _get_common_args(self):
return {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'zoneid': self.get_zone(key='id') if self.module.params.get('zone') else None,
'publicipid': self.get_ip_address(key='id'),
'name': self.module.params.get('name'),
}
def present_lb_rule(self):
required_params = [
'algorithm',
'private_port',
'public_port',
]
self.module.fail_on_missing_params(required_params=required_params)
args = self._get_common_args()
rule = self.get_rule(**args)
if rule:
rule = self._update_lb_rule(rule)
else:
rule = self._create_lb_rule(rule)
if rule:
rule = self.ensure_tags(resource=rule, resource_type='LoadBalancer')
return rule
def _create_lb_rule(self, rule):
self.result['changed'] = True
if not self.module.check_mode:
args = self._get_common_args()
args.update({
'algorithm': self.module.params.get('algorithm'),
'privateport': self.module.params.get('private_port'),
'publicport': self.module.params.get('public_port'),
'cidrlist': self.module.params.get('cidr'),
'description': self.module.params.get('description'),
'protocol': self.module.params.get('protocol'),
'networkid': self.get_network(key='id'),
})
res = self.query_api('createLoadBalancerRule', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
rule = self.poll_job(res, 'loadbalancer')
return rule
def _update_lb_rule(self, rule):
args = {
'id': rule['id'],
'algorithm': self.module.params.get('algorithm'),
'description': self.module.params.get('description'),
}
if self.has_changed(args, rule):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateLoadBalancerRule', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
rule = self.poll_job(res, 'loadbalancer')
return rule
def absent_lb_rule(self):
args = self._get_common_args()
rule = self.get_rule(**args)
if rule:
self.result['changed'] = True
if rule and not self.module.check_mode:
res = self.query_api('deleteLoadBalancerRule', id=rule['id'])
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'loadbalancer')
return rule
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
description=dict(),
algorithm=dict(choices=['source', 'roundrobin', 'leastconn'], default='source'),
private_port=dict(type='int'),
public_port=dict(type='int'),
protocol=dict(),
state=dict(choices=['present', 'absent'], default='present'),
ip_address=dict(required=True, aliases=['public_ip']),
cidr=dict(),
project=dict(),
open_firewall=dict(type='bool', default=False),
tags=dict(type='list', aliases=['tag']),
zone=dict(),
domain=dict(),
account=dict(),
vpc=dict(),
network=dict(),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_lb_rule = AnsibleCloudStackLBRule(module)
state = module.params.get('state')
if state in ['absent']:
rule = acs_lb_rule.absent_lb_rule()
else:
rule = acs_lb_rule.present_lb_rule()
result = acs_lb_rule.get_result(rule)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,350 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015, Darren Worrall <darren@iweb.co.uk>
# Copyright (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_loadbalancer_rule_member
short_description: Manages load balancer rule members on Apache CloudStack based clouds.
description:
- Add and remove load balancer rule members.
author:
- Darren Worrall (@dazworrall)
- René Moser (@resmo)
options:
name:
description:
- The name of the load balancer rule.
type: str
required: true
ip_address:
description:
- Public IP address from where the network traffic will be load balanced from.
- Only needed to find the rule if I(name) is not unique.
type: str
aliases: [ public_ip ]
vms:
description:
- List of VMs to assign to or remove from the rule.
type: list
required: true
aliases: [ vm ]
state:
description:
- Should the VMs be present or absent from the rule.
type: str
default: present
choices: [ present, absent ]
project:
description:
- Name of the project the firewall rule is related to.
type: str
domain:
description:
- Domain the rule is related to.
type: str
account:
description:
- Account the rule is related to.
type: str
zone:
description:
- Name of the zone in which the rule should be located.
- If not set, default zone is used.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Add VMs to an existing load balancer
cs_loadbalancer_rule_member:
name: balance_http
vms:
- web01
- web02
delegate_to: localhost
- name: Remove a VM from an existing load balancer
cs_loadbalancer_rule_member:
name: balance_http
vms:
- web01
- web02
state: absent
delegate_to: localhost
# Rolling upgrade of hosts
- hosts: webservers
serial: 1
pre_tasks:
- name: Remove from load balancer
cs_loadbalancer_rule_member:
name: balance_http
vm: "{{ ansible_hostname }}"
state: absent
delegate_to: localhost
tasks:
# Perform update
post_tasks:
- name: Add to load balancer
cs_loadbalancer_rule_member:
name: balance_http
vm: "{{ ansible_hostname }}"
state: present
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the rule.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
zone:
description: Name of zone the rule is related to.
returned: success
type: str
sample: ch-gva-2
project:
description: Name of project the rule is related to.
returned: success
type: str
sample: Production
account:
description: Account the rule is related to.
returned: success
type: str
sample: example account
domain:
description: Domain the rule is related to.
returned: success
type: str
sample: example domain
algorithm:
description: Load balancer algorithm used.
returned: success
type: str
sample: source
cidr:
description: CIDR to forward traffic from.
returned: success
type: str
sample: 0.0.0.0/0
name:
description: Name of the rule.
returned: success
type: str
sample: http-lb
description:
description: Description of the rule.
returned: success
type: str
sample: http load balancer rule
protocol:
description: Protocol of the rule.
returned: success
type: str
sample: tcp
public_port:
description: Public port.
returned: success
type: int
sample: 80
private_port:
description: Private IP address.
returned: success
type: int
sample: 80
public_ip:
description: Public IP address.
returned: success
type: str
sample: 1.2.3.4
vms:
description: Rule members.
returned: success
type: list
sample: '[ "web01", "web02" ]'
tags:
description: List of resource tags associated with the rule.
returned: success
type: list
sample: '[ { "key": "foo", "value": "bar" } ]'
state:
description: State of the rule.
returned: success
type: str
sample: Add
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackLBRuleMember(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackLBRuleMember, self).__init__(module)
self.returns = {
'publicip': 'public_ip',
'algorithm': 'algorithm',
'cidrlist': 'cidr',
'protocol': 'protocol',
}
# these values will be casted to int
self.returns_to_int = {
'publicport': 'public_port',
'privateport': 'private_port',
}
def get_rule(self):
args = self._get_common_args()
args.update({
'name': self.module.params.get('name'),
'zoneid': self.get_zone(key='id') if self.module.params.get('zone') else None,
})
if self.module.params.get('ip_address'):
args['publicipid'] = self.get_ip_address(key='id')
rules = self.query_api('listLoadBalancerRules', **args)
if rules:
if len(rules['loadbalancerrule']) > 1:
self.module.fail_json(msg="More than one rule having name %s. Please pass 'ip_address' as well." % args['name'])
return rules['loadbalancerrule'][0]
return None
def _get_common_args(self):
return {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
}
def _get_members_of_rule(self, rule):
res = self.query_api('listLoadBalancerRuleInstances', id=rule['id'])
return res.get('loadbalancerruleinstance', [])
def _ensure_members(self, operation):
if operation not in ['add', 'remove']:
self.module.fail_json(msg="Bad operation: %s" % operation)
rule = self.get_rule()
if not rule:
self.module.fail_json(msg="Unknown rule: %s" % self.module.params.get('name'))
existing = {}
for vm in self._get_members_of_rule(rule=rule):
existing[vm['name']] = vm['id']
wanted_names = self.module.params.get('vms')
if operation == 'add':
cs_func = 'assignToLoadBalancerRule'
to_change = set(wanted_names) - set(existing.keys())
else:
cs_func = 'removeFromLoadBalancerRule'
to_change = set(wanted_names) & set(existing.keys())
if not to_change:
return rule
args = self._get_common_args()
args['fetch_list'] = True
vms = self.query_api('listVirtualMachines', **args)
to_change_ids = []
for name in to_change:
for vm in vms:
if vm['name'] == name:
to_change_ids.append(vm['id'])
break
else:
self.module.fail_json(msg="Unknown VM: %s" % name)
if to_change_ids:
self.result['changed'] = True
if to_change_ids and not self.module.check_mode:
res = self.query_api(
cs_func,
id=rule['id'],
virtualmachineids=to_change_ids,
)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res)
rule = self.get_rule()
return rule
def add_members(self):
return self._ensure_members('add')
def remove_members(self):
return self._ensure_members('remove')
def get_result(self, rule):
super(AnsibleCloudStackLBRuleMember, self).get_result(rule)
if rule:
self.result['vms'] = []
for vm in self._get_members_of_rule(rule=rule):
self.result['vms'].append(vm['name'])
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
ip_address=dict(aliases=['public_ip']),
vms=dict(required=True, aliases=['vm'], type='list'),
state=dict(choices=['present', 'absent'], default='present'),
zone=dict(),
domain=dict(),
project=dict(),
account=dict(),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_lb_rule_member = AnsibleCloudStackLBRuleMember(module)
state = module.params.get('state')
if state in ['absent']:
rule = acs_lb_rule_member.remove_members()
else:
rule = acs_lb_rule_member.add_members()
result = acs_lb_rule_member.get_result(rule)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,636 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_network
short_description: Manages networks on Apache CloudStack based clouds.
description:
- Create, update, restart and delete networks.
author: René Moser (@resmo)
options:
name:
description:
- Name (case sensitive) of the network.
type: str
required: true
display_text:
description:
- Display text of the network.
- If not specified, I(name) will be used as I(display_text).
type: str
network_offering:
description:
- Name of the offering for the network.
- Required if I(state=present).
type: str
start_ip:
description:
- The beginning IPv4 address of the network belongs to.
- Only considered on create.
type: str
end_ip:
description:
- The ending IPv4 address of the network belongs to.
- If not specified, value of I(start_ip) is used.
- Only considered on create.
type: str
gateway:
description:
- The gateway of the network.
- Required for shared networks and isolated networks when it belongs to a VPC.
- Only considered on create.
type: str
netmask:
description:
- The netmask of the network.
- Required for shared networks and isolated networks when it belongs to a VPC.
- Only considered on create.
type: str
start_ipv6:
description:
- The beginning IPv6 address of the network belongs to.
- Only considered on create.
type: str
end_ipv6:
description:
- The ending IPv6 address of the network belongs to.
- If not specified, value of I(start_ipv6) is used.
- Only considered on create.
type: str
cidr_ipv6:
description:
- CIDR of IPv6 network, must be at least /64.
- Only considered on create.
type: str
gateway_ipv6:
description:
- The gateway of the IPv6 network.
- Required for shared networks.
- Only considered on create.
type: str
vlan:
description:
- The ID or VID of the network.
type: str
vpc:
description:
- Name of the VPC of the network.
type: str
isolated_pvlan:
description:
- The isolated private VLAN for this network.
type: str
clean_up:
description:
- Cleanup old network elements.
- Only considered on I(state=restarted).
default: no
type: bool
acl_type:
description:
- Access control type for the network.
- If not specified, Cloudstack will default to C(account) for isolated networks
- and C(domain) for shared networks.
- Only considered on create.
type: str
choices: [ account, domain ]
acl:
description:
- The name of the access control list for the VPC network tier.
type: str
subdomain_access:
description:
- Defines whether to allow subdomains to use networks dedicated to their parent domain(s).
- Should be used with I(acl_type=domain).
- Only considered on create.
type: bool
network_domain:
description:
- The network domain.
type: str
state:
description:
- State of the network.
type: str
default: present
choices: [ present, absent, restarted ]
zone:
description:
- Name of the zone in which the network should be deployed.
- If not set, default zone is used.
type: str
project:
description:
- Name of the project the network to be deployed in.
type: str
domain:
description:
- Domain the network is related to.
type: str
account:
description:
- Account the network is related to.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- "To delete all tags, set a empty list e.g. I(tags: [])."
type: list
aliases: [ tag ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a network
cs_network:
name: my network
zone: gva-01
network_offering: DefaultIsolatedNetworkOfferingWithSourceNatService
network_domain: example.com
delegate_to: localhost
- name: Create a VPC tier
cs_network:
name: my VPC tier 1
zone: gva-01
vpc: my VPC
network_offering: DefaultIsolatedNetworkOfferingForVpcNetworks
gateway: 10.43.0.1
netmask: 255.255.255.0
acl: my web acl
delegate_to: localhost
- name: Update a network
cs_network:
name: my network
display_text: network of domain example.local
network_domain: example.local
delegate_to: localhost
- name: Restart a network with clean up
cs_network:
name: my network
clean_up: yes
state: restarted
delegate_to: localhost
- name: Remove a network
cs_network:
name: my network
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the network.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the network.
returned: success
type: str
sample: web project
display_text:
description: Display text of the network.
returned: success
type: str
sample: web project
dns1:
description: IP address of the 1st nameserver.
returned: success
type: str
sample: 1.2.3.4
dns2:
description: IP address of the 2nd nameserver.
returned: success
type: str
sample: 1.2.3.4
cidr:
description: IPv4 network CIDR.
returned: success
type: str
sample: 10.101.64.0/24
gateway:
description: IPv4 gateway.
returned: success
type: str
sample: 10.101.64.1
netmask:
description: IPv4 netmask.
returned: success
type: str
sample: 255.255.255.0
cidr_ipv6:
description: IPv6 network CIDR.
returned: if available
type: str
sample: 2001:db8::/64
gateway_ipv6:
description: IPv6 gateway.
returned: if available
type: str
sample: 2001:db8::1
zone:
description: Name of zone.
returned: success
type: str
sample: ch-gva-2
domain:
description: Domain the network is related to.
returned: success
type: str
sample: ROOT
account:
description: Account the network is related to.
returned: success
type: str
sample: example account
project:
description: Name of project.
returned: success
type: str
sample: Production
tags:
description: List of resource tags associated with the network.
returned: success
type: list
sample: '[ { "key": "foo", "value": "bar" } ]'
acl_type:
description: Access type of the network (Domain, Account).
returned: success
type: str
sample: Account
acl:
description: Name of the access control list for the VPC network tier.
returned: success
type: str
sample: My ACL
acl_id:
description: ID of the access control list for the VPC network tier.
returned: success
type: str
sample: dfafcd55-0510-4b8c-b6c5-b8cedb4cfd88
broadcast_domain_type:
description: Broadcast domain type of the network.
returned: success
type: str
sample: Vlan
type:
description: Type of the network.
returned: success
type: str
sample: Isolated
traffic_type:
description: Traffic type of the network.
returned: success
type: str
sample: Guest
state:
description: State of the network (Allocated, Implemented, Setup).
returned: success
type: str
sample: Allocated
is_persistent:
description: Whether the network is persistent or not.
returned: success
type: bool
sample: false
network_domain:
description: The network domain
returned: success
type: str
sample: example.local
network_offering:
description: The network offering name.
returned: success
type: str
sample: DefaultIsolatedNetworkOfferingWithSourceNatService
network_offering_display_text:
description: The network offering display text.
returned: success
type: str
sample: Offering for Isolated Vpc networks with Source Nat service enabled
network_offering_conserve_mode:
description: Whether the network offering has IP conserve mode enabled or not.
returned: success
type: bool
sample: false
network_offering_availability:
description: The availability of the network offering the network is created from
returned: success
type: str
sample: Optional
is_system:
description: Whether the network is system related or not.
returned: success
type: bool
sample: false
vpc:
description: Name of the VPC.
returned: if available
type: str
sample: My VPC
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackNetwork(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackNetwork, self).__init__(module)
self.returns = {
'networkdomain': 'network_domain',
'networkofferingname': 'network_offering',
'networkofferingdisplaytext': 'network_offering_display_text',
'networkofferingconservemode': 'network_offering_conserve_mode',
'networkofferingavailability': 'network_offering_availability',
'aclid': 'acl_id',
'issystem': 'is_system',
'ispersistent': 'is_persistent',
'acltype': 'acl_type',
'type': 'type',
'traffictype': 'traffic_type',
'ip6gateway': 'gateway_ipv6',
'ip6cidr': 'cidr_ipv6',
'gateway': 'gateway',
'cidr': 'cidr',
'netmask': 'netmask',
'broadcastdomaintype': 'broadcast_domain_type',
'dns1': 'dns1',
'dns2': 'dns2',
}
self.network = None
def get_network_acl(self, key=None, acl_id=None):
if acl_id is not None:
args = {
'id': acl_id,
'vpcid': self.get_vpc(key='id'),
}
else:
acl_name = self.module.params.get('acl')
if not acl_name:
return
args = {
'name': acl_name,
'vpcid': self.get_vpc(key='id'),
}
network_acls = self.query_api('listNetworkACLLists', **args)
if network_acls:
acl = network_acls['networkacllist'][0]
return self._get_by_key(key, acl)
def get_network_offering(self, key=None):
network_offering = self.module.params.get('network_offering')
if not network_offering:
self.module.fail_json(msg="missing required arguments: network_offering")
args = {
'zoneid': self.get_zone(key='id'),
'fetch_list': True,
}
network_offerings = self.query_api('listNetworkOfferings', **args)
if network_offerings:
for no in network_offerings:
if network_offering in [no['name'], no['displaytext'], no['id']]:
return self._get_by_key(key, no)
self.module.fail_json(msg="Network offering '%s' not found" % network_offering)
def _get_args(self):
args = {
'name': self.module.params.get('name'),
'displaytext': self.get_or_fallback('display_text', 'name'),
'networkdomain': self.module.params.get('network_domain'),
'networkofferingid': self.get_network_offering(key='id')
}
return args
def get_network(self, refresh=False):
if not self.network or refresh:
network = self.module.params.get('name')
args = {
'zoneid': self.get_zone(key='id'),
'projectid': self.get_project(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'vpcid': self.get_vpc(key='id'),
'fetch_list': True,
}
networks = self.query_api('listNetworks', **args)
if networks:
for n in networks:
if network in [n['name'], n['displaytext'], n['id']]:
self.network = n
self.network['acl'] = self.get_network_acl(key='name', acl_id=n.get('aclid'))
break
return self.network
def present_network(self):
if self.module.params.get('acl') is not None and self.module.params.get('vpc') is None:
self.module.fail_json(msg="Missing required params: vpc")
network = self.get_network()
if not network:
network = self.create_network(network)
else:
network = self.update_network(network)
if network:
network = self.ensure_tags(resource=network, resource_type='Network')
return network
def update_network(self, network):
args = self._get_args()
args['id'] = network['id']
if self.has_changed(args, network):
self.result['changed'] = True
if not self.module.check_mode:
network = self.query_api('updateNetwork', **args)
poll_async = self.module.params.get('poll_async')
if network and poll_async:
network = self.poll_job(network, 'network')
# Skip ACL check if the network is not a VPC tier
if network.get('aclid') != self.get_network_acl(key='id'):
self.result['changed'] = True
if not self.module.check_mode:
args = {
'aclid': self.get_network_acl(key='id'),
'networkid': network['id'],
}
network = self.query_api('replaceNetworkACLList', **args)
if self.module.params.get('poll_async'):
self.poll_job(network, 'networkacllist')
network = self.get_network(refresh=True)
return network
def create_network(self, network):
self.result['changed'] = True
args = self._get_args()
args.update({
'acltype': self.module.params.get('acl_type'),
'aclid': self.get_network_acl(key='id'),
'zoneid': self.get_zone(key='id'),
'projectid': self.get_project(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'startip': self.module.params.get('start_ip'),
'endip': self.get_or_fallback('end_ip', 'start_ip'),
'netmask': self.module.params.get('netmask'),
'gateway': self.module.params.get('gateway'),
'startipv6': self.module.params.get('start_ipv6'),
'endipv6': self.get_or_fallback('end_ipv6', 'start_ipv6'),
'ip6cidr': self.module.params.get('cidr_ipv6'),
'ip6gateway': self.module.params.get('gateway_ipv6'),
'vlan': self.module.params.get('vlan'),
'isolatedpvlan': self.module.params.get('isolated_pvlan'),
'subdomainaccess': self.module.params.get('subdomain_access'),
'vpcid': self.get_vpc(key='id')
})
if not self.module.check_mode:
res = self.query_api('createNetwork', **args)
network = res['network']
return network
def restart_network(self):
network = self.get_network()
if not network:
self.module.fail_json(msg="No network named '%s' found." % self.module.params('name'))
# Restarting only available for these states
if network['state'].lower() in ['implemented', 'setup']:
self.result['changed'] = True
args = {
'id': network['id'],
'cleanup': self.module.params.get('clean_up')
}
if not self.module.check_mode:
network = self.query_api('restartNetwork', **args)
poll_async = self.module.params.get('poll_async')
if network and poll_async:
network = self.poll_job(network, 'network')
return network
def absent_network(self):
network = self.get_network()
if network:
self.result['changed'] = True
args = {
'id': network['id']
}
if not self.module.check_mode:
res = self.query_api('deleteNetwork', **args)
poll_async = self.module.params.get('poll_async')
if res and poll_async:
self.poll_job(res, 'network')
return network
def get_result(self, network):
super(AnsibleCloudStackNetwork, self).get_result(network)
if network:
self.result['acl'] = self.get_network_acl(key='name', acl_id=network.get('aclid'))
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
display_text=dict(),
network_offering=dict(),
zone=dict(),
start_ip=dict(),
end_ip=dict(),
gateway=dict(),
netmask=dict(),
start_ipv6=dict(),
end_ipv6=dict(),
cidr_ipv6=dict(),
gateway_ipv6=dict(),
vlan=dict(),
vpc=dict(),
isolated_pvlan=dict(),
clean_up=dict(type='bool', default=False),
network_domain=dict(),
subdomain_access=dict(type='bool'),
state=dict(choices=['present', 'absent', 'restarted'], default='present'),
acl=dict(),
acl_type=dict(choices=['account', 'domain']),
project=dict(),
domain=dict(),
account=dict(),
poll_async=dict(type='bool', default=True),
tags=dict(type='list', aliases=['tag']),
))
required_together = cs_required_together()
required_together.extend([
['netmask', 'gateway'],
])
module = AnsibleModule(
argument_spec=argument_spec,
required_together=required_together,
supports_check_mode=True
)
acs_network = AnsibleCloudStackNetwork(module)
state = module.params.get('state')
if state == 'absent':
network = acs_network.absent_network()
elif state == 'restarted':
network = acs_network.restart_network()
else:
network = acs_network.present_network()
result = acs_network.get_result(network)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,202 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_network_acl
short_description: Manages network access control lists (ACL) on Apache CloudStack based clouds.
description:
- Create and remove network ACLs.
author: René Moser (@resmo)
options:
name:
description:
- Name of the network ACL.
type: str
required: true
description:
description:
- Description of the network ACL.
- If not set, identical to I(name).
type: str
vpc:
description:
- VPC the network ACL is related to.
type: str
required: true
state:
description:
- State of the network ACL.
type: str
default: present
choices: [ present, absent ]
domain:
description:
- Domain the network ACL rule is related to.
type: str
account:
description:
- Account the network ACL rule is related to.
type: str
project:
description:
- Name of the project the network ACL is related to.
type: str
zone:
description:
- Name of the zone the VPC is related to.
- If not set, default zone is used.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: create a network ACL
cs_network_acl:
name: Webserver ACL
description: a more detailed description of the ACL
vpc: customers
delegate_to: localhost
- name: remove a network ACL
cs_network_acl:
name: Webserver ACL
vpc: customers
state: absent
delegate_to: localhost
'''
RETURN = '''
---
name:
description: Name of the network ACL.
returned: success
type: str
sample: customer acl
description:
description: Description of the network ACL.
returned: success
type: str
sample: Example description of a network ACL
vpc:
description: VPC of the network ACL.
returned: success
type: str
sample: customer vpc
zone:
description: Zone the VPC is related to.
returned: success
type: str
sample: ch-gva-2
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackNetworkAcl(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackNetworkAcl, self).__init__(module)
def get_network_acl(self):
args = {
'name': self.module.params.get('name'),
'vpcid': self.get_vpc(key='id'),
}
network_acls = self.query_api('listNetworkACLLists', **args)
if network_acls:
return network_acls['networkacllist'][0]
return None
def present_network_acl(self):
network_acl = self.get_network_acl()
if not network_acl:
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'description': self.get_or_fallback('description', 'name'),
'vpcid': self.get_vpc(key='id')
}
if not self.module.check_mode:
res = self.query_api('createNetworkACLList', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
network_acl = self.poll_job(res, 'networkacllist')
return network_acl
def absent_network_acl(self):
network_acl = self.get_network_acl()
if network_acl:
self.result['changed'] = True
args = {
'id': network_acl['id'],
}
if not self.module.check_mode:
res = self.query_api('deleteNetworkACLList', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'networkacllist')
return network_acl
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
description=dict(),
vpc=dict(required=True),
state=dict(choices=['present', 'absent'], default='present'),
zone=dict(),
domain=dict(),
account=dict(),
project=dict(),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_network_acl = AnsibleCloudStackNetworkAcl(module)
state = module.params.get('state')
if state == 'absent':
network_acl = acs_network_acl.absent_network_acl()
else:
network_acl = acs_network_acl.present_network_acl()
result = acs_network_acl.get_result(network_acl)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,461 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_network_acl_rule
short_description: Manages network access control list (ACL) rules on Apache CloudStack based clouds.
description:
- Add, update and remove network ACL rules.
author: René Moser (@resmo)
options:
network_acl:
description:
- Name of the network ACL.
type: str
required: true
aliases: [ acl ]
cidrs:
description:
- CIDRs of the rule.
type: list
default: [ 0.0.0.0/0 ]
aliases: [ cidr ]
rule_position:
description:
- The position of the network ACL rule.
type: int
required: true
aliases: [ number ]
protocol:
description:
- Protocol of the rule
choices: [ tcp, udp, icmp, all, by_number ]
type: str
default: tcp
protocol_number:
description:
- Protocol number from 1 to 256 required if I(protocol=by_number).
type: int
start_port:
description:
- Start port for this rule.
- Considered if I(protocol=tcp) or I(protocol=udp).
type: int
aliases: [ port ]
end_port:
description:
- End port for this rule.
- Considered if I(protocol=tcp) or I(protocol=udp).
- If not specified, equal I(start_port).
type: int
icmp_type:
description:
- Type of the icmp message being sent.
- Considered if I(protocol=icmp).
type: int
icmp_code:
description:
- Error code for this icmp message.
- Considered if I(protocol=icmp).
type: int
vpc:
description:
- VPC the network ACL is related to.
type: str
required: true
traffic_type:
description:
- Traffic type of the rule.
type: str
choices: [ ingress, egress ]
default: ingress
aliases: [ type ]
action_policy:
description:
- Action policy of the rule.
type: str
choices: [ allow, deny ]
default: allow
aliases: [ action ]
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- "If you want to delete all tags, set a empty list e.g. I(tags: [])."
type: list
aliases: [ tag ]
domain:
description:
- Domain the VPC is related to.
type: str
account:
description:
- Account the VPC is related to.
type: str
project:
description:
- Name of the project the VPC is related to.
type: str
zone:
description:
- Name of the zone the VPC related to.
- If not set, default zone is used.
type: str
state:
description:
- State of the network ACL rule.
type: str
default: present
choices: [ present, absent ]
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: create a network ACL rule, allow port 80 ingress
cs_network_acl_rule:
network_acl: web
rule_position: 1
vpc: my vpc
traffic_type: ingress
action_policy: allow
port: 80
cidr: 0.0.0.0/0
delegate_to: localhost
- name: create a network ACL rule, deny port range 8000-9000 ingress for 10.20.0.0/16 and 10.22.0.0/16
cs_network_acl_rule:
network_acl: web
rule_position: 1
vpc: my vpc
traffic_type: ingress
action_policy: deny
start_port: 8000
end_port: 9000
cidrs:
- 10.20.0.0/16
- 10.22.0.0/16
delegate_to: localhost
- name: remove a network ACL rule
cs_network_acl_rule:
network_acl: web
rule_position: 1
vpc: my vpc
state: absent
delegate_to: localhost
'''
RETURN = '''
---
network_acl:
description: Name of the network ACL.
returned: success
type: str
sample: customer acl
cidr:
description: CIDR of the network ACL rule.
returned: success
type: str
sample: 0.0.0.0/0
cidrs:
description: CIDRs of the network ACL rule.
returned: success
type: list
sample: [ 0.0.0.0/0 ]
rule_position:
description: Position of the network ACL rule.
returned: success
type: int
sample: 1
action_policy:
description: Action policy of the network ACL rule.
returned: success
type: str
sample: deny
traffic_type:
description: Traffic type of the network ACL rule.
returned: success
type: str
sample: ingress
protocol:
description: Protocol of the network ACL rule.
returned: success
type: str
sample: tcp
protocol_number:
description: Protocol number in case protocol is by number.
returned: success
type: int
sample: 8
start_port:
description: Start port of the network ACL rule.
returned: success
type: int
sample: 80
end_port:
description: End port of the network ACL rule.
returned: success
type: int
sample: 80
icmp_code:
description: ICMP code of the network ACL rule.
returned: success
type: int
sample: 8
icmp_type:
description: ICMP type of the network ACL rule.
returned: success
type: int
sample: 0
state:
description: State of the network ACL rule.
returned: success
type: str
sample: Active
vpc:
description: VPC of the network ACL.
returned: success
type: str
sample: customer vpc
tags:
description: List of resource tags associated with the network ACL rule.
returned: success
type: list
sample: '[ { "key": "foo", "value": "bar" } ]'
domain:
description: Domain the network ACL rule is related to.
returned: success
type: str
sample: example domain
account:
description: Account the network ACL rule is related to.
returned: success
type: str
sample: example account
project:
description: Name of project the network ACL rule is related to.
returned: success
type: str
sample: Production
zone:
description: Zone the VPC is related to.
returned: success
type: str
sample: ch-gva-2
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackNetworkAclRule(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackNetworkAclRule, self).__init__(module)
self.returns = {
'cidrlist': 'cidr',
'action': 'action_policy',
'protocol': 'protocol',
'icmpcode': 'icmp_code',
'icmptype': 'icmp_type',
'number': 'rule_position',
'traffictype': 'traffic_type',
}
# these values will be casted to int
self.returns_to_int = {
'startport': 'start_port',
'endport': 'end_port',
}
def get_network_acl_rule(self):
args = {
'aclid': self.get_network_acl(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
}
network_acl_rules = self.query_api('listNetworkACLs', **args)
for acl_rule in network_acl_rules.get('networkacl', []):
if acl_rule['number'] == self.module.params.get('rule_position'):
return acl_rule
return None
def present_network_acl_rule(self):
network_acl_rule = self.get_network_acl_rule()
protocol = self.module.params.get('protocol')
start_port = self.module.params.get('start_port')
end_port = self.get_or_fallback('end_port', 'start_port')
icmp_type = self.module.params.get('icmp_type')
icmp_code = self.module.params.get('icmp_code')
if protocol in ['tcp', 'udp'] and (start_port is None or end_port is None):
self.module.fail_json(msg="protocol is %s but the following are missing: start_port, end_port" % protocol)
elif protocol == 'icmp' and (icmp_type is None or icmp_code is None):
self.module.fail_json(msg="protocol is icmp but the following are missing: icmp_type, icmp_code")
elif protocol == 'by_number' and self.module.params.get('protocol_number') is None:
self.module.fail_json(msg="protocol is by_number but the following are missing: protocol_number")
if not network_acl_rule:
network_acl_rule = self._create_network_acl_rule(network_acl_rule)
else:
network_acl_rule = self._update_network_acl_rule(network_acl_rule)
if network_acl_rule:
network_acl_rule = self.ensure_tags(resource=network_acl_rule, resource_type='NetworkACL')
return network_acl_rule
def absent_network_acl_rule(self):
network_acl_rule = self.get_network_acl_rule()
if network_acl_rule:
self.result['changed'] = True
args = {
'id': network_acl_rule['id'],
}
if not self.module.check_mode:
res = self.query_api('deleteNetworkACL', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'networkacl')
return network_acl_rule
def _create_network_acl_rule(self, network_acl_rule):
self.result['changed'] = True
protocol = self.module.params.get('protocol')
args = {
'aclid': self.get_network_acl(key='id'),
'action': self.module.params.get('action_policy'),
'protocol': protocol if protocol != 'by_number' else self.module.params.get('protocol_number'),
'startport': self.module.params.get('start_port'),
'endport': self.get_or_fallback('end_port', 'start_port'),
'number': self.module.params.get('rule_position'),
'icmpcode': self.module.params.get('icmp_code'),
'icmptype': self.module.params.get('icmp_type'),
'traffictype': self.module.params.get('traffic_type'),
'cidrlist': self.module.params.get('cidrs'),
}
if not self.module.check_mode:
res = self.query_api('createNetworkACL', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
network_acl_rule = self.poll_job(res, 'networkacl')
return network_acl_rule
def _update_network_acl_rule(self, network_acl_rule):
protocol = self.module.params.get('protocol')
args = {
'id': network_acl_rule['id'],
'action': self.module.params.get('action_policy'),
'protocol': protocol if protocol != 'by_number' else str(self.module.params.get('protocol_number')),
'startport': self.module.params.get('start_port'),
'endport': self.get_or_fallback('end_port', 'start_port'),
'icmpcode': self.module.params.get('icmp_code'),
'icmptype': self.module.params.get('icmp_type'),
'traffictype': self.module.params.get('traffic_type'),
'cidrlist': ",".join(self.module.params.get('cidrs')),
}
if self.has_changed(args, network_acl_rule):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateNetworkACLItem', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
network_acl_rule = self.poll_job(res, 'networkacl')
return network_acl_rule
def get_result(self, network_acl_rule):
super(AnsibleCloudStackNetworkAclRule, self).get_result(network_acl_rule)
if network_acl_rule:
if 'cidrlist' in network_acl_rule:
self.result['cidrs'] = network_acl_rule['cidrlist'].split(',') or [network_acl_rule['cidrlist']]
if network_acl_rule['protocol'] not in ['tcp', 'udp', 'icmp', 'all']:
self.result['protocol_number'] = int(network_acl_rule['protocol'])
self.result['protocol'] = 'by_number'
self.result['action_policy'] = self.result['action_policy'].lower()
self.result['traffic_type'] = self.result['traffic_type'].lower()
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
network_acl=dict(required=True, aliases=['acl']),
rule_position=dict(required=True, type='int', aliases=['number']),
vpc=dict(required=True),
cidrs=dict(type='list', default=['0.0.0.0/0'], aliases=['cidr']),
protocol=dict(choices=['tcp', 'udp', 'icmp', 'all', 'by_number'], default='tcp'),
protocol_number=dict(type='int'),
traffic_type=dict(choices=['ingress', 'egress'], aliases=['type'], default='ingress'),
action_policy=dict(choices=['allow', 'deny'], aliases=['action'], default='allow'),
icmp_type=dict(type='int'),
icmp_code=dict(type='int'),
start_port=dict(type='int', aliases=['port']),
end_port=dict(type='int'),
state=dict(choices=['present', 'absent'], default='present'),
zone=dict(),
domain=dict(),
account=dict(),
project=dict(),
tags=dict(type='list', aliases=['tag']),
poll_async=dict(type='bool', default=True),
))
required_together = cs_required_together()
required_together.extend([
['icmp_type', 'icmp_code'],
])
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
mutually_exclusive=(
['icmp_type', 'start_port'],
['icmp_type', 'end_port'],
),
supports_check_mode=True
)
acs_network_acl_rule = AnsibleCloudStackNetworkAclRule(module)
state = module.params.get('state')
if state == 'absent':
network_acl_rule = acs_network_acl_rule.absent_network_acl_rule()
else:
network_acl_rule = acs_network_acl_rule.present_network_acl_rule()
result = acs_network_acl_rule.get_result(network_acl_rule)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,424 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, David Passante (@dpassante)
# Copyright (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_network_offering
short_description: Manages network offerings on Apache CloudStack based clouds.
description:
- Create, update, enable, disable and remove network offerings.
author: David Passante (@dpassante)
options:
state:
description:
- State of the network offering.
type: str
choices: [ enabled, present, disabled, absent]
default: present
display_text:
description:
- Display text of the network offerings.
type: str
guest_ip_type:
description:
- Guest type of the network offering.
type: str
choices: [ Shared, Isolated ]
name:
description:
- The name of the network offering.
type: str
required: true
supported_services:
description:
- Services supported by the network offering.
- A list of one or more items from the choice list.
type: list
choices: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, Firewall, StaticNat, Vpn, Lb ]
aliases: [ supported_service ]
traffic_type:
description:
- The traffic type for the network offering.
type: str
default: Guest
availability:
description:
- The availability of network offering. Default value is Optional
type: str
conserve_mode:
description:
- Whether the network offering has IP conserve mode enabled.
type: bool
details:
description:
- Network offering details in key/value pairs.
- with service provider as a value
type: list
egress_default_policy:
description:
- Whether the default egress policy is allow or to deny.
type: str
choices: [ allow, deny ]
persistent:
description:
- True if network offering supports persistent networks
- defaulted to false if not specified
type: bool
keepalive_enabled:
description:
- If true keepalive will be turned on in the loadbalancer.
- At the time of writing this has only an effect on haproxy.
- the mode http and httpclose options are unset in the haproxy conf file.
type: bool
max_connections:
description:
- Maximum number of concurrent connections supported by the network offering.
type: int
network_rate:
description:
- Data transfer rate in megabits per second allowed.
type: int
service_capabilities:
description:
- Desired service capabilities as part of network offering.
type: list
aliases: [ service_capability ]
service_offering:
description:
- The service offering name or ID used by virtual router provider.
type: str
service_providers:
description:
- Provider to service mapping.
- If not specified, the provider for the service will be mapped to the default provider on the physical network.
type: list
aliases: [ service_provider ]
specify_ip_ranges:
description:
- Whether the network offering supports specifying IP ranges.
- Defaulted to C(no) by the API if not specified.
type: bool
specify_vlan:
description:
- Whether the network offering supports vlans or not.
type: bool
for_vpc:
description:
- Whether the offering is meant to be used for VPC or not.
type: bool
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a network offering and enable it
cs_network_offering:
name: my_network_offering
display_text: network offering description
state: enabled
guest_ip_type: Isolated
supported_services: [ Dns, PortForwarding, Dhcp, SourceNat, UserData, Firewall, StaticNat, Vpn, Lb ]
service_providers:
- { service: 'dns', provider: 'virtualrouter' }
- { service: 'dhcp', provider: 'virtualrouter' }
delegate_to: localhost
- name: Remove a network offering
cs_network_offering:
name: my_network_offering
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the network offering.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: The name of the network offering.
returned: success
type: str
sample: MyCustomNetworkOffering
display_text:
description: The display text of the network offering.
returned: success
type: str
sample: My network offering
state:
description: The state of the network offering.
returned: success
type: str
sample: Enabled
guest_ip_type:
description: Guest type of the network offering.
returned: success
type: str
sample: Isolated
availability:
description: The availability of network offering.
returned: success
type: str
sample: Optional
service_offering_id:
description: The service offering ID.
returned: success
type: str
sample: c5f7a5fc-43f8-11e5-a151-feff819cdc9f
max_connections:
description: The maximum number of concurrent connections to be handled by LB.
returned: success
type: int
sample: 300
network_rate:
description: The network traffic transfer ate in Mbit/s.
returned: success
type: int
sample: 200
traffic_type:
description: The traffic type.
returned: success
type: str
sample: Guest
egress_default_policy:
description: Default egress policy.
returned: success
type: str
sample: allow
is_persistent:
description: Whether persistent networks are supported or not.
returned: success
type: bool
sample: false
is_default:
description: Whether network offering is the default offering or not.
returned: success
type: bool
sample: false
for_vpc:
description: Whether the offering is meant to be used for VPC or not.
returned: success
type: bool
sample: false
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackNetworkOffering(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackNetworkOffering, self).__init__(module)
self.returns = {
'guestiptype': 'guest_ip_type',
'availability': 'availability',
'serviceofferingid': 'service_offering_id',
'networkrate': 'network_rate',
'maxconnections': 'max_connections',
'traffictype': 'traffic_type',
'isdefault': 'is_default',
'ispersistent': 'is_persistent',
'forvpc': 'for_vpc'
}
self.network_offering = None
def get_service_offering_id(self):
service_offering = self.module.params.get('service_offering')
if not service_offering:
return None
args = {
'issystem': True
}
service_offerings = self.query_api('listServiceOfferings', **args)
if service_offerings:
for s in service_offerings['serviceoffering']:
if service_offering in [s['name'], s['id']]:
return s['id']
self.fail_json(msg="Service offering '%s' not found" % service_offering)
def get_network_offering(self):
if self.network_offering:
return self.network_offering
args = {
'name': self.module.params.get('name'),
'guestiptype': self.module.params.get('guest_type'),
}
no = self.query_api('listNetworkOfferings', **args)
if no:
self.network_offering = no['networkoffering'][0]
return self.network_offering
def create_or_update(self):
network_offering = self.get_network_offering()
if not network_offering:
network_offering = self.create_network_offering()
return self.update_network_offering(network_offering=network_offering)
def create_network_offering(self):
network_offering = None
self.result['changed'] = True
args = {
'state': self.module.params.get('state'),
'displaytext': self.module.params.get('display_text'),
'guestiptype': self.module.params.get('guest_ip_type'),
'name': self.module.params.get('name'),
'supportedservices': self.module.params.get('supported_services'),
'traffictype': self.module.params.get('traffic_type'),
'availability': self.module.params.get('availability'),
'conservemode': self.module.params.get('conserve_mode'),
'details': self.module.params.get('details'),
'egressdefaultpolicy': self.module.params.get('egress_default_policy') == 'allow',
'ispersistent': self.module.params.get('persistent'),
'keepaliveenabled': self.module.params.get('keepalive_enabled'),
'maxconnections': self.module.params.get('max_connections'),
'networkrate': self.module.params.get('network_rate'),
'servicecapabilitylist': self.module.params.get('service_capabilities'),
'serviceofferingid': self.get_service_offering_id(),
'serviceproviderlist': self.module.params.get('service_providers'),
'specifyipranges': self.module.params.get('specify_ip_ranges'),
'specifyvlan': self.module.params.get('specify_vlan'),
'forvpc': self.module.params.get('for_vpc'),
}
required_params = [
'display_text',
'guest_ip_type',
'supported_services',
'service_providers',
]
self.module.fail_on_missing_params(required_params=required_params)
if not self.module.check_mode:
res = self.query_api('createNetworkOffering', **args)
network_offering = res['networkoffering']
return network_offering
def delete_network_offering(self):
network_offering = self.get_network_offering()
if network_offering:
self.result['changed'] = True
if not self.module.check_mode:
self.query_api('deleteNetworkOffering', id=network_offering['id'])
return network_offering
def update_network_offering(self, network_offering):
if not network_offering:
return network_offering
args = {
'id': network_offering['id'],
'state': self.module.params.get('state'),
'displaytext': self.module.params.get('display_text'),
'name': self.module.params.get('name'),
'availability': self.module.params.get('availability'),
'maxconnections': self.module.params.get('max_connections'),
}
if args['state'] in ['enabled', 'disabled']:
args['state'] = args['state'].title()
else:
del args['state']
if self.has_changed(args, network_offering):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateNetworkOffering', **args)
network_offering = res['networkoffering']
return network_offering
def get_result(self, network_offering):
super(AnsibleCloudStackNetworkOffering, self).get_result(network_offering)
if network_offering:
self.result['egress_default_policy'] = 'allow' if network_offering.get('egressdefaultpolicy') else 'deny'
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
state=dict(choices=['enabled', 'present', 'disabled', 'absent'], default='present'),
display_text=dict(),
guest_ip_type=dict(choices=['Shared', 'Isolated']),
name=dict(required=True),
supported_services=dict(type='list', aliases=['supported_service'], choices=[
'Dns',
'PortForwarding',
'Dhcp',
'SourceNat',
'UserData',
'Firewall',
'StaticNat',
'Vpn',
'Lb',
]),
traffic_type=dict(default='Guest'),
availability=dict(),
conserve_mode=dict(type='bool'),
details=dict(type='list'),
egress_default_policy=dict(choices=['allow', 'deny']),
persistent=dict(type='bool'),
keepalive_enabled=dict(type='bool'),
max_connections=dict(type='int'),
network_rate=dict(type='int'),
service_capabilities=dict(type='list', aliases=['service_capability']),
service_offering=dict(),
service_providers=dict(type='list', aliases=['service_provider']),
specify_ip_ranges=dict(type='bool'),
specify_vlan=dict(type='bool'),
for_vpc=dict(type='bool'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_network_offering = AnsibleCloudStackNetworkOffering(module)
state = module.params.get('state')
if state in ['absent']:
network_offering = acs_network_offering.delete_network_offering()
else:
network_offering = acs_network_offering.create_or_update()
result = acs_network_offering.get_result(network_offering)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,483 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2017, Netservers Ltd. <support@netservers.co.uk>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_physical_network
short_description: Manages physical networks on Apache CloudStack based clouds.
description:
- Create, update and remove networks.
- Enabled and disabled Network Service Providers
- Enables Internal LoadBalancer and VPC/VirtualRouter elements as required
author:
- Netservers Ltd. (@netservers)
- Patryk Cichy (@PatTheSilent)
options:
name:
description:
- Name of the physical network.
required: true
aliases:
- physical_network
type: str
zone:
description:
- Name of the zone in which the network belongs.
- If not set, default zone is used.
type: str
broadcast_domain_range:
description:
- broadcast domain range for the physical network[Pod or Zone].
choices: [ POD, ZONE ]
type: str
domain:
description:
- Domain the network is owned by.
type: str
isolation_method:
description:
- Isolation method for the physical network.
choices: [ VLAN, GRE, L3 ]
type: str
network_speed:
description:
- The speed for the physical network.
choices: [1G, 10G]
type: str
tags:
description:
- A tag to identify this network.
- Physical networks support only one tag.
- To remove an existing tag pass an empty string.
aliases:
- tag
type: str
vlan:
description:
- The VLAN/VNI Ranges of the physical network.
type: str
nsps_enabled:
description:
- List of Network Service Providers to enable.
type: list
nsps_disabled:
description:
- List of Network Service Providers to disable.
type: list
state:
description:
- State of the physical network.
default: present
type: str
choices: [ present, absent, disabled, enabled ]
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Ensure a network is present
cs_physical_network:
name: net01
zone: zone01
isolation_method: VLAN
broadcast_domain_range: ZONE
delegate_to: localhost
- name: Set a tag on a network
cs_physical_network:
name: net01
tag: overlay
delegate_to: localhost
- name: Remove tag on a network
cs_physical_network:
name: net01
tag: ""
delegate_to: localhost
- name: Ensure a network is enabled with specific nsps enabled
cs_physical_network:
name: net01
zone: zone01
isolation_method: VLAN
vlan: 100-200,300-400
broadcast_domain_range: ZONE
state: enabled
nsps_enabled:
- virtualrouter
- internallbvm
- vpcvirtualrouter
delegate_to: localhost
- name: Ensure a network is disabled
cs_physical_network:
name: net01
zone: zone01
state: disabled
delegate_to: localhost
- name: Ensure a network is enabled
cs_physical_network:
name: net01
zone: zone01
state: enabled
delegate_to: localhost
- name: Ensure a network is absent
cs_physical_network:
name: net01
zone: zone01
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the network.
returned: success
type: str
sample: 3f8f25cd-c498-443f-9058-438cfbcbff50
name:
description: Name of the network.
returned: success
type: str
sample: net01
state:
description: State of the network [Enabled/Disabled].
returned: success
type: str
sample: Enabled
broadcast_domain_range:
description: broadcastdomainrange of the network [POD / ZONE].
returned: success
type: str
sample: ZONE
isolation_method:
description: isolationmethod of the network [VLAN/GRE/L3].
returned: success
type: str
sample: VLAN
network_speed:
description: networkspeed of the network [1G/10G].
returned: success
type: str
sample: 1G
zone:
description: Name of zone the physical network is in.
returned: success
type: str
sample: ch-gva-2
domain:
description: Name of domain the network is in.
returned: success
type: str
sample: domain1
nsps:
description: list of enabled or disabled Network Service Providers
type: complex
returned: on enabling/disabling of Network Service Providers
contains:
enabled:
description: list of Network Service Providers that were enabled
returned: on Network Service Provider enabling
type: list
sample:
- virtualrouter
disabled:
description: list of Network Service Providers that were disabled
returned: on Network Service Provider disabling
type: list
sample:
- internallbvm
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackPhysicalNetwork(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackPhysicalNetwork, self).__init__(module)
self.returns = {
'isolationmethods': 'isolation_method',
'broadcastdomainrange': 'broadcast_domain_range',
'networkspeed': 'network_speed',
'vlan': 'vlan',
'tags': 'tags',
}
self.nsps = []
self.vrouters = None
self.loadbalancers = None
def _get_common_args(self):
args = {
'name': self.module.params.get('name'),
'isolationmethods': self.module.params.get('isolation_method'),
'broadcastdomainrange': self.module.params.get('broadcast_domain_range'),
'networkspeed': self.module.params.get('network_speed'),
'tags': self.module.params.get('tags'),
'vlan': self.module.params.get('vlan'),
}
state = self.module.params.get('state')
if state in ['enabled', 'disabled']:
args['state'] = state.capitalize()
return args
def get_physical_network(self, key=None):
physical_network = self.module.params.get('name')
if self.physical_network:
return self._get_by_key(key, self.physical_network)
args = {
'zoneid': self.get_zone(key='id')
}
physical_networks = self.query_api('listPhysicalNetworks', **args)
if physical_networks:
for net in physical_networks['physicalnetwork']:
if physical_network.lower() in [net['name'].lower(), net['id']]:
self.physical_network = net
self.result['physical_network'] = net['name']
break
return self._get_by_key(key, self.physical_network)
def get_nsp(self, name=None):
if not self.nsps:
args = {
'physicalnetworkid': self.get_physical_network(key='id')
}
res = self.query_api('listNetworkServiceProviders', **args)
self.nsps = res['networkserviceprovider']
names = []
for nsp in self.nsps:
names.append(nsp['name'])
if nsp['name'].lower() == name.lower():
return nsp
self.module.fail_json(msg="Failed: '{0}' not in network service providers list '[{1}]'".format(name, names))
def update_nsp(self, name=None, state=None, service_list=None):
nsp = self.get_nsp(name)
if not service_list and nsp['state'] == state:
return nsp
args = {
'id': nsp['id'],
'servicelist': service_list,
'state': state
}
if not self.module.check_mode:
res = self.query_api('updateNetworkServiceProvider', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
nsp = self.poll_job(res, 'networkserviceprovider')
self.result['changed'] = True
return nsp
def get_vrouter_element(self, nsp_name='virtualrouter'):
nsp = self.get_nsp(nsp_name)
nspid = nsp['id']
if self.vrouters is None:
self.vrouters = dict()
res = self.query_api('listVirtualRouterElements', )
for vrouter in res['virtualrouterelement']:
self.vrouters[vrouter['nspid']] = vrouter
if nspid not in self.vrouters:
self.module.fail_json(msg="Failed: No VirtualRouterElement found for nsp '%s'" % nsp_name)
return self.vrouters[nspid]
def get_loadbalancer_element(self, nsp_name='internallbvm'):
nsp = self.get_nsp(nsp_name)
nspid = nsp['id']
if self.loadbalancers is None:
self.loadbalancers = dict()
res = self.query_api('listInternalLoadBalancerElements', )
for loadbalancer in res['internalloadbalancerelement']:
self.loadbalancers[loadbalancer['nspid']] = loadbalancer
if nspid not in self.loadbalancers:
self.module.fail_json(msg="Failed: No Loadbalancer found for nsp '%s'" % nsp_name)
return self.loadbalancers[nspid]
def set_vrouter_element_state(self, enabled, nsp_name='virtualrouter'):
vrouter = self.get_vrouter_element(nsp_name)
if vrouter['enabled'] == enabled:
return vrouter
args = {
'id': vrouter['id'],
'enabled': enabled
}
if not self.module.check_mode:
res = self.query_api('configureVirtualRouterElement', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
vrouter = self.poll_job(res, 'virtualrouterelement')
self.result['changed'] = True
return vrouter
def set_loadbalancer_element_state(self, enabled, nsp_name='internallbvm'):
loadbalancer = self.get_loadbalancer_element(nsp_name=nsp_name)
if loadbalancer['enabled'] == enabled:
return loadbalancer
args = {
'id': loadbalancer['id'],
'enabled': enabled
}
if not self.module.check_mode:
res = self.query_api('configureInternalLoadBalancerElement', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
loadbalancer = self.poll_job(res, 'internalloadbalancerelement')
self.result['changed'] = True
return loadbalancer
def present_network(self):
network = self.get_physical_network()
if network:
network = self._update_network()
else:
network = self._create_network()
return network
def _create_network(self):
self.result['changed'] = True
args = dict(zoneid=self.get_zone(key='id'))
args.update(self._get_common_args())
if self.get_domain(key='id'):
args['domainid'] = self.get_domain(key='id')
if not self.module.check_mode:
resource = self.query_api('createPhysicalNetwork', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.network = self.poll_job(resource, 'physicalnetwork')
return self.network
def _update_network(self):
network = self.get_physical_network()
args = dict(id=network['id'])
args.update(self._get_common_args())
if self.has_changed(args, network):
self.result['changed'] = True
if not self.module.check_mode:
resource = self.query_api('updatePhysicalNetwork', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.physical_network = self.poll_job(resource, 'physicalnetwork')
return self.physical_network
def absent_network(self):
physical_network = self.get_physical_network()
if physical_network:
self.result['changed'] = True
args = {
'id': physical_network['id'],
}
if not self.module.check_mode:
resource = self.query_api('deletePhysicalNetwork', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(resource, 'success')
return physical_network
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True, aliases=['physical_network']),
zone=dict(),
domain=dict(),
vlan=dict(),
nsps_disabled=dict(type='list'),
nsps_enabled=dict(type='list'),
network_speed=dict(choices=['1G', '10G']),
broadcast_domain_range=dict(choices=['POD', 'ZONE']),
isolation_method=dict(choices=['VLAN', 'GRE', 'L3']),
state=dict(choices=['present', 'enabled', 'disabled', 'absent'], default='present'),
tags=dict(aliases=['tag']),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_network = AnsibleCloudStackPhysicalNetwork(module)
state = module.params.get('state')
nsps_disabled = module.params.get('nsps_disabled', [])
nsps_enabled = module.params.get('nsps_enabled', [])
if state in ['absent']:
network = acs_network.absent_network()
else:
network = acs_network.present_network()
if nsps_disabled is not None:
for name in nsps_disabled:
acs_network.update_nsp(name=name, state='Disabled')
if nsps_enabled is not None:
for nsp_name in nsps_enabled:
if nsp_name.lower() in ['virtualrouter', 'vpcvirtualrouter']:
acs_network.set_vrouter_element_state(enabled=True, nsp_name=nsp_name)
elif nsp_name.lower() == 'internallbvm':
acs_network.set_loadbalancer_element_state(enabled=True, nsp_name=nsp_name)
acs_network.update_nsp(name=nsp_name, state='Enabled')
result = acs_network.get_result(network)
if nsps_enabled:
result['nsps_enabled'] = nsps_enabled
if nsps_disabled:
result['nsps_disabled'] = nsps_disabled
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,297 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_pod
short_description: Manages pods on Apache CloudStack based clouds.
description:
- Create, update, delete pods.
author: René Moser (@resmo)
options:
name:
description:
- Name of the pod.
type: str
required: true
id:
description:
- uuid of the existing pod.
type: str
start_ip:
description:
- Starting IP address for the Pod.
- Required on I(state=present)
type: str
end_ip:
description:
- Ending IP address for the Pod.
type: str
netmask:
description:
- Netmask for the Pod.
- Required on I(state=present)
type: str
gateway:
description:
- Gateway for the Pod.
- Required on I(state=present)
type: str
zone:
description:
- Name of the zone in which the pod belongs to.
- If not set, default zone is used.
type: str
state:
description:
- State of the pod.
type: str
default: present
choices: [ present, enabled, disabled, absent ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Ensure a pod is present
cs_pod:
name: pod1
zone: ch-zrh-ix-01
start_ip: 10.100.10.101
gateway: 10.100.10.1
netmask: 255.255.255.0
delegate_to: localhost
- name: Ensure a pod is disabled
cs_pod:
name: pod1
zone: ch-zrh-ix-01
state: disabled
delegate_to: localhost
- name: Ensure a pod is enabled
cs_pod:
name: pod1
zone: ch-zrh-ix-01
state: enabled
delegate_to: localhost
- name: Ensure a pod is absent
cs_pod:
name: pod1
zone: ch-zrh-ix-01
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the pod.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the pod.
returned: success
type: str
sample: pod01
start_ip:
description: Starting IP of the pod.
returned: success
type: str
sample: 10.100.1.101
end_ip:
description: Ending IP of the pod.
returned: success
type: str
sample: 10.100.1.254
netmask:
description: Netmask of the pod.
returned: success
type: str
sample: 255.255.255.0
gateway:
description: Gateway of the pod.
returned: success
type: str
sample: 10.100.1.1
allocation_state:
description: State of the pod.
returned: success
type: str
sample: Enabled
zone:
description: Name of zone the pod is in.
returned: success
type: str
sample: ch-gva-2
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackPod(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackPod, self).__init__(module)
self.returns = {
'endip': 'end_ip',
'startip': 'start_ip',
'gateway': 'gateway',
'netmask': 'netmask',
'allocationstate': 'allocation_state',
}
self.pod = None
def _get_common_pod_args(self):
args = {
'name': self.module.params.get('name'),
'zoneid': self.get_zone(key='id'),
'startip': self.module.params.get('start_ip'),
'endip': self.module.params.get('end_ip'),
'netmask': self.module.params.get('netmask'),
'gateway': self.module.params.get('gateway')
}
state = self.module.params.get('state')
if state in ['enabled', 'disabled']:
args['allocationstate'] = state.capitalize()
return args
def get_pod(self):
if not self.pod:
args = {
'zoneid': self.get_zone(key='id')
}
uuid = self.module.params.get('id')
if uuid:
args['id'] = uuid
else:
args['name'] = self.module.params.get('name')
pods = self.query_api('listPods', **args)
if pods:
for pod in pods['pod']:
if not args['name']:
self.pod = self._transform_ip_list(pod)
break
elif args['name'] == pod['name']:
self.pod = self._transform_ip_list(pod)
break
return self.pod
def present_pod(self):
pod = self.get_pod()
if pod:
pod = self._update_pod()
else:
pod = self._create_pod()
return pod
def _create_pod(self):
required_params = [
'start_ip',
'netmask',
'gateway',
]
self.module.fail_on_missing_params(required_params=required_params)
pod = None
self.result['changed'] = True
args = self._get_common_pod_args()
if not self.module.check_mode:
res = self.query_api('createPod', **args)
pod = res['pod']
return pod
def _update_pod(self):
pod = self.get_pod()
args = self._get_common_pod_args()
args['id'] = pod['id']
if self.has_changed(args, pod):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updatePod', **args)
pod = res['pod']
return pod
def absent_pod(self):
pod = self.get_pod()
if pod:
self.result['changed'] = True
args = {
'id': pod['id']
}
if not self.module.check_mode:
self.query_api('deletePod', **args)
return pod
def _transform_ip_list(self, resource):
""" Workaround for 4.11 return API break """
keys = ['endip', 'startip']
if resource:
for key in keys:
if key in resource and isinstance(resource[key], list):
resource[key] = resource[key][0]
return resource
def get_result(self, pod):
pod = self._transform_ip_list(pod)
super(AnsibleCloudStackPod, self).get_result(pod)
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
id=dict(),
name=dict(required=True),
gateway=dict(),
netmask=dict(),
start_ip=dict(),
end_ip=dict(),
zone=dict(),
state=dict(choices=['present', 'enabled', 'disabled', 'absent'], default='present'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_pod = AnsibleCloudStackPod(module)
state = module.params.get('state')
if state in ['absent']:
pod = acs_pod.absent_pod()
else:
pod = acs_pod.present_pod()
result = acs_pod.get_result(pod)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,396 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_portforward
short_description: Manages port forwarding rules on Apache CloudStack based clouds.
description:
- Create, update and remove port forwarding rules.
author: René Moser (@resmo)
options:
ip_address:
description:
- Public IP address the rule is assigned to.
type: str
required: true
vm:
description:
- Name of virtual machine which we make the port forwarding rule for.
- Required if I(state=present).
type: str
state:
description:
- State of the port forwarding rule.
type: str
default: present
choices: [ present, absent ]
protocol:
description:
- Protocol of the port forwarding rule.
type: str
default: tcp
choices: [ tcp, udp ]
public_port:
description:
- Start public port for this rule.
type: int
required: true
public_end_port:
description:
- End public port for this rule.
- If not specified equal I(public_port).
type: int
private_port:
description:
- Start private port for this rule.
type: int
required: true
private_end_port:
description:
- End private port for this rule.
- If not specified equal I(private_port).
type: int
open_firewall:
description:
- Whether the firewall rule for public port should be created, while creating the new rule.
- Use M(cs_firewall) for managing firewall rules.
default: no
type: bool
vm_guest_ip:
description:
- VM guest NIC secondary IP address for the port forwarding rule.
type: str
network:
description:
- Name of the network.
type: str
vpc:
description:
- Name of the VPC.
type: str
domain:
description:
- Domain the I(vm) is related to.
type: str
account:
description:
- Account the I(vm) is related to.
type: str
project:
description:
- Name of the project the I(vm) is located in.
type: str
zone:
description:
- Name of the zone in which the virtual machine is in.
- If not set, default zone is used.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- "To delete all tags, set a empty list e.g. I(tags: [])."
type: list
aliases: [ tag ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: 1.2.3.4:80 -> web01:8080
cs_portforward:
ip_address: 1.2.3.4
vm: web01
public_port: 80
private_port: 8080
delegate_to: localhost
- name: forward SSH and open firewall
cs_portforward:
ip_address: '{{ public_ip }}'
vm: '{{ inventory_hostname }}'
public_port: '{{ ansible_ssh_port }}'
private_port: 22
open_firewall: true
delegate_to: localhost
- name: forward DNS traffic, but do not open firewall
cs_portforward:
ip_address: 1.2.3.4
vm: '{{ inventory_hostname }}'
public_port: 53
private_port: 53
protocol: udp
delegate_to: localhost
- name: remove ssh port forwarding
cs_portforward:
ip_address: 1.2.3.4
public_port: 22
private_port: 22
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the public IP address.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
ip_address:
description: Public IP address.
returned: success
type: str
sample: 1.2.3.4
protocol:
description: Protocol.
returned: success
type: str
sample: tcp
private_port:
description: Start port on the virtual machine's IP address.
returned: success
type: int
sample: 80
private_end_port:
description: End port on the virtual machine's IP address.
returned: success
type: int
sample: 80
public_port:
description: Start port on the public IP address.
returned: success
type: int
sample: 80
public_end_port:
description: End port on the public IP address.
returned: success
type: int
sample: 80
tags:
description: Tags related to the port forwarding.
returned: success
type: list
sample: []
vm_name:
description: Name of the virtual machine.
returned: success
type: str
sample: web-01
vm_display_name:
description: Display name of the virtual machine.
returned: success
type: str
sample: web-01
vm_guest_ip:
description: IP of the virtual machine.
returned: success
type: str
sample: 10.101.65.152
vpc:
description: Name of the VPC.
returned: success
type: str
sample: my_vpc
network:
description: Name of the network.
returned: success
type: str
sample: dmz
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import AnsibleCloudStack, cs_argument_spec, cs_required_together
class AnsibleCloudStackPortforwarding(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackPortforwarding, self).__init__(module)
self.returns = {
'virtualmachinedisplayname': 'vm_display_name',
'virtualmachinename': 'vm_name',
'ipaddress': 'ip_address',
'vmguestip': 'vm_guest_ip',
'publicip': 'public_ip',
'protocol': 'protocol',
}
# these values will be casted to int
self.returns_to_int = {
'publicport': 'public_port',
'publicendport': 'public_end_port',
'privateport': 'private_port',
'privateendport': 'private_end_port',
}
self.portforwarding_rule = None
def get_portforwarding_rule(self):
if not self.portforwarding_rule:
protocol = self.module.params.get('protocol')
public_port = self.module.params.get('public_port')
args = {
'ipaddressid': self.get_ip_address(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
}
portforwarding_rules = self.query_api('listPortForwardingRules', **args)
if portforwarding_rules and 'portforwardingrule' in portforwarding_rules:
for rule in portforwarding_rules['portforwardingrule']:
if (protocol == rule['protocol'] and
public_port == int(rule['publicport'])):
self.portforwarding_rule = rule
break
return self.portforwarding_rule
def present_portforwarding_rule(self):
portforwarding_rule = self.get_portforwarding_rule()
if portforwarding_rule:
portforwarding_rule = self.update_portforwarding_rule(portforwarding_rule)
else:
portforwarding_rule = self.create_portforwarding_rule()
if portforwarding_rule:
portforwarding_rule = self.ensure_tags(resource=portforwarding_rule, resource_type='PortForwardingRule')
self.portforwarding_rule = portforwarding_rule
return portforwarding_rule
def create_portforwarding_rule(self):
args = {
'protocol': self.module.params.get('protocol'),
'publicport': self.module.params.get('public_port'),
'publicendport': self.get_or_fallback('public_end_port', 'public_port'),
'privateport': self.module.params.get('private_port'),
'privateendport': self.get_or_fallback('private_end_port', 'private_port'),
'openfirewall': self.module.params.get('open_firewall'),
'vmguestip': self.get_vm_guest_ip(),
'ipaddressid': self.get_ip_address(key='id'),
'virtualmachineid': self.get_vm(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'networkid': self.get_network(key='id'),
}
portforwarding_rule = None
self.result['changed'] = True
if not self.module.check_mode:
portforwarding_rule = self.query_api('createPortForwardingRule', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
portforwarding_rule = self.poll_job(portforwarding_rule, 'portforwardingrule')
return portforwarding_rule
def update_portforwarding_rule(self, portforwarding_rule):
args = {
'protocol': self.module.params.get('protocol'),
'publicport': self.module.params.get('public_port'),
'publicendport': self.get_or_fallback('public_end_port', 'public_port'),
'privateport': self.module.params.get('private_port'),
'privateendport': self.get_or_fallback('private_end_port', 'private_port'),
'vmguestip': self.get_vm_guest_ip(),
'ipaddressid': self.get_ip_address(key='id'),
'virtualmachineid': self.get_vm(key='id'),
'networkid': self.get_network(key='id'),
}
if self.has_changed(args, portforwarding_rule):
self.result['changed'] = True
if not self.module.check_mode:
# API broken in 4.2.1?, workaround using remove/create instead of update
# portforwarding_rule = self.query_api('updatePortForwardingRule', **args)
self.absent_portforwarding_rule()
portforwarding_rule = self.query_api('createPortForwardingRule', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
portforwarding_rule = self.poll_job(portforwarding_rule, 'portforwardingrule')
return portforwarding_rule
def absent_portforwarding_rule(self):
portforwarding_rule = self.get_portforwarding_rule()
if portforwarding_rule:
self.result['changed'] = True
args = {
'id': portforwarding_rule['id'],
}
if not self.module.check_mode:
res = self.query_api('deletePortForwardingRule', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'portforwardingrule')
return portforwarding_rule
def get_result(self, portforwarding_rule):
super(AnsibleCloudStackPortforwarding, self).get_result(portforwarding_rule)
if portforwarding_rule:
for search_key, return_key in self.returns_to_int.items():
if search_key in portforwarding_rule:
self.result[return_key] = int(portforwarding_rule[search_key])
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
ip_address=dict(required=True),
protocol=dict(choices=['tcp', 'udp'], default='tcp'),
public_port=dict(type='int', required=True),
public_end_port=dict(type='int'),
private_port=dict(type='int', required=True),
private_end_port=dict(type='int'),
state=dict(choices=['present', 'absent'], default='present'),
open_firewall=dict(type='bool', default=False),
vm_guest_ip=dict(),
vm=dict(),
vpc=dict(),
network=dict(),
zone=dict(),
domain=dict(),
account=dict(),
project=dict(),
poll_async=dict(type='bool', default=True),
tags=dict(type='list', aliases=['tag']),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_pf = AnsibleCloudStackPortforwarding(module)
state = module.params.get('state')
if state in ['absent']:
pf_rule = acs_pf.absent_portforwarding_rule()
else:
pf_rule = acs_pf.present_portforwarding_rule()
result = acs_pf.get_result(pf_rule)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,279 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_project
short_description: Manages projects on Apache CloudStack based clouds.
description:
- Create, update, suspend, activate and remove projects.
author: René Moser (@resmo)
options:
name:
description:
- Name of the project.
type: str
required: true
display_text:
description:
- Display text of the project.
- If not specified, I(name) will be used as I(display_text).
type: str
state:
description:
- State of the project.
type: str
default: present
choices: [ present, absent, active, suspended ]
domain:
description:
- Domain the project is related to.
type: str
account:
description:
- Account the project is related to.
type: str
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- "If you want to delete all tags, set a empty list e.g. I(tags: [])."
type: list
aliases: [ tag ]
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a project
cs_project:
name: web
tags:
- { key: admin, value: john }
- { key: foo, value: bar }
delegate_to: localhost
- name: Rename a project
cs_project:
name: web
display_text: my web project
delegate_to: localhost
- name: Suspend an existing project
cs_project:
name: web
state: suspended
delegate_to: localhost
- name: Activate an existing project
cs_project:
name: web
state: active
delegate_to: localhost
- name: Remove a project
cs_project:
name: web
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the project.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the project.
returned: success
type: str
sample: web project
display_text:
description: Display text of the project.
returned: success
type: str
sample: web project
state:
description: State of the project.
returned: success
type: str
sample: Active
domain:
description: Domain the project is related to.
returned: success
type: str
sample: example domain
account:
description: Account the project is related to.
returned: success
type: str
sample: example account
tags:
description: List of resource tags associated with the project.
returned: success
type: list
sample: '[ { "key": "foo", "value": "bar" } ]'
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackProject(AnsibleCloudStack):
def get_project(self):
if not self.project:
project = self.module.params.get('name')
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'fetch_list': True,
}
projects = self.query_api('listProjects', **args)
if projects:
for p in projects:
if project.lower() in [p['name'].lower(), p['id']]:
self.project = p
break
return self.project
def present_project(self):
project = self.get_project()
if not project:
project = self.create_project(project)
else:
project = self.update_project(project)
if project:
project = self.ensure_tags(resource=project, resource_type='project')
# refresh resource
self.project = project
return project
def update_project(self, project):
args = {
'id': project['id'],
'displaytext': self.get_or_fallback('display_text', 'name')
}
if self.has_changed(args, project):
self.result['changed'] = True
if not self.module.check_mode:
project = self.query_api('updateProject', **args)
poll_async = self.module.params.get('poll_async')
if project and poll_async:
project = self.poll_job(project, 'project')
return project
def create_project(self, project):
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'displaytext': self.get_or_fallback('display_text', 'name'),
'account': self.get_account('name'),
'domainid': self.get_domain('id')
}
if not self.module.check_mode:
project = self.query_api('createProject', **args)
poll_async = self.module.params.get('poll_async')
if project and poll_async:
project = self.poll_job(project, 'project')
return project
def state_project(self, state='active'):
project = self.present_project()
if project['state'].lower() != state:
self.result['changed'] = True
args = {
'id': project['id']
}
if not self.module.check_mode:
if state == 'suspended':
project = self.query_api('suspendProject', **args)
else:
project = self.query_api('activateProject', **args)
poll_async = self.module.params.get('poll_async')
if project and poll_async:
project = self.poll_job(project, 'project')
return project
def absent_project(self):
project = self.get_project()
if project:
self.result['changed'] = True
args = {
'id': project['id']
}
if not self.module.check_mode:
res = self.query_api('deleteProject', **args)
poll_async = self.module.params.get('poll_async')
if res and poll_async:
res = self.poll_job(res, 'project')
return project
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
display_text=dict(),
state=dict(choices=['present', 'absent', 'active', 'suspended'], default='present'),
domain=dict(),
account=dict(),
poll_async=dict(type='bool', default=True),
tags=dict(type='list', aliases=['tag']),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_project = AnsibleCloudStackProject(module)
state = module.params.get('state')
if state in ['absent']:
project = acs_project.absent_project()
elif state in ['active', 'suspended']:
project = acs_project.state_project(state=state)
else:
project = acs_project.present_project()
result = acs_project.get_result(project)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,193 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_region
short_description: Manages regions on Apache CloudStack based clouds.
description:
- Add, update and remove regions.
author: René Moser (@resmo)
options:
id:
description:
- ID of the region.
- Must be an number (int).
type: int
required: true
name:
description:
- Name of the region.
- Required if I(state=present)
type: str
endpoint:
description:
- Endpoint URL of the region.
- Required if I(state=present)
type: str
state:
description:
- State of the region.
type: str
default: present
choices: [ present, absent ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: create a region
cs_region:
id: 2
name: geneva
endpoint: https://cloud.gva.example.com
delegate_to: localhost
- name: remove a region with ID 2
cs_region:
id: 2
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: ID of the region.
returned: success
type: int
sample: 1
name:
description: Name of the region.
returned: success
type: str
sample: local
endpoint:
description: Endpoint of the region.
returned: success
type: str
sample: http://cloud.example.com
gslb_service_enabled:
description: Whether the GSLB service is enabled or not.
returned: success
type: bool
sample: true
portable_ip_service_enabled:
description: Whether the portable IP service is enabled or not.
returned: success
type: bool
sample: true
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackRegion(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackRegion, self).__init__(module)
self.returns = {
'endpoint': 'endpoint',
'gslbserviceenabled': 'gslb_service_enabled',
'portableipserviceenabled': 'portable_ip_service_enabled',
}
def get_region(self):
id = self.module.params.get('id')
regions = self.query_api('listRegions', id=id)
if regions:
return regions['region'][0]
return None
def present_region(self):
region = self.get_region()
if not region:
region = self._create_region(region=region)
else:
region = self._update_region(region=region)
return region
def _create_region(self, region):
self.result['changed'] = True
args = {
'id': self.module.params.get('id'),
'name': self.module.params.get('name'),
'endpoint': self.module.params.get('endpoint')
}
if not self.module.check_mode:
res = self.query_api('addRegion', **args)
region = res['region']
return region
def _update_region(self, region):
args = {
'id': self.module.params.get('id'),
'name': self.module.params.get('name'),
'endpoint': self.module.params.get('endpoint')
}
if self.has_changed(args, region):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateRegion', **args)
region = res['region']
return region
def absent_region(self):
region = self.get_region()
if region:
self.result['changed'] = True
if not self.module.check_mode:
self.query_api('removeRegion', id=region['id'])
return region
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
id=dict(required=True, type='int'),
name=dict(),
endpoint=dict(),
state=dict(choices=['present', 'absent'], default='present'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
required_if=[
('state', 'present', ['name', 'endpoint']),
],
supports_check_mode=True
)
acs_region = AnsibleCloudStackRegion(module)
state = module.params.get('state')
if state == 'absent':
region = acs_region.absent_region()
else:
region = acs_region.present_region()
result = acs_region.get_result(region)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,208 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_resourcelimit
short_description: Manages resource limits on Apache CloudStack based clouds.
description:
- Manage limits of resources for domains, accounts and projects.
author: René Moser (@resmo)
options:
resource_type:
description:
- Type of the resource.
type: str
required: true
choices:
- instance
- ip_address
- volume
- snapshot
- template
- network
- vpc
- cpu
- memory
- primary_storage
- secondary_storage
aliases: [ type ]
limit:
description:
- Maximum number of the resource.
- Default is unlimited C(-1).
type: int
default: -1
aliases: [ max ]
domain:
description:
- Domain the resource is related to.
type: str
account:
description:
- Account the resource is related to.
type: str
project:
description:
- Name of the project the resource is related to.
type: str
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Update a resource limit for instances of a domain
cs_resourcelimit:
type: instance
limit: 10
domain: customers
delegate_to: localhost
- name: Update a resource limit for instances of an account
cs_resourcelimit:
type: instance
limit: 12
account: moserre
domain: customers
delegate_to: localhost
'''
RETURN = '''
---
recource_type:
description: Type of the resource
returned: success
type: str
sample: instance
limit:
description: Maximum number of the resource.
returned: success
type: int
sample: -1
domain:
description: Domain the resource is related to.
returned: success
type: str
sample: example domain
account:
description: Account the resource is related to.
returned: success
type: str
sample: example account
project:
description: Project the resource is related to.
returned: success
type: str
sample: example project
'''
# import cloudstack common
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_required_together,
cs_argument_spec
)
RESOURCE_TYPES = {
'instance': 0,
'ip_address': 1,
'volume': 2,
'snapshot': 3,
'template': 4,
'network': 6,
'vpc': 7,
'cpu': 8,
'memory': 9,
'primary_storage': 10,
'secondary_storage': 11,
}
class AnsibleCloudStackResourceLimit(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackResourceLimit, self).__init__(module)
self.returns = {
'max': 'limit',
}
def get_resource_type(self):
resource_type = self.module.params.get('resource_type')
return RESOURCE_TYPES.get(resource_type)
def get_resource_limit(self):
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'resourcetype': self.get_resource_type()
}
resource_limit = self.query_api('listResourceLimits', **args)
if resource_limit:
if 'limit' in resource_limit['resourcelimit'][0]:
resource_limit['resourcelimit'][0]['limit'] = int(resource_limit['resourcelimit'][0])
return resource_limit['resourcelimit'][0]
self.module.fail_json(msg="Resource limit type '%s' not found." % self.module.params.get('resource_type'))
def update_resource_limit(self):
resource_limit = self.get_resource_limit()
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'resourcetype': self.get_resource_type(),
'max': self.module.params.get('limit', -1)
}
if self.has_changed(args, resource_limit):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateResourceLimit', **args)
resource_limit = res['resourcelimit']
return resource_limit
def get_result(self, resource_limit):
self.result = super(AnsibleCloudStackResourceLimit, self).get_result(resource_limit)
self.result['resource_type'] = self.module.params.get('resource_type')
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
resource_type=dict(required=True, choices=RESOURCE_TYPES.keys(), aliases=['type']),
limit=dict(default=-1, aliases=['max'], type='int'),
domain=dict(),
account=dict(),
project=dict(),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_resource_limit = AnsibleCloudStackResourceLimit(module)
resource_limit = acs_resource_limit.update_resource_limit()
result = acs_resource_limit.get_result(resource_limit)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,212 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_role
short_description: Manages user roles on Apache CloudStack based clouds.
description:
- Create, update, delete user roles.
author: René Moser (@resmo)
options:
name:
description:
- Name of the role.
type: str
required: true
uuid:
description:
- ID of the role.
- If provided, I(uuid) is used as key.
type: str
aliases: [ id ]
role_type:
description:
- Type of the role.
- Only considered for creation.
type: str
default: User
choices: [ User, DomainAdmin, ResourceAdmin, Admin ]
description:
description:
- Description of the role.
type: str
state:
description:
- State of the role.
type: str
default: present
choices: [ present, absent ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Ensure an user role is present
cs_role:
name: myrole_user
delegate_to: localhost
- name: Ensure a role having particular ID is named as myrole_user
cs_role:
name: myrole_user
id: 04589590-ac63-4ffc-93f5-b698b8ac38b6
delegate_to: localhost
- name: Ensure a role is absent
cs_role:
name: myrole_user
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the role.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the role.
returned: success
type: str
sample: myrole
description:
description: Description of the role.
returned: success
type: str
sample: "This is my role description"
role_type:
description: Type of the role.
returned: success
type: str
sample: User
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackRole(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackRole, self).__init__(module)
self.returns = {
'type': 'role_type',
}
def get_role(self):
uuid = self.module.params.get('uuid')
if uuid:
args = {
'id': uuid,
}
roles = self.query_api('listRoles', **args)
if roles:
return roles['role'][0]
else:
args = {
'name': self.module.params.get('name'),
}
roles = self.query_api('listRoles', **args)
if roles:
return roles['role'][0]
return None
def present_role(self):
role = self.get_role()
if role:
role = self._update_role(role)
else:
role = self._create_role(role)
return role
def _create_role(self, role):
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'type': self.module.params.get('role_type'),
'description': self.module.params.get('description'),
}
if not self.module.check_mode:
res = self.query_api('createRole', **args)
role = res['role']
return role
def _update_role(self, role):
args = {
'id': role['id'],
'name': self.module.params.get('name'),
'description': self.module.params.get('description'),
}
if self.has_changed(args, role):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateRole', **args)
# The API as in 4.9 does not return an updated role yet
if 'role' not in res:
role = self.get_role()
else:
role = res['role']
return role
def absent_role(self):
role = self.get_role()
if role:
self.result['changed'] = True
args = {
'id': role['id'],
}
if not self.module.check_mode:
self.query_api('deleteRole', **args)
return role
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
uuid=dict(aliases=['id']),
name=dict(required=True),
description=dict(),
role_type=dict(choices=['User', 'DomainAdmin', 'ResourceAdmin', 'Admin'], default='User'),
state=dict(choices=['present', 'absent'], default='present'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_role = AnsibleCloudStackRole(module)
state = module.params.get('state')
if state == 'absent':
role = acs_role.absent_role()
else:
role = acs_role.present_role()
result = acs_role.get_result(role)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,352 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, David Passante (@dpassante)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_role_permission
short_description: Manages role permissions on Apache CloudStack based clouds.
description:
- Create, update and remove CloudStack role permissions.
- Managing role permissions only supported in CloudStack >= 4.9.
author: David Passante (@dpassante)
options:
name:
description:
- The API name of the permission.
type: str
required: true
role:
description:
- Name or ID of the role.
type: str
required: true
permission:
description:
- The rule permission, allow or deny. Defaulted to deny.
type: str
choices: [ allow, deny ]
default: deny
state:
description:
- State of the role permission.
type: str
choices: [ present, absent ]
default: present
description:
description:
- The description of the role permission.
type: str
parent:
description:
- The parent role permission uuid. use 0 to move this rule at the top of the list.
type: str
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a role permission
cs_role_permission:
role: My_Custom_role
name: createVPC
permission: allow
description: My comments
delegate_to: localhost
- name: Remove a role permission
cs_role_permission:
state: absent
role: My_Custom_role
name: createVPC
delegate_to: localhost
- name: Update a system role permission
cs_role_permission:
role: Domain Admin
name: createVPC
permission: deny
delegate_to: localhost
- name: Update rules order. Move the rule at the top of list
cs_role_permission:
role: Domain Admin
name: createVPC
parent: 0
delegate_to: localhost
'''
RETURN = '''
---
id:
description: The ID of the role permission.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: The API name of the permission.
returned: success
type: str
sample: createVPC
permission:
description: The permission type of the api name.
returned: success
type: str
sample: allow
role_id:
description: The ID of the role to which the role permission belongs.
returned: success
type: str
sample: c6f7a5fc-43f8-11e5-a151-feff819cdc7f
description:
description: The description of the role permission
returned: success
type: str
sample: Deny createVPC for users
'''
from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackRolePermission(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackRolePermission, self).__init__(module)
cloudstack_min_version = LooseVersion('4.9.2')
self.returns = {
'id': 'id',
'roleid': 'role_id',
'rule': 'name',
'permission': 'permission',
'description': 'description',
}
self.role_permission = None
self.cloudstack_version = self._cloudstack_ver()
if self.cloudstack_version < cloudstack_min_version:
self.fail_json(msg="This module requires CloudStack >= %s." % cloudstack_min_version)
def _cloudstack_ver(self):
capabilities = self.get_capabilities()
return LooseVersion(capabilities['cloudstackversion'])
def _get_role_id(self):
role = self.module.params.get('role')
if not role:
return None
res = self.query_api('listRoles')
roles = res['role']
if roles:
for r in roles:
if role in [r['name'], r['id']]:
return r['id']
self.fail_json(msg="Role '%s' not found" % role)
def _get_role_perm(self):
role_permission = self.role_permission
args = {
'roleid': self._get_role_id(),
}
rp = self.query_api('listRolePermissions', **args)
if rp:
role_permission = rp['rolepermission']
return role_permission
def _get_rule(self, rule=None):
if not rule:
rule = self.module.params.get('name')
if self._get_role_perm():
for _rule in self._get_role_perm():
if rule == _rule['rule'] or rule == _rule['id']:
return _rule
return None
def _get_rule_order(self):
perms = self._get_role_perm()
rules = []
if perms:
for i, rule in enumerate(perms):
rules.append(rule['id'])
return rules
def replace_rule(self):
old_rule = self._get_rule()
if old_rule:
rules_order = self._get_rule_order()
old_pos = rules_order.index(old_rule['id'])
self.remove_role_perm()
new_rule = self.create_role_perm()
if new_rule:
perm_order = self.order_permissions(int(old_pos - 1), new_rule['id'])
return perm_order
return None
def order_permissions(self, parent, rule_id):
rules = self._get_rule_order()
if isinstance(parent, int):
parent_pos = parent
elif parent == '0':
parent_pos = -1
else:
parent_rule = self._get_rule(parent)
if not parent_rule:
self.fail_json(msg="Parent rule '%s' not found" % parent)
parent_pos = rules.index(parent_rule['id'])
r_id = rules.pop(rules.index(rule_id))
rules.insert((parent_pos + 1), r_id)
rules = ','.join(map(str, rules))
return rules
def create_or_update_role_perm(self):
role_permission = self._get_rule()
if not role_permission:
role_permission = self.create_role_perm()
else:
role_permission = self.update_role_perm(role_permission)
return role_permission
def create_role_perm(self):
role_permission = None
self.result['changed'] = True
args = {
'rule': self.module.params.get('name'),
'description': self.module.params.get('description'),
'roleid': self._get_role_id(),
'permission': self.module.params.get('permission'),
}
if not self.module.check_mode:
res = self.query_api('createRolePermission', **args)
role_permission = res['rolepermission']
return role_permission
def update_role_perm(self, role_perm):
perm_order = None
if not self.module.params.get('parent'):
args = {
'ruleid': role_perm['id'],
'roleid': role_perm['roleid'],
'permission': self.module.params.get('permission'),
}
if self.has_changed(args, role_perm, only_keys=['permission']):
self.result['changed'] = True
if not self.module.check_mode:
if self.cloudstack_version >= LooseVersion('4.11.0'):
self.query_api('updateRolePermission', **args)
role_perm = self._get_rule()
else:
perm_order = self.replace_rule()
else:
perm_order = self.order_permissions(self.module.params.get('parent'), role_perm['id'])
if perm_order:
args = {
'roleid': role_perm['roleid'],
'ruleorder': perm_order,
}
self.result['changed'] = True
if not self.module.check_mode:
self.query_api('updateRolePermission', **args)
role_perm = self._get_rule()
return role_perm
def remove_role_perm(self):
role_permission = self._get_rule()
if role_permission:
self.result['changed'] = True
args = {
'id': role_permission['id'],
}
if not self.module.check_mode:
self.query_api('deleteRolePermission', **args)
return role_permission
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
role=dict(required=True),
name=dict(required=True),
permission=dict(choices=['allow', 'deny'], default='deny'),
description=dict(),
state=dict(choices=['present', 'absent'], default='present'),
parent=dict(),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
mutually_exclusive=(
['permission', 'parent'],
),
supports_check_mode=True
)
acs_role_perm = AnsibleCloudStackRolePermission(module)
state = module.params.get('state')
if state in ['absent']:
role_permission = acs_role_perm.remove_role_perm()
else:
role_permission = acs_role_perm.create_or_update_role_perm()
result = acs_role_perm.get_result(role_permission)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,377 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_router
short_description: Manages routers on Apache CloudStack based clouds.
description:
- Start, restart, stop and destroy routers.
- I(state=present) is not able to create routers, use M(cs_network) instead.
author: René Moser (@resmo)
options:
name:
description:
- Name of the router.
type: str
required: true
service_offering:
description:
- Name or id of the service offering of the router.
type: str
domain:
description:
- Domain the router is related to.
type: str
account:
description:
- Account the router is related to.
type: str
project:
description:
- Name of the project the router is related to.
type: str
zone:
description:
- Name of the zone the router is deployed in.
- If not set, all zones are used.
type: str
state:
description:
- State of the router.
type: str
default: present
choices: [ present, absent, started, stopped, restarted ]
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
# Ensure the router has the desired service offering, no matter if
# the router is running or not.
- name: Present router
cs_router:
name: r-40-VM
service_offering: System Offering for Software Router
delegate_to: localhost
- name: Ensure started
cs_router:
name: r-40-VM
state: started
delegate_to: localhost
# Ensure started with desired service offering.
# If the service offerings changes, router will be rebooted.
- name: Ensure started with desired service offering
cs_router:
name: r-40-VM
service_offering: System Offering for Software Router
state: started
delegate_to: localhost
- name: Ensure stopped
cs_router:
name: r-40-VM
state: stopped
delegate_to: localhost
- name: Remove a router
cs_router:
name: r-40-VM
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the router.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the router.
returned: success
type: str
sample: r-40-VM
created:
description: Date of the router was created.
returned: success
type: str
sample: 2014-12-01T14:57:57+0100
template_version:
description: Version of the system VM template.
returned: success
type: str
sample: 4.5.1
requires_upgrade:
description: Whether the router needs to be upgraded to the new template.
returned: success
type: bool
sample: false
redundant_state:
description: Redundant state of the router.
returned: success
type: str
sample: UNKNOWN
role:
description: Role of the router.
returned: success
type: str
sample: VIRTUAL_ROUTER
zone:
description: Name of zone the router is in.
returned: success
type: str
sample: ch-gva-2
service_offering:
description: Name of the service offering the router has.
returned: success
type: str
sample: System Offering For Software Router
state:
description: State of the router.
returned: success
type: str
sample: Active
domain:
description: Domain the router is related to.
returned: success
type: str
sample: ROOT
account:
description: Account the router is related to.
returned: success
type: str
sample: admin
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackRouter(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackRouter, self).__init__(module)
self.returns = {
'serviceofferingname': 'service_offering',
'version': 'template_version',
'requiresupgrade': 'requires_upgrade',
'redundantstate': 'redundant_state',
'role': 'role'
}
self.router = None
def get_service_offering_id(self):
service_offering = self.module.params.get('service_offering')
if not service_offering:
return None
args = {
'issystem': True
}
service_offerings = self.query_api('listServiceOfferings', **args)
if service_offerings:
for s in service_offerings['serviceoffering']:
if service_offering in [s['name'], s['id']]:
return s['id']
self.module.fail_json(msg="Service offering '%s' not found" % service_offering)
def get_router(self):
if not self.router:
router = self.module.params.get('name')
args = {
'projectid': self.get_project(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'listall': True,
'fetch_list': True,
}
if self.module.params.get('zone'):
args['zoneid'] = self.get_zone(key='id')
routers = self.query_api('listRouters', **args)
if routers:
for r in routers:
if router.lower() in [r['name'].lower(), r['id']]:
self.router = r
break
return self.router
def start_router(self):
router = self.get_router()
if not router:
self.module.fail_json(msg="Router not found")
if router['state'].lower() != "running":
self.result['changed'] = True
args = {
'id': router['id'],
}
if not self.module.check_mode:
res = self.query_api('startRouter', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
router = self.poll_job(res, 'router')
return router
def stop_router(self):
router = self.get_router()
if not router:
self.module.fail_json(msg="Router not found")
if router['state'].lower() != "stopped":
self.result['changed'] = True
args = {
'id': router['id'],
}
if not self.module.check_mode:
res = self.query_api('stopRouter', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
router = self.poll_job(res, 'router')
return router
def reboot_router(self):
router = self.get_router()
if not router:
self.module.fail_json(msg="Router not found")
self.result['changed'] = True
args = {
'id': router['id'],
}
if not self.module.check_mode:
res = self.query_api('rebootRouter', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
router = self.poll_job(res, 'router')
return router
def absent_router(self):
router = self.get_router()
if router:
self.result['changed'] = True
args = {
'id': router['id'],
}
if not self.module.check_mode:
res = self.query_api('destroyRouter', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'router')
return router
def present_router(self):
router = self.get_router()
if not router:
self.module.fail_json(msg="Router can not be created using the API, see cs_network.")
args = {
'id': router['id'],
'serviceofferingid': self.get_service_offering_id(),
}
state = self.module.params.get('state')
if self.has_changed(args, router):
self.result['changed'] = True
if not self.module.check_mode:
current_state = router['state'].lower()
self.stop_router()
router = self.query_api('changeServiceForRouter', **args)
if state in ['restarted', 'started']:
router = self.start_router()
# if state=present we get to the state before the service
# offering change.
elif state == "present" and current_state == "running":
router = self.start_router()
elif state == "started":
router = self.start_router()
elif state == "stopped":
router = self.stop_router()
elif state == "restarted":
router = self.reboot_router()
return router
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
service_offering=dict(),
state=dict(choices=['present', 'started', 'stopped', 'restarted', 'absent'], default="present"),
domain=dict(),
account=dict(),
project=dict(),
zone=dict(),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_router = AnsibleCloudStackRouter(module)
state = module.params.get('state')
if state in ['absent']:
router = acs_router.absent_router()
else:
router = acs_router.present_router()
result = acs_router.get_result(router)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,200 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_securitygroup
short_description: Manages security groups on Apache CloudStack based clouds.
description:
- Create and remove security groups.
author: René Moser (@resmo)
options:
name:
description:
- Name of the security group.
type: str
required: true
description:
description:
- Description of the security group.
type: str
state:
description:
- State of the security group.
type: str
default: present
choices: [ present, absent ]
domain:
description:
- Domain the security group is related to.
type: str
account:
description:
- Account the security group is related to.
type: str
project:
description:
- Name of the project the security group to be created in.
type: str
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: create a security group
cs_securitygroup:
name: default
description: default security group
delegate_to: localhost
- name: remove a security group
cs_securitygroup:
name: default
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the security group.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: Name of security group.
returned: success
type: str
sample: app
description:
description: Description of security group.
returned: success
type: str
sample: application security group
tags:
description: List of resource tags associated with the security group.
returned: success
type: list
sample: '[ { "key": "foo", "value": "bar" } ]'
project:
description: Name of project the security group is related to.
returned: success
type: str
sample: Production
domain:
description: Domain the security group is related to.
returned: success
type: str
sample: example domain
account:
description: Account the security group is related to.
returned: success
type: str
sample: example account
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import AnsibleCloudStack, cs_argument_spec, cs_required_together
class AnsibleCloudStackSecurityGroup(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackSecurityGroup, self).__init__(module)
self.security_group = None
def get_security_group(self):
if not self.security_group:
args = {
'projectid': self.get_project(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'securitygroupname': self.module.params.get('name'),
}
sgs = self.query_api('listSecurityGroups', **args)
if sgs:
self.security_group = sgs['securitygroup'][0]
return self.security_group
def create_security_group(self):
security_group = self.get_security_group()
if not security_group:
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'projectid': self.get_project(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'description': self.module.params.get('description'),
}
if not self.module.check_mode:
res = self.query_api('createSecurityGroup', **args)
security_group = res['securitygroup']
return security_group
def remove_security_group(self):
security_group = self.get_security_group()
if security_group:
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'projectid': self.get_project(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
}
if not self.module.check_mode:
self.query_api('deleteSecurityGroup', **args)
return security_group
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
description=dict(),
state=dict(choices=['present', 'absent'], default='present'),
project=dict(),
account=dict(),
domain=dict(),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_sg = AnsibleCloudStackSecurityGroup(module)
state = module.params.get('state')
if state in ['absent']:
sg = acs_sg.remove_security_group()
else:
sg = acs_sg.create_security_group()
result = acs_sg.get_result(sg)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,389 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_securitygroup_rule
short_description: Manages security group rules on Apache CloudStack based clouds.
description:
- Add and remove security group rules.
author: René Moser (@resmo)
options:
security_group:
description:
- Name of the security group the rule is related to. The security group must be existing.
type: str
required: true
state:
description:
- State of the security group rule.
type: str
default: present
choices: [ present, absent ]
protocol:
description:
- Protocol of the security group rule.
type: str
default: tcp
choices: [ tcp, udp, icmp, ah, esp, gre ]
type:
description:
- Ingress or egress security group rule.
type: str
default: ingress
choices: [ ingress, egress ]
cidr:
description:
- CIDR (full notation) to be used for security group rule.
type: str
default: 0.0.0.0/0
user_security_group:
description:
- Security group this rule is based of.
type: str
start_port:
description:
- Start port for this rule. Required if I(protocol=tcp) or I(protocol=udp).
type: int
aliases: [ port ]
end_port:
description:
- End port for this rule. Required if I(protocol=tcp) or I(protocol=udp), but I(start_port) will be used if not set.
type: int
icmp_type:
description:
- Type of the icmp message being sent. Required if I(protocol=icmp).
type: int
icmp_code:
description:
- Error code for this icmp message. Required if I(protocol=icmp).
type: int
project:
description:
- Name of the project the security group to be created in.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
---
- name: allow inbound port 80/tcp from 1.2.3.4 added to security group 'default'
cs_securitygroup_rule:
security_group: default
port: 80
cidr: 1.2.3.4/32
delegate_to: localhost
- name: allow tcp/udp outbound added to security group 'default'
cs_securitygroup_rule:
security_group: default
type: egress
start_port: 1
end_port: 65535
protocol: '{{ item }}'
with_items:
- tcp
- udp
delegate_to: localhost
- name: allow inbound icmp from 0.0.0.0/0 added to security group 'default'
cs_securitygroup_rule:
security_group: default
protocol: icmp
icmp_code: -1
icmp_type: -1
delegate_to: localhost
- name: remove rule inbound port 80/tcp from 0.0.0.0/0 from security group 'default'
cs_securitygroup_rule:
security_group: default
port: 80
state: absent
delegate_to: localhost
- name: allow inbound port 80/tcp from security group web added to security group 'default'
cs_securitygroup_rule:
security_group: default
port: 80
user_security_group: web
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the of the rule.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
security_group:
description: security group of the rule.
returned: success
type: str
sample: default
type:
description: type of the rule.
returned: success
type: str
sample: ingress
cidr:
description: CIDR of the rule.
returned: success and cidr is defined
type: str
sample: 0.0.0.0/0
user_security_group:
description: user security group of the rule.
returned: success and user_security_group is defined
type: str
sample: default
protocol:
description: protocol of the rule.
returned: success
type: str
sample: tcp
start_port:
description: start port of the rule.
returned: success
type: int
sample: 80
end_port:
description: end port of the rule.
returned: success
type: int
sample: 80
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import AnsibleCloudStack, cs_argument_spec, cs_required_together
class AnsibleCloudStackSecurityGroupRule(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackSecurityGroupRule, self).__init__(module)
self.returns = {
'icmptype': 'icmp_type',
'icmpcode': 'icmp_code',
'endport': 'end_port',
'startport': 'start_port',
'protocol': 'protocol',
'cidr': 'cidr',
'securitygroupname': 'user_security_group',
}
def _tcp_udp_match(self, rule, protocol, start_port, end_port):
return (protocol in ['tcp', 'udp'] and
protocol == rule['protocol'] and
start_port == int(rule['startport']) and
end_port == int(rule['endport']))
def _icmp_match(self, rule, protocol, icmp_code, icmp_type):
return (protocol == 'icmp' and
protocol == rule['protocol'] and
icmp_code == int(rule['icmpcode']) and
icmp_type == int(rule['icmptype']))
def _ah_esp_gre_match(self, rule, protocol):
return (protocol in ['ah', 'esp', 'gre'] and
protocol == rule['protocol'])
def _type_security_group_match(self, rule, security_group_name):
return (security_group_name and
'securitygroupname' in rule and
security_group_name == rule['securitygroupname'])
def _type_cidr_match(self, rule, cidr):
return ('cidr' in rule and
cidr == rule['cidr'])
def _get_rule(self, rules):
user_security_group_name = self.module.params.get('user_security_group')
cidr = self.module.params.get('cidr')
protocol = self.module.params.get('protocol')
start_port = self.module.params.get('start_port')
end_port = self.get_or_fallback('end_port', 'start_port')
icmp_code = self.module.params.get('icmp_code')
icmp_type = self.module.params.get('icmp_type')
if protocol in ['tcp', 'udp'] and (start_port is None or end_port is None):
self.module.fail_json(msg="no start_port or end_port set for protocol '%s'" % protocol)
if protocol == 'icmp' and (icmp_type is None or icmp_code is None):
self.module.fail_json(msg="no icmp_type or icmp_code set for protocol '%s'" % protocol)
for rule in rules:
if user_security_group_name:
type_match = self._type_security_group_match(rule, user_security_group_name)
else:
type_match = self._type_cidr_match(rule, cidr)
protocol_match = (self._tcp_udp_match(rule, protocol, start_port, end_port) or
self._icmp_match(rule, protocol, icmp_code, icmp_type) or
self._ah_esp_gre_match(rule, protocol))
if type_match and protocol_match:
return rule
return None
def get_security_group(self, security_group_name=None):
if not security_group_name:
security_group_name = self.module.params.get('security_group')
args = {
'securitygroupname': security_group_name,
'projectid': self.get_project('id'),
}
sgs = self.query_api('listSecurityGroups', **args)
if not sgs or 'securitygroup' not in sgs:
self.module.fail_json(msg="security group '%s' not found" % security_group_name)
return sgs['securitygroup'][0]
def add_rule(self):
security_group = self.get_security_group()
args = {}
user_security_group_name = self.module.params.get('user_security_group')
# the user_security_group and cidr are mutually_exclusive, but cidr is defaulted to 0.0.0.0/0.
# that is why we ignore if we have a user_security_group.
if user_security_group_name:
args['usersecuritygrouplist'] = []
user_security_group = self.get_security_group(user_security_group_name)
args['usersecuritygrouplist'].append({
'group': user_security_group['name'],
'account': user_security_group['account'],
})
else:
args['cidrlist'] = self.module.params.get('cidr')
args['protocol'] = self.module.params.get('protocol')
args['startport'] = self.module.params.get('start_port')
args['endport'] = self.get_or_fallback('end_port', 'start_port')
args['icmptype'] = self.module.params.get('icmp_type')
args['icmpcode'] = self.module.params.get('icmp_code')
args['projectid'] = self.get_project('id')
args['securitygroupid'] = security_group['id']
rule = None
res = None
sg_type = self.module.params.get('type')
if sg_type == 'ingress':
if 'ingressrule' in security_group:
rule = self._get_rule(security_group['ingressrule'])
if not rule:
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('authorizeSecurityGroupIngress', **args)
elif sg_type == 'egress':
if 'egressrule' in security_group:
rule = self._get_rule(security_group['egressrule'])
if not rule:
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('authorizeSecurityGroupEgress', **args)
poll_async = self.module.params.get('poll_async')
if res and poll_async:
security_group = self.poll_job(res, 'securitygroup')
key = sg_type + "rule" # ingressrule / egressrule
if key in security_group:
rule = security_group[key][0]
return rule
def remove_rule(self):
security_group = self.get_security_group()
rule = None
res = None
sg_type = self.module.params.get('type')
if sg_type == 'ingress':
rule = self._get_rule(security_group['ingressrule'])
if rule:
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('revokeSecurityGroupIngress', id=rule['ruleid'])
elif sg_type == 'egress':
rule = self._get_rule(security_group['egressrule'])
if rule:
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('revokeSecurityGroupEgress', id=rule['ruleid'])
poll_async = self.module.params.get('poll_async')
if res and poll_async:
res = self.poll_job(res, 'securitygroup')
return rule
def get_result(self, security_group_rule):
super(AnsibleCloudStackSecurityGroupRule, self).get_result(security_group_rule)
self.result['type'] = self.module.params.get('type')
self.result['security_group'] = self.module.params.get('security_group')
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
security_group=dict(required=True),
type=dict(choices=['ingress', 'egress'], default='ingress'),
cidr=dict(default='0.0.0.0/0'),
user_security_group=dict(),
protocol=dict(choices=['tcp', 'udp', 'icmp', 'ah', 'esp', 'gre'], default='tcp'),
icmp_type=dict(type='int'),
icmp_code=dict(type='int'),
start_port=dict(type='int', aliases=['port']),
end_port=dict(type='int'),
state=dict(choices=['present', 'absent'], default='present'),
project=dict(),
poll_async=dict(type='bool', default=True),
))
required_together = cs_required_together()
required_together.extend([
['icmp_type', 'icmp_code'],
])
module = AnsibleModule(
argument_spec=argument_spec,
required_together=required_together,
mutually_exclusive=(
['icmp_type', 'start_port'],
['icmp_type', 'end_port'],
['icmp_code', 'start_port'],
['icmp_code', 'end_port'],
),
supports_check_mode=True
)
acs_sg_rule = AnsibleCloudStackSecurityGroupRule(module)
state = module.params.get('state')
if state in ['absent']:
sg_rule = acs_sg_rule.remove_rule()
else:
sg_rule = acs_sg_rule.add_rule()
result = acs_sg_rule.get_result(sg_rule)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,582 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_service_offering
description:
- Create and delete service offerings for guest and system VMs.
- Update display_text of existing service offering.
short_description: Manages service offerings on Apache CloudStack based clouds.
author: René Moser (@resmo)
options:
disk_bytes_read_rate:
description:
- Bytes read rate of the disk offering.
type: int
aliases: [ bytes_read_rate ]
disk_bytes_write_rate:
description:
- Bytes write rate of the disk offering.
type: int
aliases: [ bytes_write_rate ]
cpu_number:
description:
- The number of CPUs of the service offering.
type: int
cpu_speed:
description:
- The CPU speed of the service offering in MHz.
type: int
limit_cpu_usage:
description:
- Restrict the CPU usage to committed service offering.
type: bool
deployment_planner:
description:
- The deployment planner heuristics used to deploy a VM of this offering.
- If not set, the value of global config I(vm.deployment.planner) is used.
type: str
display_text:
description:
- Display text of the service offering.
- If not set, I(name) will be used as I(display_text) while creating.
type: str
domain:
description:
- Domain the service offering is related to.
- Public for all domains and subdomains if not set.
type: str
host_tags:
description:
- The host tags for this service offering.
type: list
aliases:
- host_tag
hypervisor_snapshot_reserve:
description:
- Hypervisor snapshot reserve space as a percent of a volume.
- Only for managed storage using Xen or VMware.
type: int
is_iops_customized:
description:
- Whether compute offering iops is custom or not.
type: bool
aliases: [ disk_iops_customized ]
disk_iops_read_rate:
description:
- IO requests read rate of the disk offering.
type: int
disk_iops_write_rate:
description:
- IO requests write rate of the disk offering.
type: int
disk_iops_max:
description:
- Max. iops of the compute offering.
type: int
disk_iops_min:
description:
- Min. iops of the compute offering.
type: int
is_system:
description:
- Whether it is a system VM offering or not.
type: bool
default: no
is_volatile:
description:
- Whether the virtual machine needs to be volatile or not.
- Every reboot of VM the root disk is detached then destroyed and a fresh root disk is created and attached to VM.
type: bool
memory:
description:
- The total memory of the service offering in MB.
type: int
name:
description:
- Name of the service offering.
type: str
required: true
network_rate:
description:
- Data transfer rate in Mb/s allowed.
- Supported only for non-system offering and system offerings having I(system_vm_type=domainrouter).
type: int
offer_ha:
description:
- Whether HA is set for the service offering.
type: bool
default: no
provisioning_type:
description:
- Provisioning type used to create volumes.
type: str
choices:
- thin
- sparse
- fat
service_offering_details:
description:
- Details for planner, used to store specific parameters.
- A list of dictionaries having keys C(key) and C(value).
type: list
state:
description:
- State of the service offering.
type: str
choices:
- present
- absent
default: present
storage_type:
description:
- The storage type of the service offering.
type: str
choices:
- local
- shared
system_vm_type:
description:
- The system VM type.
- Required if I(is_system=yes).
type: str
choices:
- domainrouter
- consoleproxy
- secondarystoragevm
storage_tags:
description:
- The storage tags for this service offering.
type: list
aliases:
- storage_tag
is_customized:
description:
- Whether the offering is customizable or not.
type: bool
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a non-volatile compute service offering with local storage
cs_service_offering:
name: Micro
display_text: Micro 512mb 1cpu
cpu_number: 1
cpu_speed: 2198
memory: 512
host_tags: eco
storage_type: local
delegate_to: localhost
- name: Create a volatile compute service offering with shared storage
cs_service_offering:
name: Tiny
display_text: Tiny 1gb 1cpu
cpu_number: 1
cpu_speed: 2198
memory: 1024
storage_type: shared
is_volatile: yes
host_tags: eco
storage_tags: eco
delegate_to: localhost
- name: Create or update a volatile compute service offering with shared storage
cs_service_offering:
name: Tiny
display_text: Tiny 1gb 1cpu
cpu_number: 1
cpu_speed: 2198
memory: 1024
storage_type: shared
is_volatile: yes
host_tags: eco
storage_tags: eco
delegate_to: localhost
- name: Create or update a custom compute service offering
cs_service_offering:
name: custom
display_text: custom compute offer
is_customized: yes
storage_type: shared
host_tags: eco
storage_tags: eco
delegate_to: localhost
- name: Remove a compute service offering
cs_service_offering:
name: Tiny
state: absent
delegate_to: localhost
- name: Create or update a system offering for the console proxy
cs_service_offering:
name: System Offering for Console Proxy 2GB
display_text: System Offering for Console Proxy 2GB RAM
is_system: yes
system_vm_type: consoleproxy
cpu_number: 1
cpu_speed: 2198
memory: 2048
storage_type: shared
storage_tags: perf
delegate_to: localhost
- name: Remove a system offering
cs_service_offering:
name: System Offering for Console Proxy 2GB
is_system: yes
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the service offering
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
cpu_number:
description: Number of CPUs in the service offering
returned: success
type: int
sample: 4
cpu_speed:
description: Speed of CPUs in MHz in the service offering
returned: success
type: int
sample: 2198
disk_iops_max:
description: Max iops of the disk offering
returned: success
type: int
sample: 1000
disk_iops_min:
description: Min iops of the disk offering
returned: success
type: int
sample: 500
disk_bytes_read_rate:
description: Bytes read rate of the service offering
returned: success
type: int
sample: 1000
disk_bytes_write_rate:
description: Bytes write rate of the service offering
returned: success
type: int
sample: 1000
disk_iops_read_rate:
description: IO requests per second read rate of the service offering
returned: success
type: int
sample: 1000
disk_iops_write_rate:
description: IO requests per second write rate of the service offering
returned: success
type: int
sample: 1000
created:
description: Date the offering was created
returned: success
type: str
sample: 2017-11-19T10:48:59+0000
display_text:
description: Display text of the offering
returned: success
type: str
sample: Micro 512mb 1cpu
domain:
description: Domain the offering is into
returned: success
type: str
sample: ROOT
host_tags:
description: List of host tags
returned: success
type: list
sample: [ 'eco' ]
storage_tags:
description: List of storage tags
returned: success
type: list
sample: [ 'eco' ]
is_system:
description: Whether the offering is for system VMs or not
returned: success
type: bool
sample: false
is_iops_customized:
description: Whether the offering uses custom IOPS or not
returned: success
type: bool
sample: false
is_volatile:
description: Whether the offering is volatile or not
returned: success
type: bool
sample: false
limit_cpu_usage:
description: Whether the CPU usage is restricted to committed service offering
returned: success
type: bool
sample: false
memory:
description: Memory of the system offering
returned: success
type: int
sample: 512
name:
description: Name of the system offering
returned: success
type: str
sample: Micro
offer_ha:
description: Whether HA support is enabled in the offering or not
returned: success
type: bool
sample: false
provisioning_type:
description: Provisioning type used to create volumes
returned: success
type: str
sample: thin
storage_type:
description: Storage type used to create volumes
returned: success
type: str
sample: shared
system_vm_type:
description: System VM type of this offering
returned: success
type: str
sample: consoleproxy
service_offering_details:
description: Additioanl service offering details
returned: success
type: dict
sample: "{'vgpuType': 'GRID K180Q','pciDevice':'Group of NVIDIA Corporation GK107GL [GRID K1] GPUs'}"
network_rate:
description: Data transfer rate in megabits per second allowed
returned: success
type: int
sample: 1000
is_customized:
description: Whether the offering is customizable or not
returned: success
type: bool
sample: false
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackServiceOffering(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackServiceOffering, self).__init__(module)
self.returns = {
'cpunumber': 'cpu_number',
'cpuspeed': 'cpu_speed',
'deploymentplanner': 'deployment_planner',
'diskBytesReadRate': 'disk_bytes_read_rate',
'diskBytesWriteRate': 'disk_bytes_write_rate',
'diskIopsReadRate': 'disk_iops_read_rate',
'diskIopsWriteRate': 'disk_iops_write_rate',
'maxiops': 'disk_iops_max',
'miniops': 'disk_iops_min',
'hypervisorsnapshotreserve': 'hypervisor_snapshot_reserve',
'iscustomized': 'is_customized',
'iscustomizediops': 'is_iops_customized',
'issystem': 'is_system',
'isvolatile': 'is_volatile',
'limitcpuuse': 'limit_cpu_usage',
'memory': 'memory',
'networkrate': 'network_rate',
'offerha': 'offer_ha',
'provisioningtype': 'provisioning_type',
'serviceofferingdetails': 'service_offering_details',
'storagetype': 'storage_type',
'systemvmtype': 'system_vm_type',
'tags': 'storage_tags',
}
def get_service_offering(self):
args = {
'name': self.module.params.get('name'),
'domainid': self.get_domain(key='id'),
'issystem': self.module.params.get('is_system'),
'systemvmtype': self.module.params.get('system_vm_type'),
}
service_offerings = self.query_api('listServiceOfferings', **args)
if service_offerings:
return service_offerings['serviceoffering'][0]
def present_service_offering(self):
service_offering = self.get_service_offering()
if not service_offering:
service_offering = self._create_offering(service_offering)
else:
service_offering = self._update_offering(service_offering)
return service_offering
def absent_service_offering(self):
service_offering = self.get_service_offering()
if service_offering:
self.result['changed'] = True
if not self.module.check_mode:
args = {
'id': service_offering['id'],
}
self.query_api('deleteServiceOffering', **args)
return service_offering
def _create_offering(self, service_offering):
self.result['changed'] = True
system_vm_type = self.module.params.get('system_vm_type')
is_system = self.module.params.get('is_system')
required_params = []
if is_system and not system_vm_type:
required_params.append('system_vm_type')
self.module.fail_on_missing_params(required_params=required_params)
args = {
'name': self.module.params.get('name'),
'displaytext': self.get_or_fallback('display_text', 'name'),
'bytesreadrate': self.module.params.get('disk_bytes_read_rate'),
'byteswriterate': self.module.params.get('disk_bytes_write_rate'),
'cpunumber': self.module.params.get('cpu_number'),
'cpuspeed': self.module.params.get('cpu_speed'),
'customizediops': self.module.params.get('is_iops_customized'),
'deploymentplanner': self.module.params.get('deployment_planner'),
'domainid': self.get_domain(key='id'),
'hosttags': self.module.params.get('host_tags'),
'hypervisorsnapshotreserve': self.module.params.get('hypervisor_snapshot_reserve'),
'iopsreadrate': self.module.params.get('disk_iops_read_rate'),
'iopswriterate': self.module.params.get('disk_iops_write_rate'),
'maxiops': self.module.params.get('disk_iops_max'),
'miniops': self.module.params.get('disk_iops_min'),
'issystem': is_system,
'isvolatile': self.module.params.get('is_volatile'),
'memory': self.module.params.get('memory'),
'networkrate': self.module.params.get('network_rate'),
'offerha': self.module.params.get('offer_ha'),
'provisioningtype': self.module.params.get('provisioning_type'),
'serviceofferingdetails': self.module.params.get('service_offering_details'),
'storagetype': self.module.params.get('storage_type'),
'systemvmtype': system_vm_type,
'tags': self.module.params.get('storage_tags'),
'limitcpuuse': self.module.params.get('limit_cpu_usage'),
'customized': self.module.params.get('is_customized')
}
if not self.module.check_mode:
res = self.query_api('createServiceOffering', **args)
service_offering = res['serviceoffering']
return service_offering
def _update_offering(self, service_offering):
args = {
'id': service_offering['id'],
'name': self.module.params.get('name'),
'displaytext': self.get_or_fallback('display_text', 'name'),
}
if self.has_changed(args, service_offering):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateServiceOffering', **args)
service_offering = res['serviceoffering']
return service_offering
def get_result(self, service_offering):
super(AnsibleCloudStackServiceOffering, self).get_result(service_offering)
if service_offering:
if 'hosttags' in service_offering:
self.result['host_tags'] = service_offering['hosttags'].split(',') or [service_offering['hosttags']]
# Prevent confusion, the api returns a tags key for storage tags.
if 'tags' in service_offering:
self.result['storage_tags'] = service_offering['tags'].split(',') or [service_offering['tags']]
if 'tags' in self.result:
del self.result['tags']
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
display_text=dict(),
cpu_number=dict(type='int'),
cpu_speed=dict(type='int'),
limit_cpu_usage=dict(type='bool'),
deployment_planner=dict(),
domain=dict(),
host_tags=dict(type='list', aliases=['host_tag']),
hypervisor_snapshot_reserve=dict(type='int'),
disk_bytes_read_rate=dict(type='int', aliases=['bytes_read_rate']),
disk_bytes_write_rate=dict(type='int', aliases=['bytes_write_rate']),
disk_iops_read_rate=dict(type='int'),
disk_iops_write_rate=dict(type='int'),
disk_iops_max=dict(type='int'),
disk_iops_min=dict(type='int'),
is_system=dict(type='bool', default=False),
is_volatile=dict(type='bool'),
is_iops_customized=dict(type='bool', aliases=['disk_iops_customized']),
memory=dict(type='int'),
network_rate=dict(type='int'),
offer_ha=dict(type='bool'),
provisioning_type=dict(choices=['thin', 'sparse', 'fat']),
service_offering_details=dict(type='list'),
storage_type=dict(choices=['local', 'shared']),
system_vm_type=dict(choices=['domainrouter', 'consoleproxy', 'secondarystoragevm']),
storage_tags=dict(type='list', aliases=['storage_tag']),
state=dict(choices=['present', 'absent'], default='present'),
is_customized=dict(type='bool'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_so = AnsibleCloudStackServiceOffering(module)
state = module.params.get('state')
if state == "absent":
service_offering = acs_so.absent_service_offering()
else:
service_offering = acs_so.present_service_offering()
result = acs_so.get_result(service_offering)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,358 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_snapshot_policy
short_description: Manages volume snapshot policies on Apache CloudStack based clouds.
description:
- Create, update and delete volume snapshot policies.
author: René Moser (@resmo)
options:
volume:
description:
- Name of the volume.
- Either I(volume) or I(vm) is required.
type: str
volume_type:
description:
- Type of the volume.
type: str
choices:
- DATADISK
- ROOT
vm:
description:
- Name of the instance to select the volume from.
- Use I(volume_type) if VM has a DATADISK and ROOT volume.
- In case of I(volume_type=DATADISK), additionally use I(device_id) if VM has more than one DATADISK volume.
- Either I(volume) or I(vm) is required.
type: str
device_id:
description:
- ID of the device on a VM the volume is attached to.
- This will only be considered if VM has multiple DATADISK volumes.
type: int
vpc:
description:
- Name of the vpc the instance is deployed in.
type: str
interval_type:
description:
- Interval of the snapshot.
type: str
default: daily
choices: [ hourly, daily, weekly, monthly ]
aliases: [ interval ]
max_snaps:
description:
- Max number of snapshots.
type: int
default: 8
aliases: [ max ]
schedule:
description:
- Time the snapshot is scheduled. Required if I(state=present).
- 'Format for I(interval_type=HOURLY): C(MM)'
- 'Format for I(interval_type=DAILY): C(MM:HH)'
- 'Format for I(interval_type=WEEKLY): C(MM:HH:DD (1-7))'
- 'Format for I(interval_type=MONTHLY): C(MM:HH:DD (1-28))'
type: str
time_zone:
description:
- Specifies a timezone for this command.
type: str
default: UTC
aliases: [ timezone ]
state:
description:
- State of the snapshot policy.
type: str
default: present
choices: [ present, absent ]
domain:
description:
- Domain the volume is related to.
type: str
account:
description:
- Account the volume is related to.
type: str
project:
description:
- Name of the project the volume is related to.
type: str
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: ensure a snapshot policy daily at 1h00 UTC
cs_snapshot_policy:
volume: ROOT-478
schedule: '00:1'
max_snaps: 3
delegate_to: localhost
- name: ensure a snapshot policy daily at 1h00 UTC on the second DATADISK of VM web-01
cs_snapshot_policy:
vm: web-01
volume_type: DATADISK
device_id: 2
schedule: '00:1'
max_snaps: 3
delegate_to: localhost
- name: ensure a snapshot policy hourly at minute 5 UTC
cs_snapshot_policy:
volume: ROOT-478
schedule: '5'
interval_type: hourly
max_snaps: 1
delegate_to: localhost
- name: ensure a snapshot policy weekly on Sunday at 05h00, TZ Europe/Zurich
cs_snapshot_policy:
volume: ROOT-478
schedule: '00:5:1'
interval_type: weekly
max_snaps: 1
time_zone: 'Europe/Zurich'
delegate_to: localhost
- name: ensure a snapshot policy is absent
cs_snapshot_policy:
volume: ROOT-478
interval_type: hourly
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the snapshot policy.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
interval_type:
description: interval type of the snapshot policy.
returned: success
type: str
sample: daily
schedule:
description: schedule of the snapshot policy.
returned: success
type: str
sample:
max_snaps:
description: maximum number of snapshots retained.
returned: success
type: int
sample: 10
time_zone:
description: the time zone of the snapshot policy.
returned: success
type: str
sample: Etc/UTC
volume:
description: the volume of the snapshot policy.
returned: success
type: str
sample: Etc/UTC
zone:
description: Name of zone the volume is related to.
returned: success
type: str
sample: ch-gva-2
project:
description: Name of project the volume is related to.
returned: success
type: str
sample: Production
account:
description: Account the volume is related to.
returned: success
type: str
sample: example account
domain:
description: Domain the volume is related to.
returned: success
type: str
sample: example domain
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackSnapshotPolicy(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackSnapshotPolicy, self).__init__(module)
self.returns = {
'schedule': 'schedule',
'timezone': 'time_zone',
'maxsnaps': 'max_snaps',
}
self.interval_types = {
'hourly': 0,
'daily': 1,
'weekly': 2,
'monthly': 3,
}
self.volume = None
def get_interval_type(self):
interval_type = self.module.params.get('interval_type')
return self.interval_types[interval_type]
def get_volume(self, key=None):
if self.volume:
return self._get_by_key(key, self.volume)
args = {
'name': self.module.params.get('volume'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'virtualmachineid': self.get_vm(key='id', filter_zone=False),
'type': self.module.params.get('volume_type'),
}
volumes = self.query_api('listVolumes', **args)
if volumes:
if volumes['count'] > 1:
device_id = self.module.params.get('device_id')
if not device_id:
self.module.fail_json(msg="Found more then 1 volume: combine params 'vm', 'volume_type', 'device_id' and/or 'volume' to select the volume")
else:
for v in volumes['volume']:
if v.get('deviceid') == device_id:
self.volume = v
return self._get_by_key(key, self.volume)
self.module.fail_json(msg="No volume found with device id %s" % device_id)
self.volume = volumes['volume'][0]
return self._get_by_key(key, self.volume)
return None
def get_snapshot_policy(self):
args = {
'volumeid': self.get_volume(key='id')
}
policies = self.query_api('listSnapshotPolicies', **args)
if policies:
for policy in policies['snapshotpolicy']:
if policy['intervaltype'] == self.get_interval_type():
return policy
return None
def present_snapshot_policy(self):
required_params = [
'schedule',
]
self.module.fail_on_missing_params(required_params=required_params)
policy = self.get_snapshot_policy()
args = {
'id': policy.get('id') if policy else None,
'intervaltype': self.module.params.get('interval_type'),
'schedule': self.module.params.get('schedule'),
'maxsnaps': self.module.params.get('max_snaps'),
'timezone': self.module.params.get('time_zone'),
'volumeid': self.get_volume(key='id')
}
if not policy or (policy and self.has_changed(policy, args, only_keys=['schedule', 'maxsnaps', 'timezone'])):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('createSnapshotPolicy', **args)
policy = res['snapshotpolicy']
return policy
def absent_snapshot_policy(self):
policy = self.get_snapshot_policy()
if policy:
self.result['changed'] = True
args = {
'id': policy['id']
}
if not self.module.check_mode:
self.query_api('deleteSnapshotPolicies', **args)
return policy
def get_result(self, policy):
super(AnsibleCloudStackSnapshotPolicy, self).get_result(policy)
if policy and 'intervaltype' in policy:
for key, value in self.interval_types.items():
if value == policy['intervaltype']:
self.result['interval_type'] = key
break
volume = self.get_volume()
if volume:
volume_results = {
'volume': volume.get('name'),
'zone': volume.get('zonename'),
'project': volume.get('project'),
'account': volume.get('account'),
'domain': volume.get('domain'),
}
self.result.update(volume_results)
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
volume=dict(),
volume_type=dict(choices=['DATADISK', 'ROOT']),
vm=dict(),
device_id=dict(type='int'),
vpc=dict(),
interval_type=dict(default='daily', choices=['hourly', 'daily', 'weekly', 'monthly'], aliases=['interval']),
schedule=dict(),
time_zone=dict(default='UTC', aliases=['timezone']),
max_snaps=dict(type='int', default=8, aliases=['max']),
state=dict(choices=['present', 'absent'], default='present'),
domain=dict(),
account=dict(),
project=dict(),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
required_one_of=(
['vm', 'volume'],
),
supports_check_mode=True
)
acs_snapshot_policy = AnsibleCloudStackSnapshotPolicy(module)
state = module.params.get('state')
if state in ['absent']:
policy = acs_snapshot_policy.absent_snapshot_policy()
else:
policy = acs_snapshot_policy.present_snapshot_policy()
result = acs_snapshot_policy.get_result(policy)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,267 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_sshkeypair
short_description: Manages SSH keys on Apache CloudStack based clouds.
description:
- Create, register and remove SSH keys.
- If no key was found and no public key was provided and a new SSH
private/public key pair will be created and the private key will be returned.
author: René Moser (@resmo)
options:
name:
description:
- Name of public key.
type: str
required: true
domain:
description:
- Domain the public key is related to.
type: str
account:
description:
- Account the public key is related to.
type: str
project:
description:
- Name of the project the public key to be registered in.
type: str
state:
description:
- State of the public key.
type: str
default: present
choices: [ present, absent ]
public_key:
description:
- String of the public key.
type: str
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: create a new private / public key pair
cs_sshkeypair:
name: linus@example.com
delegate_to: localhost
register: key
- debug:
msg: 'Private key is {{ key.private_key }}'
- name: remove a public key by its name
cs_sshkeypair:
name: linus@example.com
state: absent
delegate_to: localhost
- name: register your existing local public key
cs_sshkeypair:
name: linus@example.com
public_key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the SSH public key.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: Name of the SSH public key.
returned: success
type: str
sample: linus@example.com
fingerprint:
description: Fingerprint of the SSH public key.
returned: success
type: str
sample: "86:5e:a3:e8:bd:95:7b:07:7c:c2:5c:f7:ad:8b:09:28"
private_key:
description: Private key of generated SSH keypair.
returned: changed
type: str
sample: "-----BEGIN RSA PRIVATE KEY-----\nMII...8tO\n-----END RSA PRIVATE KEY-----\n"
'''
import traceback
SSHPUBKEYS_IMP_ERR = None
try:
import sshpubkeys
HAS_LIB_SSHPUBKEYS = True
except ImportError:
SSHPUBKEYS_IMP_ERR = traceback.format_exc()
HAS_LIB_SSHPUBKEYS = False
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils._text import to_native
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_required_together,
cs_argument_spec
)
class AnsibleCloudStackSshKey(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackSshKey, self).__init__(module)
self.returns = {
'privatekey': 'private_key',
'fingerprint': 'fingerprint',
}
self.ssh_key = None
def register_ssh_key(self, public_key):
ssh_key = self.get_ssh_key()
args = self._get_common_args()
name = self.module.params.get('name')
res = None
if not ssh_key:
self.result['changed'] = True
args['publickey'] = public_key
if not self.module.check_mode:
args['name'] = name
res = self.query_api('registerSSHKeyPair', **args)
else:
fingerprint = self._get_ssh_fingerprint(public_key)
if ssh_key['fingerprint'] != fingerprint:
self.result['changed'] = True
if not self.module.check_mode:
# delete the ssh key with matching name but wrong fingerprint
args['name'] = name
self.query_api('deleteSSHKeyPair', **args)
elif ssh_key['name'].lower() != name.lower():
self.result['changed'] = True
if not self.module.check_mode:
# delete the ssh key with matching fingerprint but wrong name
args['name'] = ssh_key['name']
self.query_api('deleteSSHKeyPair', **args)
# First match for key retrievement will be the fingerprint.
# We need to make another lookup if there is a key with identical name.
self.ssh_key = None
ssh_key = self.get_ssh_key()
if ssh_key and ssh_key['fingerprint'] != fingerprint:
args['name'] = name
self.query_api('deleteSSHKeyPair', **args)
if not self.module.check_mode and self.result['changed']:
args['publickey'] = public_key
args['name'] = name
res = self.query_api('registerSSHKeyPair', **args)
if res and 'keypair' in res:
ssh_key = res['keypair']
return ssh_key
def create_ssh_key(self):
ssh_key = self.get_ssh_key()
if not ssh_key:
self.result['changed'] = True
args = self._get_common_args()
args['name'] = self.module.params.get('name')
if not self.module.check_mode:
res = self.query_api('createSSHKeyPair', **args)
ssh_key = res['keypair']
return ssh_key
def remove_ssh_key(self, name=None):
ssh_key = self.get_ssh_key()
if ssh_key:
self.result['changed'] = True
args = self._get_common_args()
args['name'] = name or self.module.params.get('name')
if not self.module.check_mode:
self.query_api('deleteSSHKeyPair', **args)
return ssh_key
def _get_common_args(self):
return {
'domainid': self.get_domain('id'),
'account': self.get_account('name'),
'projectid': self.get_project('id')
}
def get_ssh_key(self):
if not self.ssh_key:
public_key = self.module.params.get('public_key')
if public_key:
# Query by fingerprint of the public key
args_fingerprint = self._get_common_args()
args_fingerprint['fingerprint'] = self._get_ssh_fingerprint(public_key)
ssh_keys = self.query_api('listSSHKeyPairs', **args_fingerprint)
if ssh_keys and 'sshkeypair' in ssh_keys:
self.ssh_key = ssh_keys['sshkeypair'][0]
# When key has not been found by fingerprint, use the name
if not self.ssh_key:
args_name = self._get_common_args()
args_name['name'] = self.module.params.get('name')
ssh_keys = self.query_api('listSSHKeyPairs', **args_name)
if ssh_keys and 'sshkeypair' in ssh_keys:
self.ssh_key = ssh_keys['sshkeypair'][0]
return self.ssh_key
def _get_ssh_fingerprint(self, public_key):
key = sshpubkeys.SSHKey(public_key)
if hasattr(key, 'hash_md5'):
return key.hash_md5().replace(to_native('MD5:'), to_native(''))
return key.hash()
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
public_key=dict(),
domain=dict(),
account=dict(),
project=dict(),
state=dict(choices=['present', 'absent'], default='present'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
if not HAS_LIB_SSHPUBKEYS:
module.fail_json(msg=missing_required_lib("sshpubkeys"), exception=SSHPUBKEYS_IMP_ERR)
acs_sshkey = AnsibleCloudStackSshKey(module)
state = module.params.get('state')
if state in ['absent']:
ssh_key = acs_sshkey.remove_ssh_key()
else:
public_key = module.params.get('public_key')
if public_key:
ssh_key = acs_sshkey.register_ssh_key(public_key)
else:
ssh_key = acs_sshkey.create_ssh_key()
result = acs_sshkey.get_result(ssh_key)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,255 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_staticnat
short_description: Manages static NATs on Apache CloudStack based clouds.
description:
- Create, update and remove static NATs.
author: René Moser (@resmo)
options:
ip_address:
description:
- Public IP address the static NAT is assigned to.
type: str
required: true
vm:
description:
- Name of virtual machine which we make the static NAT for.
- Required if I(state=present).
type: str
vm_guest_ip:
description:
- VM guest NIC secondary IP address for the static NAT.
type: str
network:
description:
- Network the IP address is related to.
type: str
vpc:
description:
- VPC the network related to.
type: str
state:
description:
- State of the static NAT.
type: str
default: present
choices: [ present, absent ]
domain:
description:
- Domain the static NAT is related to.
type: str
account:
description:
- Account the static NAT is related to.
type: str
project:
description:
- Name of the project the static NAT is related to.
type: str
zone:
description:
- Name of the zone in which the virtual machine is in.
- If not set, default zone is used.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a static NAT for IP 1.2.3.4 to web01
cs_staticnat:
ip_address: 1.2.3.4
vm: web01
delegate_to: localhost
- name: Remove a static NAT
cs_staticnat:
ip_address: 1.2.3.4
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the ip_address.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
ip_address:
description: Public IP address.
returned: success
type: str
sample: 1.2.3.4
vm_name:
description: Name of the virtual machine.
returned: success
type: str
sample: web-01
vm_display_name:
description: Display name of the virtual machine.
returned: success
type: str
sample: web-01
vm_guest_ip:
description: IP of the virtual machine.
returned: success
type: str
sample: 10.101.65.152
zone:
description: Name of zone the static NAT is related to.
returned: success
type: str
sample: ch-gva-2
project:
description: Name of project the static NAT is related to.
returned: success
type: str
sample: Production
account:
description: Account the static NAT is related to.
returned: success
type: str
sample: example account
domain:
description: Domain the static NAT is related to.
returned: success
type: str
sample: example domain
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackStaticNat(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackStaticNat, self).__init__(module)
self.returns = {
'virtualmachinedisplayname': 'vm_display_name',
'virtualmachinename': 'vm_name',
'ipaddress': 'ip_address',
'vmipaddress': 'vm_guest_ip',
}
def create_static_nat(self, ip_address):
self.result['changed'] = True
args = {
'virtualmachineid': self.get_vm(key='id'),
'ipaddressid': ip_address['id'],
'vmguestip': self.get_vm_guest_ip(),
'networkid': self.get_network(key='id')
}
if not self.module.check_mode:
self.query_api('enableStaticNat', **args)
# reset ip address and query new values
self.ip_address = None
ip_address = self.get_ip_address()
return ip_address
def update_static_nat(self, ip_address):
args = {
'virtualmachineid': self.get_vm(key='id'),
'ipaddressid': ip_address['id'],
'vmguestip': self.get_vm_guest_ip(),
'networkid': self.get_network(key='id')
}
# make an alias, so we can use has_changed()
ip_address['vmguestip'] = ip_address['vmipaddress']
if self.has_changed(args, ip_address, ['vmguestip', 'virtualmachineid']):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('disableStaticNat', ipaddressid=ip_address['id'])
self.poll_job(res, 'staticnat')
self.query_api('enableStaticNat', **args)
# reset ip address and query new values
self.ip_address = None
ip_address = self.get_ip_address()
return ip_address
def present_static_nat(self):
ip_address = self.get_ip_address()
if not ip_address['isstaticnat']:
ip_address = self.create_static_nat(ip_address)
else:
ip_address = self.update_static_nat(ip_address)
return ip_address
def absent_static_nat(self):
ip_address = self.get_ip_address()
if ip_address['isstaticnat']:
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('disableStaticNat', ipaddressid=ip_address['id'])
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'staticnat')
return ip_address
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
ip_address=dict(required=True),
vm=dict(),
vm_guest_ip=dict(),
network=dict(),
vpc=dict(),
state=dict(choices=['present', 'absent'], default='present'),
zone=dict(),
domain=dict(),
account=dict(),
project=dict(),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_static_nat = AnsibleCloudStackStaticNat(module)
state = module.params.get('state')
if state in ['absent']:
ip_address = acs_static_nat.absent_static_nat()
else:
ip_address = acs_static_nat.present_static_nat()
result = acs_static_nat.get_result(ip_address)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,510 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2017, Netservers Ltd. <support@netservers.co.uk>
# (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_storage_pool
short_description: Manages Primary Storage Pools on Apache CloudStack based clouds.
description:
- Create, update, put into maintenance, disable, enable and remove storage pools.
author:
- Netservers Ltd. (@netservers)
- René Moser (@resmo)
options:
name:
description:
- Name of the storage pool.
type: str
required: true
zone:
description:
- Name of the zone in which the host should be deployed.
- If not set, default zone is used.
type: str
storage_url:
description:
- URL of the storage pool.
- Required if I(state=present).
type: str
pod:
description:
- Name of the pod.
type: str
cluster:
description:
- Name of the cluster.
type: str
scope:
description:
- The scope of the storage pool.
- Defaults to cluster when C(cluster) is provided, otherwise zone.
type: str
choices: [ cluster, zone ]
managed:
description:
- Whether the storage pool should be managed by CloudStack.
- Only considered on creation.
type: bool
hypervisor:
description:
- Required when creating a zone scoped pool.
- Possible values are C(KVM), C(VMware), C(BareMetal), C(XenServer), C(LXC), C(HyperV), C(UCS), C(OVM), C(Simulator).
type: str
storage_tags:
description:
- Tags associated with this storage pool.
type: list
aliases: [ storage_tag ]
provider:
description:
- Name of the storage provider e.g. SolidFire, SolidFireShared, DefaultPrimary, CloudByte.
type: str
default: DefaultPrimary
capacity_bytes:
description:
- Bytes CloudStack can provision from this storage pool.
type: int
capacity_iops:
description:
- Bytes CloudStack can provision from this storage pool.
type: int
allocation_state:
description:
- Allocation state of the storage pool.
type: str
choices: [ enabled, disabled, maintenance ]
state:
description:
- State of the storage pool.
type: str
default: present
choices: [ present, absent ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: ensure a zone scoped storage_pool is present
cs_storage_pool:
zone: zone01
storage_url: rbd://admin:SECRET@ceph-mons.domain/poolname
provider: DefaultPrimary
name: Ceph RBD
scope: zone
hypervisor: KVM
delegate_to: localhost
- name: ensure a cluster scoped storage_pool is disabled
cs_storage_pool:
name: Ceph RBD
zone: zone01
cluster: cluster01
pod: pod01
storage_url: rbd://admin:SECRET@ceph-the-mons.domain/poolname
provider: DefaultPrimary
scope: cluster
allocation_state: disabled
delegate_to: localhost
- name: ensure a cluster scoped storage_pool is in maintenance
cs_storage_pool:
name: Ceph RBD
zone: zone01
cluster: cluster01
pod: pod01
storage_url: rbd://admin:SECRET@ceph-the-mons.domain/poolname
provider: DefaultPrimary
scope: cluster
allocation_state: maintenance
delegate_to: localhost
- name: ensure a storage_pool is absent
cs_storage_pool:
name: Ceph RBD
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the pool.
returned: success
type: str
sample: a3fca65a-7db1-4891-b97c-48806a978a96
created:
description: Date of the pool was created.
returned: success
type: str
sample: 2014-12-01T14:57:57+0100
capacity_iops:
description: IOPS CloudStack can provision from this storage pool
returned: when available
type: int
sample: 60000
zone:
description: The name of the zone.
returned: success
type: str
sample: Zone01
cluster:
description: The name of the cluster.
returned: when scope is cluster
type: str
sample: Cluster01
pod:
description: The name of the pod.
returned: when scope is cluster
type: str
sample: Cluster01
disk_size_allocated:
description: The pool's currently allocated disk space.
returned: success
type: int
sample: 2443517624320
disk_size_total:
description: The total size of the pool.
returned: success
type: int
sample: 3915055693824
disk_size_used:
description: The pool's currently used disk size.
returned: success
type: int
sample: 1040862622180
scope:
description: The scope of the storage pool.
returned: success
type: str
sample: cluster
hypervisor:
description: Hypervisor related to this storage pool.
returned: when available
type: str
sample: KVM
state:
description: The state of the storage pool as returned by the API.
returned: success
type: str
sample: Up
allocation_state:
description: The state of the storage pool.
returned: success
type: str
sample: enabled
path:
description: The storage pool path used in the storage_url.
returned: success
type: str
sample: poolname
overprovision_factor:
description: The overprovision factor of the storage pool.
returned: success
type: str
sample: 2.0
suitable_for_migration:
description: Whether the storage pool is suitable to migrate a volume or not.
returned: success
type: bool
sample: false
storage_capabilities:
description: Capabilities of the storage pool.
returned: success
type: dict
sample: {"VOLUME_SNAPSHOT_QUIESCEVM": "false"}
storage_tags:
description: the tags for the storage pool.
returned: success
type: list
sample: ["perf", "ssd"]
'''
# import cloudstack common
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackStoragePool(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackStoragePool, self).__init__(module)
self.returns = {
'capacityiops': 'capacity_iops',
'podname': 'pod',
'clustername': 'cluster',
'disksizeallocated': 'disk_size_allocated',
'disksizetotal': 'disk_size_total',
'disksizeused': 'disk_size_used',
'scope': 'scope',
'hypervisor': 'hypervisor',
'type': 'type',
'ip_address': 'ipaddress',
'path': 'path',
'overprovisionfactor': 'overprovision_factor',
'storagecapabilities': 'storage_capabilities',
'suitableformigration': 'suitable_for_migration',
}
self.allocation_states = {
# Host state: param state
'Up': 'enabled',
'Disabled': 'disabled',
'Maintenance': 'maintenance',
}
self.storage_pool = None
def _get_common_args(self):
return {
'name': self.module.params.get('name'),
'url': self.module.params.get('storage_url'),
'zoneid': self.get_zone(key='id'),
'provider': self.get_storage_provider(),
'scope': self.module.params.get('scope'),
'hypervisor': self.module.params.get('hypervisor'),
'capacitybytes': self.module.params.get('capacity_bytes'),
'capacityiops': self.module.params.get('capacity_iops'),
}
def _allocation_state_enabled_disabled_changed(self, pool, allocation_state):
if allocation_state in ['enabled', 'disabled']:
for pool_state, param_state in self.allocation_states.items():
if pool_state == pool['state'] and allocation_state != param_state:
return True
return False
def _handle_allocation_state(self, pool, state=None):
allocation_state = state or self.module.params.get('allocation_state')
if not allocation_state:
return pool
if self.allocation_states.get(pool['state']) == allocation_state:
return pool
# Cancel maintenance if target state is enabled/disabled
elif allocation_state in ['enabled', 'disabled']:
pool = self._cancel_maintenance(pool)
pool = self._update_storage_pool(pool=pool, allocation_state=allocation_state)
# Only an enabled host can put in maintenance
elif allocation_state == 'maintenance':
pool = self._update_storage_pool(pool=pool, allocation_state='enabled')
pool = self._enable_maintenance(pool=pool)
return pool
def _create_storage_pool(self):
args = self._get_common_args()
args.update({
'clusterid': self.get_cluster(key='id'),
'podid': self.get_pod(key='id'),
'managed': self.module.params.get('managed'),
})
scope = self.module.params.get('scope')
if scope is None:
args['scope'] = 'cluster' if args['clusterid'] else 'zone'
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('createStoragePool', **args)
return res['storagepool']
def _update_storage_pool(self, pool, allocation_state=None):
args = {
'id': pool['id'],
'capacitybytes': self.module.params.get('capacity_bytes'),
'capacityiops': self.module.params.get('capacity_iops'),
'tags': self.get_storage_tags(),
}
if self.has_changed(args, pool) or self._allocation_state_enabled_disabled_changed(pool, allocation_state):
self.result['changed'] = True
args['enabled'] = allocation_state == 'enabled' if allocation_state in ['enabled', 'disabled'] else None
if not self.module.check_mode:
res = self.query_api('updateStoragePool', **args)
pool = res['storagepool']
return pool
def _enable_maintenance(self, pool):
if pool['state'].lower() != "maintenance":
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('enableStorageMaintenance', id=pool['id'])
pool = self.poll_job(res, 'storagepool')
return pool
def _cancel_maintenance(self, pool):
if pool['state'].lower() == "maintenance":
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('cancelStorageMaintenance', id=pool['id'])
pool = self.poll_job(res, 'storagepool')
return pool
def get_storage_tags(self):
storage_tags = self.module.params.get('storage_tags')
if storage_tags is None:
return None
return ','.join(storage_tags)
def get_storage_pool(self, key=None):
if self.storage_pool is None:
zoneid = self.get_zone(key='id')
clusterid = self.get_cluster(key='id')
podid = self.get_pod(key='id')
args = {
'zoneid': zoneid,
'podid': podid,
'clusterid': clusterid,
'name': self.module.params.get('name'),
}
res = self.query_api('listStoragePools', **args)
if 'storagepool' not in res:
return None
self.storage_pool = res['storagepool'][0]
return self.storage_pool
def present_storage_pool(self):
pool = self.get_storage_pool()
if pool:
pool = self._update_storage_pool(pool=pool)
else:
pool = self._create_storage_pool()
if pool:
pool = self._handle_allocation_state(pool=pool)
return pool
def absent_storage_pool(self):
pool = self.get_storage_pool()
if pool:
self.result['changed'] = True
args = {
'id': pool['id'],
}
if not self.module.check_mode:
# Only a pool in maintenance can be deleted
self._handle_allocation_state(pool=pool, state='maintenance')
self.query_api('deleteStoragePool', **args)
return pool
def get_storage_provider(self, type="primary"):
args = {
'type': type,
}
provider = self.module.params.get('provider')
storage_providers = self.query_api('listStorageProviders', **args)
for sp in storage_providers.get('dataStoreProvider') or []:
if sp['name'].lower() == provider.lower():
return provider
self.fail_json(msg="Storage provider %s not found" % provider)
def get_pod(self, key=None):
pod = self.module.params.get('pod')
if not pod:
return None
args = {
'name': pod,
'zoneid': self.get_zone(key='id'),
}
pods = self.query_api('listPods', **args)
if pods:
return self._get_by_key(key, pods['pod'][0])
self.fail_json(msg="Pod %s not found" % self.module.params.get('pod'))
def get_cluster(self, key=None):
cluster = self.module.params.get('cluster')
if not cluster:
return None
args = {
'name': cluster,
'zoneid': self.get_zone(key='id'),
}
clusters = self.query_api('listClusters', **args)
if clusters:
return self._get_by_key(key, clusters['cluster'][0])
self.fail_json(msg="Cluster %s not found" % cluster)
def get_result(self, pool):
super(AnsibleCloudStackStoragePool, self).get_result(pool)
if pool:
self.result['storage_url'] = "%s://%s/%s" % (pool['type'], pool['ipaddress'], pool['path'])
self.result['scope'] = pool['scope'].lower()
self.result['storage_tags'] = pool['tags'].split(',') if pool.get('tags') else []
self.result['allocation_state'] = self.allocation_states.get(pool['state'])
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
storage_url=dict(),
zone=dict(),
pod=dict(),
cluster=dict(),
scope=dict(choices=['zone', 'cluster']),
hypervisor=dict(),
provider=dict(default='DefaultPrimary'),
capacity_bytes=dict(type='int'),
capacity_iops=dict(type='int'),
managed=dict(type='bool'),
storage_tags=dict(type='list', aliases=['storage_tag']),
allocation_state=dict(choices=['enabled', 'disabled', 'maintenance']),
state=dict(choices=['present', 'absent'], default='present'),
))
required_together = cs_required_together()
required_together.extend([
['pod', 'cluster'],
])
module = AnsibleModule(
argument_spec=argument_spec,
required_together=required_together,
required_if=[
('state', 'present', ['storage_url']),
],
supports_check_mode=True
)
acs_storage_pool = AnsibleCloudStackStoragePool(module)
state = module.params.get('state')
if state in ['absent']:
pool = acs_storage_pool.absent_storage_pool()
else:
pool = acs_storage_pool.present_storage_pool()
result = acs_storage_pool.get_result(pool)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,744 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_template
short_description: Manages templates on Apache CloudStack based clouds.
description:
- Register templates from an URL.
- Create templates from a ROOT volume of a stopped VM or its snapshot.
- Update (since version 2.7), extract and delete templates.
author: René Moser (@resmo)
options:
name:
description:
- Name of the template.
type: str
required: true
url:
description:
- URL of where the template is hosted on I(state=present).
- URL to which the template would be extracted on I(state=extracted).
- Mutually exclusive with I(vm).
type: str
vm:
description:
- VM name the template will be created from its volume or alternatively from a snapshot.
- VM must be in stopped state if created from its volume.
- Mutually exclusive with I(url).
type: str
snapshot:
description:
- Name of the snapshot, created from the VM ROOT volume, the template will be created from.
- I(vm) is required together with this argument.
type: str
os_type:
description:
- OS type that best represents the OS of this template.
type: str
checksum:
description:
- The MD5 checksum value of this template.
- If set, we search by checksum instead of name.
type: str
is_ready:
description:
- "Note: this flag was not implemented and therefore marked as deprecated."
- Deprecated, will be removed in version 2.11.
type: bool
is_public:
description:
- Register the template to be publicly available to all users.
- Only used if I(state) is C(present).
type: bool
is_featured:
description:
- Register the template to be featured.
- Only used if I(state) is C(present).
type: bool
is_dynamically_scalable:
description:
- Register the template having XS/VMware tools installed in order to support dynamic scaling of VM CPU/memory.
- Only used if I(state) is C(present).
type: bool
cross_zones:
description:
- Whether the template should be synced or removed across zones.
- Only used if I(state) is C(present) or C(absent).
default: no
type: bool
mode:
description:
- Mode for the template extraction.
- Only used if I(state=extracted).
type: str
default: http_download
choices: [ http_download, ftp_upload ]
domain:
description:
- Domain the template, snapshot or VM is related to.
type: str
account:
description:
- Account the template, snapshot or VM is related to.
type: str
project:
description:
- Name of the project the template to be registered in.
type: str
zone:
description:
- Name of the zone you wish the template to be registered or deleted from.
- If not specified, first found zone will be used.
type: str
template_filter:
description:
- Name of the filter used to search for the template.
- The filter C(all) was added in 2.7.
type: str
default: self
choices: [ all, featured, self, selfexecutable, sharedexecutable, executable, community ]
template_find_options:
description:
- Options to find a template uniquely.
- More than one allowed.
type: list
choices: [ display_text, checksum, cross_zones ]
aliases: [ template_find_option ]
default: []
hypervisor:
description:
- Name the hypervisor to be used for creating the new template.
- Relevant when using I(state=present).
- Possible values are C(KVM), C(VMware), C(BareMetal), C(XenServer), C(LXC), C(HyperV), C(UCS), C(OVM), C(Simulator).
type: str
requires_hvm:
description:
- Whether the template requires HVM or not.
- Only considered while creating the template.
type: bool
password_enabled:
description:
- Enable template password reset support.
type: bool
template_tag:
description:
- The tag for this template.
type: str
sshkey_enabled:
description:
- True if the template supports the sshkey upload feature.
- Only considered if I(url) is used (API limitation).
type: bool
is_routing:
description:
- Sets the template type to routing, i.e. if template is used to deploy routers.
- Only considered if I(url) is used.
type: bool
format:
description:
- The format for the template.
- Only considered if I(state=present).
type: str
choices: [ QCOW2, RAW, VHD, OVA ]
is_extractable:
description:
- Allows the template or its derivatives to be extractable.
type: bool
details:
description:
- Template details in key/value pairs.
type: str
bits:
description:
- 32 or 64 bits support.
type: int
default: 64
choices: [ 32, 64 ]
display_text:
description:
- Display text of the template.
type: str
state:
description:
- State of the template.
type: str
default: present
choices: [ present, absent, extracted ]
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- "To delete all tags, set a empty list e.g. I(tags: [])."
type: list
aliases: [ tag ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: register a systemvm template
cs_template:
name: systemvm-vmware-4.5
url: "http://packages.shapeblue.com/systemvmtemplate/4.5/systemvm64template-4.5-vmware.ova"
hypervisor: VMware
format: OVA
cross_zones: yes
os_type: Debian GNU/Linux 7(64-bit)
delegate_to: localhost
- name: Create a template from a stopped virtual machine's volume
cs_template:
name: Debian 9 (64-bit) 20GB ({{ ansible_date_time.date }})
vm: debian-9-base-vm
os_type: Debian GNU/Linux 9 (64-bit)
zone: tokio-ix
password_enabled: yes
is_public: yes
delegate_to: localhost
# Note: Use template_find_option(s) when a template name is not unique
- name: Create a template from a stopped virtual machine's volume
cs_template:
name: Debian 9 (64-bit)
display_text: Debian 9 (64-bit) 20GB ({{ ansible_date_time.date }})
template_find_option: display_text
vm: debian-9-base-vm
os_type: Debian GNU/Linux 9 (64-bit)
zone: tokio-ix
password_enabled: yes
is_public: yes
delegate_to: localhost
- name: create a template from a virtual machine's root volume snapshot
cs_template:
name: Debian 9 (64-bit) Snapshot ROOT-233_2015061509114
snapshot: ROOT-233_2015061509114
os_type: Debian GNU/Linux 9 (64-bit)
zone: tokio-ix
password_enabled: yes
is_public: yes
delegate_to: localhost
- name: Remove a template
cs_template:
name: systemvm-4.2
cross_zones: yes
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the template or extracted object.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: Name of the template or extracted object.
returned: success
type: str
sample: Debian 7 64-bit
display_text:
description: Display text of the template.
returned: if available
type: str
sample: Debian 7.7 64-bit minimal 2015-03-19
checksum:
description: MD5 checksum of the template.
returned: if available
type: str
sample: 0b31bccccb048d20b551f70830bb7ad0
status:
description: Status of the template or extracted object.
returned: success
type: str
sample: Download Complete
is_ready:
description: True if the template is ready to be deployed from.
returned: if available
type: bool
sample: true
is_public:
description: True if the template is public.
returned: if available
type: bool
sample: true
is_featured:
description: True if the template is featured.
returned: if available
type: bool
sample: true
is_extractable:
description: True if the template is extractable.
returned: if available
type: bool
sample: true
format:
description: Format of the template.
returned: if available
type: str
sample: OVA
os_type:
description: Type of the OS.
returned: if available
type: str
sample: CentOS 6.5 (64-bit)
password_enabled:
description: True if the reset password feature is enabled, false otherwise.
returned: if available
type: bool
sample: false
sshkey_enabled:
description: true if template is sshkey enabled, false otherwise.
returned: if available
type: bool
sample: false
cross_zones:
description: true if the template is managed across all zones, false otherwise.
returned: if available
type: bool
sample: false
template_type:
description: Type of the template.
returned: if available
type: str
sample: USER
created:
description: Date of registering.
returned: success
type: str
sample: 2015-03-29T14:57:06+0200
template_tag:
description: Template tag related to this template.
returned: if available
type: str
sample: special
hypervisor:
description: Hypervisor related to this template.
returned: if available
type: str
sample: VMware
mode:
description: Mode of extraction
returned: on state=extracted
type: str
sample: http_download
state:
description: State of the extracted template
returned: on state=extracted
type: str
sample: DOWNLOAD_URL_CREATED
url:
description: Url to which the template is extracted to
returned: on state=extracted
type: str
sample: "http://1.2.3.4/userdata/eb307f13-4aca-45e8-b157-a414a14e6b04.ova"
tags:
description: List of resource tags associated with the template.
returned: if available
type: list
sample: '[ { "key": "foo", "value": "bar" } ]'
zone:
description: Name of zone the template is registered in.
returned: success
type: str
sample: zuerich
domain:
description: Domain the template is related to.
returned: success
type: str
sample: example domain
account:
description: Account the template is related to.
returned: success
type: str
sample: example account
project:
description: Name of project the template is related to.
returned: success
type: str
sample: Production
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackTemplate(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackTemplate, self).__init__(module)
self.returns = {
'checksum': 'checksum',
'status': 'status',
'isready': 'is_ready',
'templatetag': 'template_tag',
'sshkeyenabled': 'sshkey_enabled',
'passwordenabled': 'password_enabled',
'templatetype': 'template_type',
'ostypename': 'os_type',
'crossZones': 'cross_zones',
'format': 'format',
'hypervisor': 'hypervisor',
'url': 'url',
'extractMode': 'mode',
'state': 'state',
}
def _get_args(self):
args = {
'name': self.module.params.get('name'),
'displaytext': self.get_or_fallback('display_text', 'name'),
'bits': self.module.params.get('bits'),
'isdynamicallyscalable': self.module.params.get('is_dynamically_scalable'),
'isextractable': self.module.params.get('is_extractable'),
'isfeatured': self.module.params.get('is_featured'),
'ispublic': self.module.params.get('is_public'),
'passwordenabled': self.module.params.get('password_enabled'),
'requireshvm': self.module.params.get('requires_hvm'),
'templatetag': self.module.params.get('template_tag'),
'ostypeid': self.get_os_type(key='id'),
}
if not args['ostypeid']:
self.module.fail_json(msg="Missing required arguments: os_type")
return args
def get_root_volume(self, key=None):
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'virtualmachineid': self.get_vm(key='id'),
'type': "ROOT"
}
volumes = self.query_api('listVolumes', **args)
if volumes:
return self._get_by_key(key, volumes['volume'][0])
self.module.fail_json(msg="Root volume for '%s' not found" % self.get_vm('name'))
def get_snapshot(self, key=None):
snapshot = self.module.params.get('snapshot')
if not snapshot:
return None
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'volumeid': self.get_root_volume('id'),
'fetch_list': True,
}
snapshots = self.query_api('listSnapshots', **args)
if snapshots:
for s in snapshots:
if snapshot in [s['name'], s['id']]:
return self._get_by_key(key, s)
self.module.fail_json(msg="Snapshot '%s' not found" % snapshot)
def present_template(self):
template = self.get_template()
if template:
template = self.update_template(template)
elif self.module.params.get('url'):
template = self.register_template()
elif self.module.params.get('vm'):
template = self.create_template()
else:
self.fail_json(msg="one of the following is required on state=present: url, vm")
return template
def create_template(self):
template = None
self.result['changed'] = True
args = self._get_args()
snapshot_id = self.get_snapshot(key='id')
if snapshot_id:
args['snapshotid'] = snapshot_id
else:
args['volumeid'] = self.get_root_volume('id')
if not self.module.check_mode:
template = self.query_api('createTemplate', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
template = self.poll_job(template, 'template')
if template:
template = self.ensure_tags(resource=template, resource_type='Template')
return template
def register_template(self):
required_params = [
'format',
'url',
'hypervisor',
]
self.module.fail_on_missing_params(required_params=required_params)
template = None
self.result['changed'] = True
args = self._get_args()
args.update({
'url': self.module.params.get('url'),
'format': self.module.params.get('format'),
'checksum': self.module.params.get('checksum'),
'isextractable': self.module.params.get('is_extractable'),
'isrouting': self.module.params.get('is_routing'),
'sshkeyenabled': self.module.params.get('sshkey_enabled'),
'hypervisor': self.get_hypervisor(),
'domainid': self.get_domain(key='id'),
'account': self.get_account(key='name'),
'projectid': self.get_project(key='id'),
})
if not self.module.params.get('cross_zones'):
args['zoneid'] = self.get_zone(key='id')
else:
args['zoneid'] = -1
if not self.module.check_mode:
self.query_api('registerTemplate', **args)
template = self.get_template()
return template
def update_template(self, template):
args = {
'id': template['id'],
'displaytext': self.get_or_fallback('display_text', 'name'),
'format': self.module.params.get('format'),
'isdynamicallyscalable': self.module.params.get('is_dynamically_scalable'),
'isrouting': self.module.params.get('is_routing'),
'ostypeid': self.get_os_type(key='id'),
'passwordenabled': self.module.params.get('password_enabled'),
}
if self.has_changed(args, template):
self.result['changed'] = True
if not self.module.check_mode:
self.query_api('updateTemplate', **args)
template = self.get_template()
args = {
'id': template['id'],
'isextractable': self.module.params.get('is_extractable'),
'isfeatured': self.module.params.get('is_featured'),
'ispublic': self.module.params.get('is_public'),
}
if self.has_changed(args, template):
self.result['changed'] = True
if not self.module.check_mode:
self.query_api('updateTemplatePermissions', **args)
# Refresh
template = self.get_template()
if template:
template = self.ensure_tags(resource=template, resource_type='Template')
return template
def _is_find_option(self, param_name):
return param_name in self.module.params.get('template_find_options')
def _find_option_match(self, template, param_name, internal_name=None):
if not internal_name:
internal_name = param_name
if param_name in self.module.params.get('template_find_options'):
param_value = self.module.params.get(param_name)
if not param_value:
self.fail_json(msg="The param template_find_options has %s but param was not provided." % param_name)
if template[internal_name] == param_value:
return True
return False
def get_template(self):
args = {
'name': self.module.params.get('name'),
'templatefilter': self.module.params.get('template_filter'),
'domainid': self.get_domain(key='id'),
'account': self.get_account(key='name'),
'projectid': self.get_project(key='id')
}
cross_zones = self.module.params.get('cross_zones')
if not cross_zones:
args['zoneid'] = self.get_zone(key='id')
template_found = None
templates = self.query_api('listTemplates', **args)
if templates:
for tmpl in templates['template']:
if self._is_find_option('cross_zones') and not self._find_option_match(
template=tmpl,
param_name='cross_zones',
internal_name='crossZones'):
continue
if self._is_find_option('checksum') and not self._find_option_match(
template=tmpl,
param_name='checksum'):
continue
if self._is_find_option('display_text') and not self._find_option_match(
template=tmpl,
param_name='display_text',
internal_name='displaytext'):
continue
if not template_found:
template_found = tmpl
# A cross zones template has one entry per zone but the same id
elif tmpl['id'] == template_found['id']:
continue
else:
self.fail_json(msg="Multiple templates found matching provided params. Please use template_find_options.")
return template_found
def extract_template(self):
template = self.get_template()
if not template:
self.module.fail_json(msg="Failed: template not found")
args = {
'id': template['id'],
'url': self.module.params.get('url'),
'mode': self.module.params.get('mode'),
'zoneid': self.get_zone(key='id')
}
self.result['changed'] = True
if not self.module.check_mode:
template = self.query_api('extractTemplate', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
template = self.poll_job(template, 'template')
return template
def remove_template(self):
template = self.get_template()
if template:
self.result['changed'] = True
args = {
'id': template['id']
}
if not self.module.params.get('cross_zones'):
args['zoneid'] = self.get_zone(key='id')
if not self.module.check_mode:
res = self.query_api('deleteTemplate', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
res = self.poll_job(res, 'template')
return template
def get_result(self, template):
super(AnsibleCloudStackTemplate, self).get_result(template)
if template:
if 'isextractable' in template:
self.result['is_extractable'] = True if template['isextractable'] else False
if 'isfeatured' in template:
self.result['is_featured'] = True if template['isfeatured'] else False
if 'ispublic' in template:
self.result['is_public'] = True if template['ispublic'] else False
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
display_text=dict(),
url=dict(),
vm=dict(),
snapshot=dict(),
os_type=dict(),
is_ready=dict(type='bool', removed_in_version='2.11'),
is_public=dict(type='bool'),
is_featured=dict(type='bool'),
is_dynamically_scalable=dict(type='bool'),
is_extractable=dict(type='bool'),
is_routing=dict(type='bool'),
checksum=dict(),
template_filter=dict(default='self', choices=['all', 'featured', 'self', 'selfexecutable', 'sharedexecutable', 'executable', 'community']),
template_find_options=dict(type='list', choices=['display_text', 'checksum', 'cross_zones'], aliases=['template_find_option'], default=[]),
hypervisor=dict(),
requires_hvm=dict(type='bool'),
password_enabled=dict(type='bool'),
template_tag=dict(),
sshkey_enabled=dict(type='bool'),
format=dict(choices=['QCOW2', 'RAW', 'VHD', 'OVA']),
details=dict(),
bits=dict(type='int', choices=[32, 64], default=64),
state=dict(choices=['present', 'absent', 'extracted'], default='present'),
cross_zones=dict(type='bool', default=False),
mode=dict(choices=['http_download', 'ftp_upload'], default='http_download'),
zone=dict(),
domain=dict(),
account=dict(),
project=dict(),
poll_async=dict(type='bool', default=True),
tags=dict(type='list', aliases=['tag']),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
mutually_exclusive=(
['url', 'vm'],
['zone', 'cross_zones'],
),
supports_check_mode=True
)
acs_tpl = AnsibleCloudStackTemplate(module)
state = module.params.get('state')
if state == 'absent':
tpl = acs_tpl.remove_template()
elif state == 'extracted':
tpl = acs_tpl.extract_template()
else:
tpl = acs_tpl.present_template()
result = acs_tpl.get_result(tpl)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,327 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2019, Patryk D. Cichy <patryk.d.cichy@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_traffic_type
short_description: Manages traffic types on CloudStack Physical Networks
description:
- Add, remove, update Traffic Types associated with CloudStack Physical Networks.
extends_documentation_fragment:
- community.general.cloudstack
author:
- Patryk Cichy (@PatTheSilent)
options:
physical_network:
description:
- the name of the Physical Network
required: true
type: str
zone:
description:
- Name of the zone with the physical network.
- Default zone will be used if this is empty.
type: str
traffic_type:
description:
- the trafficType to be added to the physical network.
required: true
choices: [Management, Guest, Public, Storage]
type: str
state:
description:
- State of the traffic type
choices: [present, absent]
default: present
type: str
hyperv_networklabel:
description:
- The network name label of the physical device dedicated to this traffic on a HyperV host.
type: str
isolation_method:
description:
- Use if the physical network has multiple isolation types and traffic type is public.
choices: [vlan, vxlan]
type: str
kvm_networklabel:
description:
- The network name label of the physical device dedicated to this traffic on a KVM host.
type: str
ovm3_networklabel:
description:
- The network name of the physical device dedicated to this traffic on an OVM3 host.
type: str
vlan:
description:
- The VLAN id to be used for Management traffic by VMware host.
type: str
vmware_networklabel:
description:
- The network name label of the physical device dedicated to this traffic on a VMware host.
type: str
xen_networklabel:
description:
- The network name label of the physical device dedicated to this traffic on a XenServer host.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
'''
EXAMPLES = '''
- name: add a traffic type
cs_traffic_type:
physical_network: public-network
traffic_type: Guest
zone: test-zone
delegate_to: localhost
- name: update traffic type
cs_traffic_type:
physical_network: public-network
traffic_type: Guest
kvm_networklabel: cloudbr0
zone: test-zone
delegate_to: localhost
- name: remove traffic type
cs_traffic_type:
physical_network: public-network
traffic_type: Public
state: absent
zone: test-zone
delegate_to: localhost
'''
RETURN = '''
---
id:
description: ID of the network provider
returned: success
type: str
sample: 659c1840-9374-440d-a412-55ca360c9d3c
traffic_type:
description: the trafficType that was added to the physical network
returned: success
type: str
sample: Public
hyperv_networklabel:
description: The network name label of the physical device dedicated to this traffic on a HyperV host
returned: success
type: str
sample: HyperV Internal Switch
kvm_networklabel:
description: The network name label of the physical device dedicated to this traffic on a KVM host
returned: success
type: str
sample: cloudbr0
ovm3_networklabel:
description: The network name of the physical device dedicated to this traffic on an OVM3 host
returned: success
type: str
sample: cloudbr0
physical_network:
description: the physical network this belongs to
returned: success
type: str
sample: 28ed70b7-9a1f-41bf-94c3-53a9f22da8b6
vmware_networklabel:
description: The network name label of the physical device dedicated to this traffic on a VMware host
returned: success
type: str
sample: Management Network
xen_networklabel:
description: The network name label of the physical device dedicated to this traffic on a XenServer host
returned: success
type: str
sample: xenbr0
zone:
description: Name of zone the physical network is in.
returned: success
type: str
sample: ch-gva-2
'''
from ansible_collections.community.general.plugins.module_utils.cloudstack import AnsibleCloudStack, cs_argument_spec, cs_required_together
from ansible.module_utils.basic import AnsibleModule
class AnsibleCloudStackTrafficType(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackTrafficType, self).__init__(module)
self.returns = {
'traffictype': 'traffic_type',
'hypervnetworklabel': 'hyperv_networklabel',
'kvmnetworklabel': 'kvm_networklabel',
'ovm3networklabel': 'ovm3_networklabel',
'physicalnetworkid': 'physical_network',
'vmwarenetworklabel': 'vmware_networklabel',
'xennetworklabel': 'xen_networklabel'
}
self.traffic_type = None
def _get_label_args(self):
label_args = dict()
if self.module.params.get('hyperv_networklabel'):
label_args.update(dict(hypervnetworklabel=self.module.params.get('hyperv_networklabel')))
if self.module.params.get('kvm_networklabel'):
label_args.update(dict(kvmnetworklabel=self.module.params.get('kvm_networklabel')))
if self.module.params.get('ovm3_networklabel'):
label_args.update(dict(ovm3networklabel=self.module.params.get('ovm3_networklabel')))
if self.module.params.get('vmware_networklabel'):
label_args.update(dict(vmwarenetworklabel=self.module.params.get('vmware_networklabel')))
return label_args
def _get_additional_args(self):
additional_args = dict()
if self.module.params.get('isolation_method'):
additional_args.update(dict(isolationmethod=self.module.params.get('isolation_method')))
if self.module.params.get('vlan'):
additional_args.update(dict(vlan=self.module.params.get('vlan')))
additional_args.update(self._get_label_args())
return additional_args
def get_traffic_types(self):
args = {
'physicalnetworkid': self.get_physical_network(key='id')
}
traffic_types = self.query_api('listTrafficTypes', **args)
return traffic_types
def get_traffic_type(self):
if self.traffic_type:
return self.traffic_type
traffic_type = self.module.params.get('traffic_type')
traffic_types = self.get_traffic_types()
if traffic_types:
for t_type in traffic_types['traffictype']:
if traffic_type.lower() in [t_type['traffictype'].lower(), t_type['id']]:
self.traffic_type = t_type
break
return self.traffic_type
def present_traffic_type(self):
traffic_type = self.get_traffic_type()
if traffic_type:
self.traffic_type = self.update_traffic_type()
else:
self.result['changed'] = True
self.traffic_type = self.add_traffic_type()
return self.traffic_type
def add_traffic_type(self):
traffic_type = self.module.params.get('traffic_type')
args = {
'physicalnetworkid': self.get_physical_network(key='id'),
'traffictype': traffic_type
}
args.update(self._get_additional_args())
if not self.module.check_mode:
resource = self.query_api('addTrafficType', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.traffic_type = self.poll_job(resource, 'traffictype')
return self.traffic_type
def absent_traffic_type(self):
traffic_type = self.get_traffic_type()
if traffic_type:
args = {
'id': traffic_type['id']
}
self.result['changed'] = True
if not self.module.check_mode:
resource = self.query_api('deleteTrafficType', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(resource, 'traffictype')
return traffic_type
def update_traffic_type(self):
traffic_type = self.get_traffic_type()
args = {
'id': traffic_type['id']
}
args.update(self._get_label_args())
if self.has_changed(args, traffic_type):
self.result['changed'] = True
if not self.module.check_mode:
resource = self.query_api('updateTrafficType', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.traffic_type = self.poll_job(resource, 'traffictype')
return self.traffic_type
def setup_module_object():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
physical_network=dict(required=True),
zone=dict(),
state=dict(choices=['present', 'absent'], default='present'),
traffic_type=dict(required=True, choices=['Management', 'Guest', 'Public', 'Storage']),
hyperv_networklabel=dict(),
isolation_method=dict(choices=['vlan', 'vxlan']),
kvm_networklabel=dict(),
ovm3_networklabel=dict(),
vlan=dict(),
vmware_networklabel=dict(),
xen_networklabel=dict(),
poll_async=dict(type='bool', default=True)
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
return module
def execute_module(module):
actt = AnsibleCloudStackTrafficType(module)
state = module.params.get('state')
if state in ['present']:
result = actt.present_traffic_type()
else:
result = actt.absent_traffic_type()
return actt.get_result(result)
def main():
module = setup_module_object()
result = execute_module(module)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,445 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_user
short_description: Manages users on Apache CloudStack based clouds.
description:
- Create, update, disable, lock, enable and remove users.
author: René Moser (@resmo)
options:
username:
description:
- Username of the user.
type: str
required: true
account:
description:
- Account the user will be created under.
- Required on I(state=present).
type: str
password:
description:
- Password of the user to be created.
- Required on I(state=present).
- Only considered on creation and will not be updated if user exists.
type: str
first_name:
description:
- First name of the user.
- Required on I(state=present).
type: str
last_name:
description:
- Last name of the user.
- Required on I(state=present).
type: str
email:
description:
- Email of the user.
- Required on I(state=present).
type: str
timezone:
description:
- Timezone of the user.
type: str
keys_registered:
description:
- If API keys of the user should be generated.
- "Note: Keys can not be removed by the API again."
type: bool
default: no
domain:
description:
- Domain the user is related to.
type: str
default: ROOT
state:
description:
- State of the user.
- C(unlocked) is an alias for C(enabled).
type: str
default: present
choices: [ present, absent, enabled, disabled, locked, unlocked ]
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create an user in domain 'CUSTOMERS'
cs_user:
account: developers
username: johndoe
password: S3Cur3
last_name: Doe
first_name: John
email: john.doe@example.com
domain: CUSTOMERS
delegate_to: localhost
- name: Lock an existing user in domain 'CUSTOMERS'
cs_user:
username: johndoe
domain: CUSTOMERS
state: locked
delegate_to: localhost
- name: Disable an existing user in domain 'CUSTOMERS'
cs_user:
username: johndoe
domain: CUSTOMERS
state: disabled
delegate_to: localhost
- name: Enable/unlock an existing user in domain 'CUSTOMERS'
cs_user:
username: johndoe
domain: CUSTOMERS
state: enabled
delegate_to: localhost
- name: Remove an user in domain 'CUSTOMERS'
cs_user:
name: customer_xy
domain: CUSTOMERS
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the user.
returned: success
type: str
sample: 87b1e0ce-4e01-11e4-bb66-0050569e64b8
username:
description: Username of the user.
returned: success
type: str
sample: johndoe
fist_name:
description: First name of the user.
returned: success
type: str
sample: John
last_name:
description: Last name of the user.
returned: success
type: str
sample: Doe
email:
description: Emailof the user.
returned: success
type: str
sample: john.doe@example.com
user_api_key:
description: API key of the user.
returned: success
type: str
sample: JLhcg8VWi8DoFqL2sSLZMXmGojcLnFrOBTipvBHJjySODcV4mCOo29W2duzPv5cALaZnXj5QxDx3xQfaQt3DKg
user_api_secret:
description: API secret of the user.
returned: success
type: str
sample: FUELo3LB9fa1UopjTLPdqLv_6OXQMJZv9g9N4B_Ao3HFz8d6IGFCV9MbPFNM8mwz00wbMevja1DoUNDvI8C9-g
account:
description: Account name of the user.
returned: success
type: str
sample: developers
account_type:
description: Type of the account.
returned: success
type: str
sample: user
timezone:
description: Timezone of the user.
returned: success
type: str
sample: enabled
created:
description: Date the user was created.
returned: success
type: str
sample: Doe
state:
description: State of the user.
returned: success
type: str
sample: enabled
domain:
description: Domain the user is related.
returned: success
type: str
sample: ROOT
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackUser(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackUser, self).__init__(module)
self.returns = {
'username': 'username',
'firstname': 'first_name',
'lastname': 'last_name',
'email': 'email',
'secretkey': 'user_api_secret',
'apikey': 'user_api_key',
'timezone': 'timezone',
}
self.account_types = {
'user': 0,
'root_admin': 1,
'domain_admin': 2,
}
self.user = None
def get_account_type(self):
account_type = self.module.params.get('account_type')
return self.account_types[account_type]
def get_user(self):
if not self.user:
args = {
'domainid': self.get_domain('id'),
'fetch_list': True,
}
users = self.query_api('listUsers', **args)
if users:
user_name = self.module.params.get('username')
for u in users:
if user_name.lower() == u['username'].lower():
self.user = u
break
return self.user
def enable_user(self):
user = self.get_user()
if not user:
user = self.present_user()
if user['state'].lower() != 'enabled':
self.result['changed'] = True
args = {
'id': user['id'],
}
if not self.module.check_mode:
res = self.query_api('enableUser', **args)
user = res['user']
return user
def lock_user(self):
user = self.get_user()
if not user:
user = self.present_user()
# we need to enable the user to lock it.
if user['state'].lower() == 'disabled':
user = self.enable_user()
if user['state'].lower() != 'locked':
self.result['changed'] = True
args = {
'id': user['id'],
}
if not self.module.check_mode:
res = self.query_api('lockUser', **args)
user = res['user']
return user
def disable_user(self):
user = self.get_user()
if not user:
user = self.present_user()
if user['state'].lower() != 'disabled':
self.result['changed'] = True
args = {
'id': user['id'],
}
if not self.module.check_mode:
user = self.query_api('disableUser', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
user = self.poll_job(user, 'user')
return user
def present_user(self):
required_params = [
'account',
'email',
'password',
'first_name',
'last_name',
]
self.module.fail_on_missing_params(required_params=required_params)
user = self.get_user()
if user:
user = self._update_user(user)
else:
user = self._create_user(user)
return user
def _get_common_args(self):
return {
'firstname': self.module.params.get('first_name'),
'lastname': self.module.params.get('last_name'),
'email': self.module.params.get('email'),
'timezone': self.module.params.get('timezone'),
}
def _create_user(self, user):
self.result['changed'] = True
args = self._get_common_args()
args.update({
'account': self.get_account(key='name'),
'domainid': self.get_domain('id'),
'username': self.module.params.get('username'),
'password': self.module.params.get('password'),
})
if not self.module.check_mode:
res = self.query_api('createUser', **args)
user = res['user']
# register user api keys
if self.module.params.get('keys_registered'):
res = self.query_api('registerUserKeys', id=user['id'])
user.update(res['userkeys'])
return user
def _update_user(self, user):
args = self._get_common_args()
args.update({
'id': user['id'],
})
if self.has_changed(args, user):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateUser', **args)
user = res['user']
# register user api keys
if 'apikey' not in user and self.module.params.get('keys_registered'):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('registerUserKeys', id=user['id'])
user.update(res['userkeys'])
return user
def absent_user(self):
user = self.get_user()
if user:
self.result['changed'] = True
if not self.module.check_mode:
self.query_api('deleteUser', id=user['id'])
return user
def get_result(self, user):
super(AnsibleCloudStackUser, self).get_result(user)
if user:
if 'accounttype' in user:
for key, value in self.account_types.items():
if value == user['accounttype']:
self.result['account_type'] = key
break
# secretkey has been removed since CloudStack 4.10 from listUsers API
if self.module.params.get('keys_registered') and 'apikey' in user and 'secretkey' not in user:
user_keys = self.query_api('getUserKeys', id=user['id'])
if user_keys:
self.result['user_api_secret'] = user_keys['userkeys'].get('secretkey')
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
username=dict(required=True),
account=dict(),
state=dict(choices=['present', 'absent', 'enabled', 'disabled', 'locked', 'unlocked'], default='present'),
domain=dict(default='ROOT'),
email=dict(),
first_name=dict(),
last_name=dict(),
password=dict(no_log=True),
timezone=dict(),
keys_registered=dict(type='bool', default=False),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_acc = AnsibleCloudStackUser(module)
state = module.params.get('state')
if state == 'absent':
user = acs_acc.absent_user()
elif state in ['enabled', 'unlocked']:
user = acs_acc.enable_user()
elif state == 'disabled':
user = acs_acc.disable_user()
elif state == 'locked':
user = acs_acc.lock_user()
else:
user = acs_acc.present_user()
result = acs_acc.get_result(user)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,388 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2018, David Passante <@dpassante>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_vlan_ip_range
short_description: Manages VLAN IP ranges on Apache CloudStack based clouds.
description:
- Create and delete VLAN IP range.
author: David Passante (@dpassante)
options:
network:
description:
- The network name or id.
- Required if I(for_virtual_network) and I(physical_network) are not set.
type: str
physical_network:
description:
- The physical network name or id.
type: str
start_ip:
description:
- The beginning IPv4 address in the VLAN IP range.
- Only considered on create.
type: str
required: true
end_ip:
description:
- The ending IPv4 address in the VLAN IP range.
- If not specified, value of I(start_ip) is used.
- Only considered on create.
type: str
gateway:
description:
- The gateway of the VLAN IP range.
- Required if I(state=present).
type: str
netmask:
description:
- The netmask of the VLAN IP range.
- Required if I(state=present).
type: str
start_ipv6:
description:
- The beginning IPv6 address in the IPv6 network range.
- Only considered on create.
type: str
end_ipv6:
description:
- The ending IPv6 address in the IPv6 network range.
- If not specified, value of I(start_ipv6) is used.
- Only considered on create.
type: str
gateway_ipv6:
description:
- The gateway of the IPv6 network.
- Only considered on create.
type: str
cidr_ipv6:
description:
- The CIDR of IPv6 network, must be at least /64.
type: str
vlan:
description:
- The ID or VID of the network.
- If not specified, will be defaulted to the vlan of the network.
type: str
state:
description:
- State of the network ip range.
type: str
default: present
choices: [ present, absent ]
zone:
description:
- The Zone ID of the VLAN IP range.
- If not set, default zone is used.
type: str
domain:
description:
- Domain of the account owning the VLAN.
type: str
account:
description:
- Account who owns the VLAN.
- Mutually exclusive with I(project).
type: str
project:
description:
- Project who owns the VLAN.
- Mutually exclusive with I(account).
type: str
for_virtual_network:
description:
- C(yes) if VLAN is of Virtual type, C(no) if Direct.
- If set to C(yes) but neither I(physical_network) or I(network) is set CloudStack will try to add the
VLAN range to the Physical Network with a Public traffic type.
type: bool
default: no
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: create a VLAN IP range for network test
cs_vlan_ip_range:
network: test
vlan: 98
start_ip: 10.2.4.10
end_ip: 10.2.4.100
gateway: 10.2.4.1
netmask: 255.255.255.0
zone: zone-02
delegate_to: localhost
- name: remove a VLAN IP range for network test
cs_vlan_ip_range:
state: absent
network: test
start_ip: 10.2.4.10
end_ip: 10.2.4.100
zone: zone-02
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the VLAN IP range.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
network:
description: The network of vlan range
returned: if available
type: str
sample: test
vlan:
description: The ID or VID of the VLAN.
returned: success
type: str
sample: vlan://98
gateway:
description: IPv4 gateway.
returned: success
type: str
sample: 10.2.4.1
netmask:
description: IPv4 netmask.
returned: success
type: str
sample: 255.255.255.0
gateway_ipv6:
description: IPv6 gateway.
returned: if available
type: str
sample: 2001:db8::1
cidr_ipv6:
description: The CIDR of IPv6 network.
returned: if available
type: str
sample: 2001:db8::/64
zone:
description: Name of zone.
returned: success
type: str
sample: zone-02
domain:
description: Domain name of the VLAN IP range.
returned: success
type: str
sample: ROOT
account:
description: Account who owns the network.
returned: if available
type: str
sample: example account
project:
description: Project who owns the network.
returned: if available
type: str
sample: example project
for_systemvms:
description: Whether VLAN IP range is dedicated to system vms or not.
returned: success
type: bool
sample: false
for_virtual_network:
description: Whether VLAN IP range is of Virtual type or not.
returned: success
type: bool
sample: false
physical_network:
description: The physical network VLAN IP range belongs to.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
start_ip:
description: The start ip of the VLAN IP range.
returned: success
type: str
sample: 10.2.4.10
end_ip:
description: The end ip of the VLAN IP range.
returned: success
type: str
sample: 10.2.4.100
start_ipv6:
description: The start ipv6 of the VLAN IP range.
returned: if available
type: str
sample: 2001:db8::10
end_ipv6:
description: The end ipv6 of the VLAN IP range.
returned: if available
type: str
sample: 2001:db8::50
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackVlanIpRange(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackVlanIpRange, self).__init__(module)
self.returns = {
'startip': 'start_ip',
'endip': 'end_ip',
'physicalnetworkid': 'physical_network',
'vlan': 'vlan',
'forsystemvms': 'for_systemvms',
'forvirtualnetwork': 'for_virtual_network',
'gateway': 'gateway',
'netmask': 'netmask',
'ip6gateway': 'gateway_ipv6',
'ip6cidr': 'cidr_ipv6',
'startipv6': 'start_ipv6',
'endipv6': 'end_ipv6',
}
self.ip_range = None
def get_vlan_ip_range(self):
if not self.ip_range:
args = {
'zoneid': self.get_zone(key='id'),
'projectid': self.get_project(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'networkid': self.get_network(key='id'),
}
res = self.query_api('listVlanIpRanges', **args)
if res:
ip_range_list = res['vlaniprange']
params = {
'startip': self.module.params.get('start_ip'),
'endip': self.get_or_fallback('end_ip', 'start_ip'),
}
for ipr in ip_range_list:
if params['startip'] == ipr['startip'] and params['endip'] == ipr['endip']:
self.ip_range = ipr
break
return self.ip_range
def present_vlan_ip_range(self):
ip_range = self.get_vlan_ip_range()
if not ip_range:
ip_range = self.create_vlan_ip_range()
return ip_range
def create_vlan_ip_range(self):
self.result['changed'] = True
vlan = self.module.params.get('vlan')
args = {
'zoneid': self.get_zone(key='id'),
'projectid': self.get_project(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'startip': self.module.params.get('start_ip'),
'endip': self.get_or_fallback('end_ip', 'start_ip'),
'netmask': self.module.params.get('netmask'),
'gateway': self.module.params.get('gateway'),
'startipv6': self.module.params.get('start_ipv6'),
'endipv6': self.get_or_fallback('end_ipv6', 'start_ipv6'),
'ip6gateway': self.module.params.get('gateway_ipv6'),
'ip6cidr': self.module.params.get('cidr_ipv6'),
'vlan': self.get_network(key='vlan') if not vlan else vlan,
'networkid': self.get_network(key='id'),
'forvirtualnetwork': self.module.params.get('for_virtual_network'),
}
if self.module.params.get('physical_network'):
args['physicalnetworkid'] = self.get_physical_network(key='id')
if not self.module.check_mode:
res = self.query_api('createVlanIpRange', **args)
self.ip_range = res['vlan']
return self.ip_range
def absent_vlan_ip_range(self):
ip_range = self.get_vlan_ip_range()
if ip_range:
self.result['changed'] = True
args = {
'id': ip_range['id'],
}
if not self.module.check_mode:
self.query_api('deleteVlanIpRange', **args)
return ip_range
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
network=dict(type='str'),
physical_network=dict(type='str'),
zone=dict(type='str'),
start_ip=dict(type='str', required=True),
end_ip=dict(type='str'),
gateway=dict(type='str'),
netmask=dict(type='str'),
start_ipv6=dict(type='str'),
end_ipv6=dict(type='str'),
gateway_ipv6=dict(type='str'),
cidr_ipv6=dict(type='str'),
vlan=dict(type='str'),
state=dict(choices=['present', 'absent'], default='present'),
domain=dict(type='str'),
account=dict(type='str'),
project=dict(type='str'),
for_virtual_network=dict(type='bool', default=False),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
mutually_exclusive=(
['account', 'project'],
),
required_if=(("state", "present", ("gateway", "netmask")),),
supports_check_mode=True,
)
acs_vlan_ip_range = AnsibleCloudStackVlanIpRange(module)
state = module.params.get('state')
if state == 'absent':
ipr = acs_vlan_ip_range.absent_vlan_ip_range()
else:
ipr = acs_vlan_ip_range.present_vlan_ip_range()
result = acs_vlan_ip_range.get_result(ipr)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,284 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_vmsnapshot
short_description: Manages VM snapshots on Apache CloudStack based clouds.
description:
- Create, remove and revert VM from snapshots.
author: René Moser (@resmo)
options:
name:
description:
- Unique Name of the snapshot. In CloudStack terms display name.
type: str
required: true
aliases: [ display_name ]
vm:
description:
- Name of the virtual machine.
type: str
required: true
description:
description:
- Description of the snapshot.
type: str
snapshot_memory:
description:
- Snapshot memory if set to true.
default: no
type: bool
zone:
description:
- Name of the zone in which the VM is in. If not set, default zone is used.
type: str
project:
description:
- Name of the project the VM is assigned to.
type: str
state:
description:
- State of the snapshot.
type: str
default: present
choices: [ present, absent, revert ]
domain:
description:
- Domain the VM snapshot is related to.
type: str
account:
description:
- Account the VM snapshot is related to.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- "To delete all tags, set a empty list e.g. I(tags: [])."
type: list
aliases: [ tag ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a VM snapshot of disk and memory before an upgrade
cs_vmsnapshot:
name: Snapshot before upgrade
vm: web-01
snapshot_memory: yes
delegate_to: localhost
- name: Revert a VM to a snapshot after a failed upgrade
cs_vmsnapshot:
name: Snapshot before upgrade
vm: web-01
state: revert
delegate_to: localhost
- name: Remove a VM snapshot after successful upgrade
cs_vmsnapshot:
name: Snapshot before upgrade
vm: web-01
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the snapshot.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: Name of the snapshot.
returned: success
type: str
sample: snapshot before update
display_name:
description: Display name of the snapshot.
returned: success
type: str
sample: snapshot before update
created:
description: date of the snapshot.
returned: success
type: str
sample: 2015-03-29T14:57:06+0200
current:
description: true if the snapshot is current
returned: success
type: bool
sample: True
state:
description: state of the vm snapshot
returned: success
type: str
sample: Allocated
type:
description: type of vm snapshot
returned: success
type: str
sample: DiskAndMemory
description:
description: description of vm snapshot
returned: success
type: str
sample: snapshot brought to you by Ansible
domain:
description: Domain the vm snapshot is related to.
returned: success
type: str
sample: example domain
account:
description: Account the vm snapshot is related to.
returned: success
type: str
sample: example account
project:
description: Name of project the vm snapshot is related to.
returned: success
type: str
sample: Production
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackVmSnapshot(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackVmSnapshot, self).__init__(module)
self.returns = {
'type': 'type',
'current': 'current',
}
def get_snapshot(self):
args = {
'virtualmachineid': self.get_vm('id'),
'account': self.get_account('name'),
'domainid': self.get_domain('id'),
'projectid': self.get_project('id'),
'name': self.module.params.get('name'),
}
snapshots = self.query_api('listVMSnapshot', **args)
if snapshots:
return snapshots['vmSnapshot'][0]
return None
def create_snapshot(self):
snapshot = self.get_snapshot()
if not snapshot:
self.result['changed'] = True
args = {
'virtualmachineid': self.get_vm('id'),
'name': self.module.params.get('name'),
'description': self.module.params.get('description'),
'snapshotmemory': self.module.params.get('snapshot_memory'),
}
if not self.module.check_mode:
res = self.query_api('createVMSnapshot', **args)
poll_async = self.module.params.get('poll_async')
if res and poll_async:
snapshot = self.poll_job(res, 'vmsnapshot')
if snapshot:
snapshot = self.ensure_tags(resource=snapshot, resource_type='Snapshot')
return snapshot
def remove_snapshot(self):
snapshot = self.get_snapshot()
if snapshot:
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('deleteVMSnapshot', vmsnapshotid=snapshot['id'])
poll_async = self.module.params.get('poll_async')
if res and poll_async:
res = self.poll_job(res, 'vmsnapshot')
return snapshot
def revert_vm_to_snapshot(self):
snapshot = self.get_snapshot()
if snapshot:
self.result['changed'] = True
if snapshot['state'] != "Ready":
self.module.fail_json(msg="snapshot state is '%s', not ready, could not revert VM" % snapshot['state'])
if not self.module.check_mode:
res = self.query_api('revertToVMSnapshot', vmsnapshotid=snapshot['id'])
poll_async = self.module.params.get('poll_async')
if res and poll_async:
res = self.poll_job(res, 'vmsnapshot')
return snapshot
self.module.fail_json(msg="snapshot not found, could not revert VM")
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True, aliases=['display_name']),
vm=dict(required=True),
description=dict(),
zone=dict(),
snapshot_memory=dict(type='bool', default=False),
state=dict(choices=['present', 'absent', 'revert'], default='present'),
domain=dict(),
account=dict(),
project=dict(),
poll_async=dict(type='bool', default=True),
tags=dict(type='list', aliases=['tag']),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_vmsnapshot = AnsibleCloudStackVmSnapshot(module)
state = module.params.get('state')
if state in ['revert']:
snapshot = acs_vmsnapshot.revert_vm_to_snapshot()
elif state in ['absent']:
snapshot = acs_vmsnapshot.remove_snapshot()
else:
snapshot = acs_vmsnapshot.create_snapshot()
result = acs_vmsnapshot.get_result(snapshot)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,572 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, Jefferson Girão <jefferson@girao.net>
# (c) 2015, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_volume
short_description: Manages volumes on Apache CloudStack based clouds.
description:
- Create, destroy, attach, detach, extract or upload volumes.
author:
- Jefferson Girão (@jeffersongirao)
- René Moser (@resmo)
options:
name:
description:
- Name of the volume.
- I(name) can only contain ASCII letters.
type: str
required: true
account:
description:
- Account the volume is related to.
type: str
device_id:
description:
- ID of the device on a VM the volume is attached to.
- Only considered if I(state) is C(attached).
type: int
custom_id:
description:
- Custom id to the resource.
- Allowed to Root Admins only.
type: str
disk_offering:
description:
- Name of the disk offering to be used.
- Required one of I(disk_offering), I(snapshot) if volume is not already I(state=present).
type: str
display_volume:
description:
- Whether to display the volume to the end user or not.
- Allowed to Root Admins only.
type: bool
domain:
description:
- Name of the domain the volume to be deployed in.
type: str
max_iops:
description:
- Max iops
type: int
min_iops:
description:
- Min iops
type: int
project:
description:
- Name of the project the volume to be deployed in.
type: str
size:
description:
- Size of disk in GB
type: int
snapshot:
description:
- The snapshot name for the disk volume.
- Required one of I(disk_offering), I(snapshot) if volume is not already I(state=present).
type: str
force:
description:
- Force removal of volume even it is attached to a VM.
- Considered on I(state=absent) only.
default: no
type: bool
shrink_ok:
description:
- Whether to allow to shrink the volume.
default: no
type: bool
vm:
description:
- Name of the virtual machine to attach the volume to.
type: str
zone:
description:
- Name of the zone in which the volume should be deployed.
- If not set, default zone is used.
type: str
state:
description:
- State of the volume.
- The choices C(extracted) and C(uploaded) were added in version 2.8.
type: str
default: present
choices: [ present, absent, attached, detached, extracted, uploaded ]
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- "To delete all tags, set a empty list e.g. I(tags: [])."
type: list
aliases: [ tag ]
url:
description:
- URL to which the volume would be extracted on I(state=extracted)
- or the URL where to download the volume on I(state=uploaded).
- Only considered if I(state) is C(extracted) or C(uploaded).
type: str
mode:
description:
- Mode for the volume extraction.
- Only considered if I(state=extracted).
type: str
choices: [ http_download, ftp_upload ]
default: http_download
format:
description:
- The format for the volume.
- Only considered if I(state=uploaded).
type: str
choices: [ QCOW2, RAW, VHD, VHDX, OVA ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: create volume within project and zone with specified storage options
cs_volume:
name: web-vm-1-volume
project: Integration
zone: ch-zrh-ix-01
disk_offering: PerfPlus Storage
size: 20
delegate_to: localhost
- name: create/attach volume to instance
cs_volume:
name: web-vm-1-volume
disk_offering: PerfPlus Storage
size: 20
vm: web-vm-1
state: attached
delegate_to: localhost
- name: detach volume
cs_volume:
name: web-vm-1-volume
state: detached
delegate_to: localhost
- name: remove volume
cs_volume:
name: web-vm-1-volume
state: absent
delegate_to: localhost
# New in version 2.8
- name: Extract DATA volume to make it downloadable
cs_volume:
state: extracted
name: web-vm-1-volume
register: data_vol_out
delegate_to: localhost
- name: Create new volume by downloading source volume
cs_volume:
state: uploaded
name: web-vm-1-volume-2
format: VHD
url: "{{ data_vol_out.url }}"
delegate_to: localhost
'''
RETURN = '''
id:
description: ID of the volume.
returned: success
type: str
sample:
name:
description: Name of the volume.
returned: success
type: str
sample: web-volume-01
display_name:
description: Display name of the volume.
returned: success
type: str
sample: web-volume-01
group:
description: Group the volume belongs to
returned: success
type: str
sample: web
domain:
description: Domain the volume belongs to
returned: success
type: str
sample: example domain
project:
description: Project the volume belongs to
returned: success
type: str
sample: Production
zone:
description: Name of zone the volume is in.
returned: success
type: str
sample: ch-gva-2
created:
description: Date of the volume was created.
returned: success
type: str
sample: 2014-12-01T14:57:57+0100
attached:
description: Date of the volume was attached.
returned: success
type: str
sample: 2014-12-01T14:57:57+0100
type:
description: Disk volume type.
returned: success
type: str
sample: DATADISK
size:
description: Size of disk volume.
returned: success
type: int
sample: 20
vm:
description: Name of the vm the volume is attached to (not returned when detached)
returned: success
type: str
sample: web-01
state:
description: State of the volume
returned: success
type: str
sample: Attached
device_id:
description: Id of the device on user vm the volume is attached to (not returned when detached)
returned: success
type: int
sample: 1
url:
description: The url of the uploaded volume or the download url depending extraction mode.
returned: success when I(state=extracted)
type: str
sample: http://1.12.3.4/userdata/387e2c7c-7c42-4ecc-b4ed-84e8367a1965.vhd
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_required_together,
cs_argument_spec
)
class AnsibleCloudStackVolume(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackVolume, self).__init__(module)
self.returns = {
'group': 'group',
'attached': 'attached',
'vmname': 'vm',
'deviceid': 'device_id',
'type': 'type',
'size': 'size',
'url': 'url',
}
self.volume = None
def get_volume(self):
if not self.volume:
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'zoneid': self.get_zone(key='id'),
'displayvolume': self.module.params.get('display_volume'),
'type': 'DATADISK',
'fetch_list': True,
}
# Do not filter on DATADISK when state=extracted
if self.module.params.get('state') == 'extracted':
del args['type']
volumes = self.query_api('listVolumes', **args)
if volumes:
volume_name = self.module.params.get('name')
for v in volumes:
if volume_name.lower() == v['name'].lower():
self.volume = v
break
return self.volume
def get_snapshot(self, key=None):
snapshot = self.module.params.get('snapshot')
if not snapshot:
return None
args = {
'name': snapshot,
'account': self.get_account('name'),
'domainid': self.get_domain('id'),
'projectid': self.get_project('id'),
}
snapshots = self.query_api('listSnapshots', **args)
if snapshots:
return self._get_by_key(key, snapshots['snapshot'][0])
self.module.fail_json(msg="Snapshot with name %s not found" % snapshot)
def present_volume(self):
volume = self.get_volume()
if volume:
volume = self.update_volume(volume)
else:
disk_offering_id = self.get_disk_offering(key='id')
snapshot_id = self.get_snapshot(key='id')
if not disk_offering_id and not snapshot_id:
self.module.fail_json(msg="Required one of: disk_offering,snapshot")
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'diskofferingid': disk_offering_id,
'displayvolume': self.module.params.get('display_volume'),
'maxiops': self.module.params.get('max_iops'),
'miniops': self.module.params.get('min_iops'),
'projectid': self.get_project(key='id'),
'size': self.module.params.get('size'),
'snapshotid': snapshot_id,
'zoneid': self.get_zone(key='id')
}
if not self.module.check_mode:
res = self.query_api('createVolume', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
volume = self.poll_job(res, 'volume')
if volume:
volume = self.ensure_tags(resource=volume, resource_type='Volume')
self.volume = volume
return volume
def attached_volume(self):
volume = self.present_volume()
if volume:
if volume.get('virtualmachineid') != self.get_vm(key='id'):
self.result['changed'] = True
if not self.module.check_mode:
volume = self.detached_volume()
if 'attached' not in volume:
self.result['changed'] = True
args = {
'id': volume['id'],
'virtualmachineid': self.get_vm(key='id'),
'deviceid': self.module.params.get('device_id'),
}
if not self.module.check_mode:
res = self.query_api('attachVolume', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
volume = self.poll_job(res, 'volume')
return volume
def detached_volume(self):
volume = self.present_volume()
if volume:
if 'attached' not in volume:
return volume
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('detachVolume', id=volume['id'])
poll_async = self.module.params.get('poll_async')
if poll_async:
volume = self.poll_job(res, 'volume')
return volume
def absent_volume(self):
volume = self.get_volume()
if volume:
if 'attached' in volume and not self.module.params.get('force'):
self.module.fail_json(msg="Volume '%s' is attached, use force=true for detaching and removing the volume." % volume.get('name'))
self.result['changed'] = True
if not self.module.check_mode:
volume = self.detached_volume()
res = self.query_api('deleteVolume', id=volume['id'])
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'volume')
return volume
def update_volume(self, volume):
args_resize = {
'id': volume['id'],
'diskofferingid': self.get_disk_offering(key='id'),
'maxiops': self.module.params.get('max_iops'),
'miniops': self.module.params.get('min_iops'),
'size': self.module.params.get('size')
}
# change unit from bytes to giga bytes to compare with args
volume_copy = volume.copy()
volume_copy['size'] = volume_copy['size'] / (2**30)
if self.has_changed(args_resize, volume_copy):
self.result['changed'] = True
if not self.module.check_mode:
args_resize['shrinkok'] = self.module.params.get('shrink_ok')
res = self.query_api('resizeVolume', **args_resize)
poll_async = self.module.params.get('poll_async')
if poll_async:
volume = self.poll_job(res, 'volume')
self.volume = volume
return volume
def extract_volume(self):
volume = self.get_volume()
if not volume:
self.module.fail_json(msg="Failed: volume not found")
args = {
'id': volume['id'],
'url': self.module.params.get('url'),
'mode': self.module.params.get('mode').upper(),
'zoneid': self.get_zone(key='id')
}
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('extractVolume', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
volume = self.poll_job(res, 'volume')
self.volume = volume
return volume
def upload_volume(self):
volume = self.get_volume()
if not volume:
disk_offering_id = self.get_disk_offering(key='id')
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'zoneid': self.get_zone(key='id'),
'format': self.module.params.get('format'),
'url': self.module.params.get('url'),
'diskofferingid': disk_offering_id,
}
if not self.module.check_mode:
res = self.query_api('uploadVolume', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
volume = self.poll_job(res, 'volume')
if volume:
volume = self.ensure_tags(resource=volume, resource_type='Volume')
self.volume = volume
return volume
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
disk_offering=dict(),
display_volume=dict(type='bool'),
max_iops=dict(type='int'),
min_iops=dict(type='int'),
size=dict(type='int'),
snapshot=dict(),
vm=dict(),
device_id=dict(type='int'),
custom_id=dict(),
force=dict(type='bool', default=False),
shrink_ok=dict(type='bool', default=False),
state=dict(default='present', choices=[
'present',
'absent',
'attached',
'detached',
'extracted',
'uploaded',
]),
zone=dict(),
domain=dict(),
account=dict(),
project=dict(),
poll_async=dict(type='bool', default=True),
tags=dict(type='list', aliases=['tag']),
url=dict(),
mode=dict(choices=['http_download', 'ftp_upload'], default='http_download'),
format=dict(choices=['QCOW2', 'RAW', 'VHD', 'VHDX', 'OVA']),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
mutually_exclusive=(
['snapshot', 'disk_offering'],
),
required_if=[
('state', 'uploaded', ['url', 'format']),
],
supports_check_mode=True
)
acs_vol = AnsibleCloudStackVolume(module)
state = module.params.get('state')
if state in ['absent']:
volume = acs_vol.absent_volume()
elif state in ['attached']:
volume = acs_vol.attached_volume()
elif state in ['detached']:
volume = acs_vol.detached_volume()
elif state == 'extracted':
volume = acs_vol.extract_volume()
elif state == 'uploaded':
volume = acs_vol.upload_volume()
else:
volume = acs_vol.present_volume()
result = acs_vol.get_result(volume)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,398 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_vpc
short_description: "Manages VPCs on Apache CloudStack based clouds."
description:
- Create, update and delete VPCs.
author: René Moser (@resmo)
options:
name:
description:
- Name of the VPC.
type: str
required: true
display_text:
description:
- Display text of the VPC.
- If not set, I(name) will be used for creating.
type: str
cidr:
description:
- CIDR of the VPC, e.g. 10.1.0.0/16
- All VPC guest networks' CIDRs must be within this CIDR.
- Required on I(state=present).
type: str
network_domain:
description:
- Network domain for the VPC.
- All networks inside the VPC will belong to this domain.
- Only considered while creating the VPC, can not be changed.
type: str
vpc_offering:
description:
- Name of the VPC offering.
- If not set, default VPC offering is used.
type: str
clean_up:
description:
- Whether to redeploy a VPC router or not when I(state=restarted)
type: bool
state:
description:
- State of the VPC.
- The state C(present) creates a started VPC.
- The state C(stopped) is only considered while creating the VPC, added in version 2.6.
type: str
default: present
choices:
- present
- absent
- stopped
- restarted
domain:
description:
- Domain the VPC is related to.
type: str
account:
description:
- Account the VPC is related to.
type: str
project:
description:
- Name of the project the VPC is related to.
type: str
zone:
description:
- Name of the zone.
- If not set, default zone is used.
type: str
tags:
description:
- List of tags. Tags are a list of dictionaries having keys I(key) and I(value).
- "For deleting all tags, set an empty list e.g. I(tags: [])."
type: list
aliases: [ tag ]
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Ensure a VPC is present but not started after creating
cs_vpc:
name: my_vpc
display_text: My example VPC
cidr: 10.10.0.0/16
state: stopped
delegate_to: localhost
- name: Ensure a VPC is present and started after creating
cs_vpc:
name: my_vpc
display_text: My example VPC
cidr: 10.10.0.0/16
delegate_to: localhost
- name: Ensure a VPC is absent
cs_vpc:
name: my_vpc
state: absent
delegate_to: localhost
- name: Ensure a VPC is restarted with clean up
cs_vpc:
name: my_vpc
clean_up: yes
state: restarted
delegate_to: localhost
'''
RETURN = '''
---
id:
description: "UUID of the VPC."
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: "Name of the VPC."
returned: success
type: str
sample: my_vpc
display_text:
description: "Display text of the VPC."
returned: success
type: str
sample: My example VPC
cidr:
description: "CIDR of the VPC."
returned: success
type: str
sample: 10.10.0.0/16
network_domain:
description: "Network domain of the VPC."
returned: success
type: str
sample: example.com
region_level_vpc:
description: "Whether the VPC is region level or not."
returned: success
type: bool
sample: true
restart_required:
description: "Whether the VPC router needs a restart or not."
returned: success
type: bool
sample: true
distributed_vpc_router:
description: "Whether the VPC uses distributed router or not."
returned: success
type: bool
sample: true
redundant_vpc_router:
description: "Whether the VPC has redundant routers or not."
returned: success
type: bool
sample: true
domain:
description: "Domain the VPC is related to."
returned: success
type: str
sample: example domain
account:
description: "Account the VPC is related to."
returned: success
type: str
sample: example account
project:
description: "Name of project the VPC is related to."
returned: success
type: str
sample: Production
zone:
description: "Name of zone the VPC is in."
returned: success
type: str
sample: ch-gva-2
state:
description: "State of the VPC."
returned: success
type: str
sample: Enabled
tags:
description: "List of resource tags associated with the VPC."
returned: success
type: list
sample: '[ { "key": "foo", "value": "bar" } ]'
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackVpc(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackVpc, self).__init__(module)
self.returns = {
'cidr': 'cidr',
'networkdomain': 'network_domain',
'redundantvpcrouter': 'redundant_vpc_router',
'distributedvpcrouter': 'distributed_vpc_router',
'regionlevelvpc': 'region_level_vpc',
'restartrequired': 'restart_required',
}
self.vpc = None
def get_vpc_offering(self, key=None):
vpc_offering = self.module.params.get('vpc_offering')
args = {
'state': 'Enabled',
}
if vpc_offering:
args['name'] = vpc_offering
fail_msg = "VPC offering not found or not enabled: %s" % vpc_offering
else:
args['isdefault'] = True
fail_msg = "No enabled default VPC offering found"
vpc_offerings = self.query_api('listVPCOfferings', **args)
if vpc_offerings:
# The API name argument filter also matches substrings, we have to
# iterate over the results to get an exact match
for vo in vpc_offerings['vpcoffering']:
if 'name' in args:
if args['name'] == vo['name']:
return self._get_by_key(key, vo)
# Return the first offering found, if not queried for the name
else:
return self._get_by_key(key, vo)
self.module.fail_json(msg=fail_msg)
def get_vpc(self):
if self.vpc:
return self.vpc
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'zoneid': self.get_zone(key='id'),
'fetch_list': True,
}
vpcs = self.query_api('listVPCs', **args)
if vpcs:
vpc_name = self.module.params.get('name')
for v in vpcs:
if vpc_name in [v['name'], v['displaytext'], v['id']]:
# Fail if the identifier matches more than one VPC
if self.vpc:
self.module.fail_json(msg="More than one VPC found with the provided identifyer: %s" % vpc_name)
else:
self.vpc = v
return self.vpc
def restart_vpc(self):
self.result['changed'] = True
vpc = self.get_vpc()
if vpc and not self.module.check_mode:
args = {
'id': vpc['id'],
'cleanup': self.module.params.get('clean_up'),
}
res = self.query_api('restartVPC', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'vpc')
return vpc
def present_vpc(self):
vpc = self.get_vpc()
if not vpc:
vpc = self._create_vpc(vpc)
else:
vpc = self._update_vpc(vpc)
if vpc:
vpc = self.ensure_tags(resource=vpc, resource_type='Vpc')
return vpc
def _create_vpc(self, vpc):
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'displaytext': self.get_or_fallback('display_text', 'name'),
'networkdomain': self.module.params.get('network_domain'),
'vpcofferingid': self.get_vpc_offering(key='id'),
'cidr': self.module.params.get('cidr'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'zoneid': self.get_zone(key='id'),
'start': self.module.params.get('state') != 'stopped'
}
self.result['diff']['after'] = args
if not self.module.check_mode:
res = self.query_api('createVPC', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
vpc = self.poll_job(res, 'vpc')
return vpc
def _update_vpc(self, vpc):
args = {
'id': vpc['id'],
'displaytext': self.module.params.get('display_text'),
}
if self.has_changed(args, vpc):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateVPC', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
vpc = self.poll_job(res, 'vpc')
return vpc
def absent_vpc(self):
vpc = self.get_vpc()
if vpc:
self.result['changed'] = True
self.result['diff']['before'] = vpc
if not self.module.check_mode:
res = self.query_api('deleteVPC', id=vpc['id'])
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'vpc')
return vpc
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
cidr=dict(),
display_text=dict(),
vpc_offering=dict(),
network_domain=dict(),
clean_up=dict(type='bool'),
state=dict(choices=['present', 'absent', 'stopped', 'restarted'], default='present'),
domain=dict(),
account=dict(),
project=dict(),
zone=dict(),
tags=dict(type='list', aliases=['tag']),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
required_if=[
('state', 'present', ['cidr']),
],
supports_check_mode=True,
)
acs_vpc = AnsibleCloudStackVpc(module)
state = module.params.get('state')
if state == 'absent':
vpc = acs_vpc.absent_vpc()
elif state == 'restarted':
vpc = acs_vpc.restart_vpc()
else:
vpc = acs_vpc.present_vpc()
result = acs_vpc.get_result(vpc)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,323 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, David Passante (@dpassante)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_vpc_offering
short_description: Manages vpc offerings on Apache CloudStack based clouds.
description:
- Create, update, enable, disable and remove CloudStack VPC offerings.
author: David Passante (@dpassante)
options:
name:
description:
- The name of the vpc offering
type: str
required: true
state:
description:
- State of the vpc offering.
type: str
choices: [ enabled, present, disabled, absent ]
default: present
display_text:
description:
- Display text of the vpc offerings
type: str
service_capabilities:
description:
- Desired service capabilities as part of vpc offering.
type: list
aliases: [ service_capability ]
service_offering:
description:
- The name or ID of the service offering for the VPC router appliance.
type: str
supported_services:
description:
- Services supported by the vpc offering
type: list
aliases: [ supported_service ]
service_providers:
description:
- provider to service mapping. If not specified, the provider for the service will be mapped to the default provider on the physical network
type: list
aliases: [ service_provider ]
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Create a vpc offering and enable it
cs_vpc_offering:
name: my_vpc_offering
display_text: vpc offering description
state: enabled
supported_services: [ Dns, Dhcp ]
service_providers:
- {service: 'dns', provider: 'VpcVirtualRouter'}
- {service: 'dhcp', provider: 'VpcVirtualRouter'}
delegate_to: localhost
- name: Create a vpc offering with redundant router
cs_vpc_offering:
name: my_vpc_offering
display_text: vpc offering description
supported_services: [ Dns, Dhcp, SourceNat ]
service_providers:
- {service: 'dns', provider: 'VpcVirtualRouter'}
- {service: 'dhcp', provider: 'VpcVirtualRouter'}
- {service: 'SourceNat', provider: 'VpcVirtualRouter'}
service_capabilities:
- {service: 'SourceNat', capabilitytype: 'RedundantRouter', capabilityvalue: true}
delegate_to: localhost
- name: Create a region level vpc offering with distributed router
cs_vpc_offering:
name: my_vpc_offering
display_text: vpc offering description
state: present
supported_services: [ Dns, Dhcp, SourceNat ]
service_providers:
- {service: 'dns', provider: 'VpcVirtualRouter'}
- {service: 'dhcp', provider: 'VpcVirtualRouter'}
- {service: 'SourceNat', provider: 'VpcVirtualRouter'}
service_capabilities:
- {service: 'Connectivity', capabilitytype: 'DistributedRouter', capabilityvalue: true}
- {service: 'Connectivity', capabilitytype: 'RegionLevelVPC', capabilityvalue: true}
delegate_to: localhost
- name: Remove a vpc offering
cs_vpc_offering:
name: my_vpc_offering
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the vpc offering.
returned: success
type: str
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: The name of the vpc offering
returned: success
type: str
sample: MyCustomVPCOffering
display_text:
description: The display text of the vpc offering
returned: success
type: str
sample: My vpc offering
state:
description: The state of the vpc offering
returned: success
type: str
sample: Enabled
service_offering_id:
description: The service offering ID.
returned: success
type: str
sample: c5f7a5fc-43f8-11e5-a151-feff819cdc9f
is_default:
description: Whether VPC offering is the default offering or not.
returned: success
type: bool
sample: false
region_level:
description: Indicated if the offering can support region level vpc.
returned: success
type: bool
sample: false
distributed:
description: Indicates if the vpc offering supports distributed router for one-hop forwarding.
returned: success
type: bool
sample: false
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackVPCOffering(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackVPCOffering, self).__init__(module)
self.returns = {
'serviceofferingid': 'service_offering_id',
'isdefault': 'is_default',
'distributedvpcrouter': 'distributed',
'supportsregionLevelvpc': 'region_level',
}
self.vpc_offering = None
def get_vpc_offering(self):
if self.vpc_offering:
return self.vpc_offering
args = {
'name': self.module.params.get('name'),
}
vo = self.query_api('listVPCOfferings', **args)
if vo:
for vpc_offer in vo['vpcoffering']:
if args['name'] == vpc_offer['name']:
self.vpc_offering = vpc_offer
return self.vpc_offering
def get_service_offering_id(self):
service_offering = self.module.params.get('service_offering')
if not service_offering:
return None
args = {
'issystem': True
}
service_offerings = self.query_api('listServiceOfferings', **args)
if service_offerings:
for s in service_offerings['serviceoffering']:
if service_offering in [s['name'], s['id']]:
return s['id']
self.fail_json(msg="Service offering '%s' not found" % service_offering)
def create_or_update(self):
vpc_offering = self.get_vpc_offering()
if not vpc_offering:
vpc_offering = self.create_vpc_offering()
return self.update_vpc_offering(vpc_offering)
def create_vpc_offering(self):
vpc_offering = None
self.result['changed'] = True
args = {
'name': self.module.params.get('name'),
'state': self.module.params.get('state'),
'displaytext': self.module.params.get('display_text'),
'supportedservices': self.module.params.get('supported_services'),
'serviceproviderlist': self.module.params.get('service_providers'),
'serviceofferingid': self.get_service_offering_id(),
'servicecapabilitylist': self.module.params.get('service_capabilities'),
}
required_params = [
'display_text',
'supported_services',
]
self.module.fail_on_missing_params(required_params=required_params)
if not self.module.check_mode:
res = self.query_api('createVPCOffering', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
vpc_offering = self.poll_job(res, 'vpcoffering')
return vpc_offering
def delete_vpc_offering(self):
vpc_offering = self.get_vpc_offering()
if vpc_offering:
self.result['changed'] = True
args = {
'id': vpc_offering['id'],
}
if not self.module.check_mode:
res = self.query_api('deleteVPCOffering', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
vpc_offering = self.poll_job(res, 'vpcoffering')
return vpc_offering
def update_vpc_offering(self, vpc_offering):
if not vpc_offering:
return vpc_offering
args = {
'id': vpc_offering['id'],
'state': self.module.params.get('state'),
'name': self.module.params.get('name'),
'displaytext': self.module.params.get('display_text'),
}
if args['state'] in ['enabled', 'disabled']:
args['state'] = args['state'].title()
else:
del args['state']
if self.has_changed(args, vpc_offering):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateVPCOffering', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
vpc_offering = self.poll_job(res, 'vpcoffering')
return vpc_offering
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
display_text=dict(),
state=dict(choices=['enabled', 'present', 'disabled', 'absent'], default='present'),
service_capabilities=dict(type='list', aliases=['service_capability']),
service_offering=dict(),
supported_services=dict(type='list', aliases=['supported_service']),
service_providers=dict(type='list', aliases=['service_provider']),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_vpc_offering = AnsibleCloudStackVPCOffering(module)
state = module.params.get('state')
if state in ['absent']:
vpc_offering = acs_vpc_offering.delete_vpc_offering()
else:
vpc_offering = acs_vpc_offering.create_or_update()
result = acs_vpc_offering.get_result(vpc_offering)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,355 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: cs_vpn_connection
short_description: Manages site-to-site VPN connections on Apache CloudStack based clouds.
description:
- Create and remove VPN connections.
author: René Moser (@resmo)
options:
vpc:
description:
- Name of the VPC the VPN connection is related to.
type: str
required: true
vpn_customer_gateway:
description:
- Name of the VPN customer gateway.
type: str
required: true
passive:
description:
- State of the VPN connection.
- Only considered when I(state=present).
default: no
type: bool
force:
description:
- Activate the VPN gateway if not already activated on I(state=present).
- Also see M(cs_vpn_gateway).
default: no
type: bool
state:
description:
- State of the VPN connection.
type: str
default: present
choices: [ present, absent ]
zone:
description:
- Name of the zone the VPC is related to.
- If not set, default zone is used.
type: str
domain:
description:
- Domain the VPN connection is related to.
type: str
account:
description:
- Account the VPN connection is related to.
type: str
project:
description:
- Name of the project the VPN connection is related to.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = r'''
- name: Create a VPN connection with activated VPN gateway
cs_vpn_connection:
vpn_customer_gateway: my vpn connection
vpc: my vpc
delegate_to: localhost
- name: Create a VPN connection and force VPN gateway activation
cs_vpn_connection:
vpn_customer_gateway: my vpn connection
vpc: my vpc
force: yes
delegate_to: localhost
- name: Remove a vpn connection
cs_vpn_connection:
vpn_customer_gateway: my vpn connection
vpc: my vpc
state: absent
delegate_to: localhost
'''
RETURN = r'''
---
id:
description: UUID of the VPN connection.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
vpn_gateway_id:
description: UUID of the VPN gateway.
returned: success
type: str
sample: 04589590-ac63-93f5-4ffc-b698b8ac38b6
domain:
description: Domain the VPN connection is related to.
returned: success
type: str
sample: example domain
account:
description: Account the VPN connection is related to.
returned: success
type: str
sample: example account
project:
description: Name of project the VPN connection is related to.
returned: success
type: str
sample: Production
created:
description: Date the connection was created.
returned: success
type: str
sample: 2014-12-01T14:57:57+0100
dpd:
description: Whether dead pear detection is enabled or not.
returned: success
type: bool
sample: true
esp_lifetime:
description: Lifetime in seconds of phase 2 VPN connection.
returned: success
type: int
sample: 86400
esp_policy:
description: IKE policy of the VPN connection.
returned: success
type: str
sample: aes256-sha1;modp1536
force_encap:
description: Whether encapsulation for NAT traversal is enforced or not.
returned: success
type: bool
sample: true
ike_lifetime:
description: Lifetime in seconds of phase 1 VPN connection.
returned: success
type: int
sample: 86400
ike_policy:
description: ESP policy of the VPN connection.
returned: success
type: str
sample: aes256-sha1;modp1536
cidrs:
description: List of CIDRs of the customer gateway.
returned: success
type: list
sample: [ 10.10.10.0/24 ]
passive:
description: Whether the connection is passive or not.
returned: success
type: bool
sample: false
public_ip:
description: IP address of the VPN gateway.
returned: success
type: str
sample: 10.100.212.10
gateway:
description: IP address of the VPN customer gateway.
returned: success
type: str
sample: 10.101.214.10
state:
description: State of the VPN connection.
returned: success
type: str
sample: Connected
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackVpnConnection(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackVpnConnection, self).__init__(module)
self.returns = {
'dpd': 'dpd',
'esplifetime': 'esp_lifetime',
'esppolicy': 'esp_policy',
'gateway': 'gateway',
'ikepolicy': 'ike_policy',
'ikelifetime': 'ike_lifetime',
'publicip': 'public_ip',
'passive': 'passive',
's2svpngatewayid': 'vpn_gateway_id',
}
self.vpn_customer_gateway = None
def get_vpn_customer_gateway(self, key=None, identifier=None, refresh=False):
if not refresh and self.vpn_customer_gateway:
return self._get_by_key(key, self.vpn_customer_gateway)
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'fetch_list': True,
}
vpn_customer_gateway = identifier or self.module.params.get('vpn_customer_gateway')
vcgws = self.query_api('listVpnCustomerGateways', **args)
if vcgws:
for vcgw in vcgws:
if vpn_customer_gateway.lower() in [vcgw['id'], vcgw['name'].lower()]:
self.vpn_customer_gateway = vcgw
return self._get_by_key(key, self.vpn_customer_gateway)
self.fail_json(msg="VPN customer gateway not found: %s" % vpn_customer_gateway)
def get_vpn_gateway(self, key=None):
args = {
'vpcid': self.get_vpc(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
}
vpn_gateways = self.query_api('listVpnGateways', **args)
if vpn_gateways:
return self._get_by_key(key, vpn_gateways['vpngateway'][0])
elif self.module.params.get('force'):
if self.module.check_mode:
return {}
res = self.query_api('createVpnGateway', **args)
vpn_gateway = self.poll_job(res, 'vpngateway')
return self._get_by_key(key, vpn_gateway)
self.fail_json(msg="VPN gateway not found and not forced to create one")
def get_vpn_connection(self):
args = {
'vpcid': self.get_vpc(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
}
vpn_conns = self.query_api('listVpnConnections', **args)
if vpn_conns:
for vpn_conn in vpn_conns['vpnconnection']:
if self.get_vpn_customer_gateway(key='id') == vpn_conn['s2scustomergatewayid']:
return vpn_conn
def present_vpn_connection(self):
vpn_conn = self.get_vpn_connection()
args = {
's2scustomergatewayid': self.get_vpn_customer_gateway(key='id'),
's2svpngatewayid': self.get_vpn_gateway(key='id'),
'passive': self.module.params.get('passive'),
}
if not vpn_conn:
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('createVpnConnection', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
vpn_conn = self.poll_job(res, 'vpnconnection')
return vpn_conn
def absent_vpn_connection(self):
vpn_conn = self.get_vpn_connection()
if vpn_conn:
self.result['changed'] = True
args = {
'id': vpn_conn['id']
}
if not self.module.check_mode:
res = self.query_api('deleteVpnConnection', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'vpnconnection')
return vpn_conn
def get_result(self, vpn_conn):
super(AnsibleCloudStackVpnConnection, self).get_result(vpn_conn)
if vpn_conn:
if 'cidrlist' in vpn_conn:
self.result['cidrs'] = vpn_conn['cidrlist'].split(',') or [vpn_conn['cidrlist']]
# Ensure we return a bool
self.result['force_encap'] = True if vpn_conn.get('forceencap') else False
args = {
'key': 'name',
'identifier': vpn_conn['s2scustomergatewayid'],
'refresh': True,
}
self.result['vpn_customer_gateway'] = self.get_vpn_customer_gateway(**args)
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
vpn_customer_gateway=dict(required=True),
vpc=dict(required=True),
domain=dict(),
account=dict(),
project=dict(),
zone=dict(),
passive=dict(type='bool', default=False),
force=dict(type='bool', default=False),
state=dict(choices=['present', 'absent'], default='present'),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_vpn_conn = AnsibleCloudStackVpnConnection(module)
state = module.params.get('state')
if state == "absent":
vpn_conn = acs_vpn_conn.absent_vpn_connection()
else:
vpn_conn = acs_vpn_conn.present_vpn_connection()
result = acs_vpn_conn.get_result(vpn_conn)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,348 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: cs_vpn_customer_gateway
short_description: Manages site-to-site VPN customer gateway configurations on Apache CloudStack based clouds.
description:
- Create, update and remove VPN customer gateways.
author: René Moser (@resmo)
options:
name:
description:
- Name of the gateway.
type: str
required: true
cidrs:
description:
- List of guest CIDRs behind the gateway.
- Required if I(state=present).
type: list
aliases: [ cidr ]
gateway:
description:
- Public IP address of the gateway.
- Required if I(state=present).
type: str
esp_policy:
description:
- ESP policy in the format e.g. C(aes256-sha1;modp1536).
- Required if I(state=present).
type: str
ike_policy:
description:
- IKE policy in the format e.g. C(aes256-sha1;modp1536).
- Required if I(state=present).
type: str
ipsec_psk:
description:
- IPsec Preshared-Key.
- Cannot contain newline or double quotes.
- Required if I(state=present).
type: str
ike_lifetime:
description:
- Lifetime in seconds of phase 1 VPN connection.
- Defaulted to 86400 by the API on creation if not set.
type: int
esp_lifetime:
description:
- Lifetime in seconds of phase 2 VPN connection.
- Defaulted to 3600 by the API on creation if not set.
type: int
dpd:
description:
- Enable Dead Peer Detection.
- Disabled per default by the API on creation if not set.
type: bool
force_encap:
description:
- Force encapsulation for NAT traversal.
- Disabled per default by the API on creation if not set.
type: bool
state:
description:
- State of the VPN customer gateway.
type: str
default: present
choices: [ present, absent ]
domain:
description:
- Domain the VPN customer gateway is related to.
type: str
account:
description:
- Account the VPN customer gateway is related to.
type: str
project:
description:
- Name of the project the VPN gateway is related to.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
default: yes
type: bool
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = r'''
- name: Create a vpn customer gateway
cs_vpn_customer_gateway:
name: my vpn customer gateway
cidrs:
- 192.168.123.0/24
- 192.168.124.0/24
esp_policy: aes256-sha1;modp1536
gateway: 10.10.1.1
ike_policy: aes256-sha1;modp1536
ipsec_psk: "S3cr3Tk3Y"
delegate_to: localhost
- name: Remove a vpn customer gateway
cs_vpn_customer_gateway:
name: my vpn customer gateway
state: absent
delegate_to: localhost
'''
RETURN = r'''
---
id:
description: UUID of the VPN customer gateway.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
gateway:
description: IP address of the VPN customer gateway.
returned: success
type: str
sample: 10.100.212.10
domain:
description: Domain the VPN customer gateway is related to.
returned: success
type: str
sample: example domain
account:
description: Account the VPN customer gateway is related to.
returned: success
type: str
sample: example account
project:
description: Name of project the VPN customer gateway is related to.
returned: success
type: str
sample: Production
dpd:
description: Whether dead pear detection is enabled or not.
returned: success
type: bool
sample: true
esp_lifetime:
description: Lifetime in seconds of phase 2 VPN connection.
returned: success
type: int
sample: 86400
esp_policy:
description: IKE policy of the VPN customer gateway.
returned: success
type: str
sample: aes256-sha1;modp1536
force_encap:
description: Whether encapsulation for NAT traversal is enforced or not.
returned: success
type: bool
sample: true
ike_lifetime:
description: Lifetime in seconds of phase 1 VPN connection.
returned: success
type: int
sample: 86400
ike_policy:
description: ESP policy of the VPN customer gateway.
returned: success
type: str
sample: aes256-sha1;modp1536
name:
description: Name of this customer gateway.
returned: success
type: str
sample: my vpn customer gateway
cidrs:
description: List of CIDRs of this customer gateway.
returned: success
type: list
sample: [ 10.10.10.0/24 ]
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackVpnCustomerGateway(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackVpnCustomerGateway, self).__init__(module)
self.returns = {
'dpd': 'dpd',
'esplifetime': 'esp_lifetime',
'esppolicy': 'esp_policy',
'gateway': 'gateway',
'ikepolicy': 'ike_policy',
'ikelifetime': 'ike_lifetime',
'ipaddress': 'ip_address',
}
def _common_args(self):
return {
'name': self.module.params.get('name'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'cidrlist': ','.join(self.module.params.get('cidrs')) if self.module.params.get('cidrs') is not None else None,
'esppolicy': self.module.params.get('esp_policy'),
'esplifetime': self.module.params.get('esp_lifetime'),
'ikepolicy': self.module.params.get('ike_policy'),
'ikelifetime': self.module.params.get('ike_lifetime'),
'ipsecpsk': self.module.params.get('ipsec_psk'),
'dpd': self.module.params.get('dpd'),
'forceencap': self.module.params.get('force_encap'),
'gateway': self.module.params.get('gateway'),
}
def get_vpn_customer_gateway(self):
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'fetch_list': True,
}
vpn_customer_gateway = self.module.params.get('name')
vpn_customer_gateways = self.query_api('listVpnCustomerGateways', **args)
if vpn_customer_gateways:
for vgw in vpn_customer_gateways:
if vpn_customer_gateway.lower() in [vgw['id'], vgw['name'].lower()]:
return vgw
def present_vpn_customer_gateway(self):
vpn_customer_gateway = self.get_vpn_customer_gateway()
required_params = [
'cidrs',
'esp_policy',
'gateway',
'ike_policy',
'ipsec_psk',
]
self.module.fail_on_missing_params(required_params=required_params)
if not vpn_customer_gateway:
vpn_customer_gateway = self._create_vpn_customer_gateway(vpn_customer_gateway)
else:
vpn_customer_gateway = self._update_vpn_customer_gateway(vpn_customer_gateway)
return vpn_customer_gateway
def _create_vpn_customer_gateway(self, vpn_customer_gateway):
self.result['changed'] = True
args = self._common_args()
if not self.module.check_mode:
res = self.query_api('createVpnCustomerGateway', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
vpn_customer_gateway = self.poll_job(res, 'vpncustomergateway')
return vpn_customer_gateway
def _update_vpn_customer_gateway(self, vpn_customer_gateway):
args = self._common_args()
args.update({'id': vpn_customer_gateway['id']})
if self.has_changed(args, vpn_customer_gateway, skip_diff_for_keys=['ipsecpsk']):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateVpnCustomerGateway', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
vpn_customer_gateway = self.poll_job(res, 'vpncustomergateway')
return vpn_customer_gateway
def absent_vpn_customer_gateway(self):
vpn_customer_gateway = self.get_vpn_customer_gateway()
if vpn_customer_gateway:
self.result['changed'] = True
args = {
'id': vpn_customer_gateway['id']
}
if not self.module.check_mode:
res = self.query_api('deleteVpnCustomerGateway', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'vpncustomergateway')
return vpn_customer_gateway
def get_result(self, vpn_customer_gateway):
super(AnsibleCloudStackVpnCustomerGateway, self).get_result(vpn_customer_gateway)
if vpn_customer_gateway:
if 'cidrlist' in vpn_customer_gateway:
self.result['cidrs'] = vpn_customer_gateway['cidrlist'].split(',') or [vpn_customer_gateway['cidrlist']]
# Ensure we return a bool
self.result['force_encap'] = True if vpn_customer_gateway.get('forceencap') else False
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
state=dict(choices=['present', 'absent'], default='present'),
domain=dict(),
account=dict(),
project=dict(),
cidrs=dict(type='list', aliases=['cidr']),
esp_policy=dict(),
esp_lifetime=dict(type='int'),
gateway=dict(),
ike_policy=dict(),
ike_lifetime=dict(type='int'),
ipsec_psk=dict(no_log=True),
dpd=dict(type='bool'),
force_encap=dict(type='bool'),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_vpn_cgw = AnsibleCloudStackVpnCustomerGateway(module)
state = module.params.get('state')
if state == "absent":
vpn_customer_gateway = acs_vpn_cgw.absent_vpn_customer_gateway()
else:
vpn_customer_gateway = acs_vpn_cgw.present_vpn_customer_gateway()
result = acs_vpn_cgw.get_result(vpn_customer_gateway)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,210 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2017, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_vpn_gateway
short_description: Manages site-to-site VPN gateways on Apache CloudStack based clouds.
description:
- Creates and removes VPN site-to-site gateways.
author: René Moser (@resmo)
options:
vpc:
description:
- Name of the VPC.
type: str
required: true
state:
description:
- State of the VPN gateway.
type: str
default: present
choices: [ present, absent ]
domain:
description:
- Domain the VPN gateway is related to.
type: str
account:
description:
- Account the VPN gateway is related to.
type: str
project:
description:
- Name of the project the VPN gateway is related to.
type: str
zone:
description:
- Name of the zone the VPC is related to.
- If not set, default zone is used.
type: str
poll_async:
description:
- Poll async jobs until job has finished.
type: bool
default: yes
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Ensure a vpn gateway is present
cs_vpn_gateway:
vpc: my VPC
delegate_to: localhost
- name: Ensure a vpn gateway is absent
cs_vpn_gateway:
vpc: my VPC
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the VPN site-to-site gateway.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
public_ip:
description: IP address of the VPN site-to-site gateway.
returned: success
type: str
sample: 10.100.212.10
vpc:
description: Name of the VPC.
returned: success
type: str
sample: My VPC
domain:
description: Domain the VPN site-to-site gateway is related to.
returned: success
type: str
sample: example domain
account:
description: Account the VPN site-to-site gateway is related to.
returned: success
type: str
sample: example account
project:
description: Name of project the VPN site-to-site gateway is related to.
returned: success
type: str
sample: Production
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together
)
class AnsibleCloudStackVpnGateway(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackVpnGateway, self).__init__(module)
self.returns = {
'publicip': 'public_ip'
}
def get_vpn_gateway(self):
args = {
'vpcid': self.get_vpc(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id')
}
vpn_gateways = self.query_api('listVpnGateways', **args)
if vpn_gateways:
return vpn_gateways['vpngateway'][0]
return None
def present_vpn_gateway(self):
vpn_gateway = self.get_vpn_gateway()
if not vpn_gateway:
self.result['changed'] = True
args = {
'vpcid': self.get_vpc(key='id'),
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id')
}
if not self.module.check_mode:
res = self.query_api('createVpnGateway', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
vpn_gateway = self.poll_job(res, 'vpngateway')
return vpn_gateway
def absent_vpn_gateway(self):
vpn_gateway = self.get_vpn_gateway()
if vpn_gateway:
self.result['changed'] = True
args = {
'id': vpn_gateway['id']
}
if not self.module.check_mode:
res = self.query_api('deleteVpnGateway', **args)
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'vpngateway')
return vpn_gateway
def get_result(self, vpn_gateway):
super(AnsibleCloudStackVpnGateway, self).get_result(vpn_gateway)
if vpn_gateway:
self.result['vpc'] = self.get_vpc(key='name')
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
vpc=dict(required=True),
state=dict(choices=['present', 'absent'], default='present'),
domain=dict(),
account=dict(),
project=dict(),
zone=dict(),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_vpn_gw = AnsibleCloudStackVpnGateway(module)
state = module.params.get('state')
if state == "absent":
vpn_gateway = acs_vpn_gw.absent_vpn_gateway()
else:
vpn_gateway = acs_vpn_gw.present_vpn_gateway()
result = acs_vpn_gw.get_result(vpn_gateway)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,385 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_zone
short_description: Manages zones on Apache CloudStack based clouds.
description:
- Create, update and remove zones.
author: René Moser (@resmo)
options:
name:
description:
- Name of the zone.
type: str
required: true
id:
description:
- uuid of the existing zone.
type: str
state:
description:
- State of the zone.
type: str
default: present
choices: [ present, enabled, disabled, absent ]
domain:
description:
- Domain the zone is related to.
- Zone is a public zone if not set.
type: str
network_domain:
description:
- Network domain for the zone.
type: str
network_type:
description:
- Network type of the zone.
type: str
default: Basic
choices: [ Basic, Advanced ]
dns1:
description:
- First DNS for the zone.
- Required if I(state=present)
type: str
dns2:
description:
- Second DNS for the zone.
type: str
internal_dns1:
description:
- First internal DNS for the zone.
- If not set I(dns1) will be used on I(state=present).
type: str
internal_dns2:
description:
- Second internal DNS for the zone.
type: str
dns1_ipv6:
description:
- First DNS for IPv6 for the zone.
type: str
dns2_ipv6:
description:
- Second DNS for IPv6 for the zone.
type: str
guest_cidr_address:
description:
- Guest CIDR address for the zone.
type: str
dhcp_provider:
description:
- DHCP provider for the Zone.
type: str
local_storage_enabled:
description:
- Whether to enable local storage for the zone or not..
type: bool
securitygroups_enabled:
description:
- Whether the zone is security group enabled or not.
type: bool
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Ensure a zone is present
cs_zone:
name: ch-zrh-ix-01
dns1: 8.8.8.8
dns2: 8.8.4.4
network_type: basic
delegate_to: localhost
- name: Ensure a zone is disabled
cs_zone:
name: ch-zrh-ix-01
state: disabled
delegate_to: localhost
- name: Ensure a zone is enabled
cs_zone:
name: ch-zrh-ix-01
state: enabled
delegate_to: localhost
- name: Ensure a zone is absent
cs_zone:
name: ch-zrh-ix-01
state: absent
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the zone.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the zone.
returned: success
type: str
sample: zone01
dns1:
description: First DNS for the zone.
returned: success
type: str
sample: 8.8.8.8
dns2:
description: Second DNS for the zone.
returned: success
type: str
sample: 8.8.4.4
internal_dns1:
description: First internal DNS for the zone.
returned: success
type: str
sample: 8.8.8.8
internal_dns2:
description: Second internal DNS for the zone.
returned: success
type: str
sample: 8.8.4.4
dns1_ipv6:
description: First IPv6 DNS for the zone.
returned: success
type: str
sample: "2001:4860:4860::8888"
dns2_ipv6:
description: Second IPv6 DNS for the zone.
returned: success
type: str
sample: "2001:4860:4860::8844"
allocation_state:
description: State of the zone.
returned: success
type: str
sample: Enabled
domain:
description: Domain the zone is related to.
returned: success
type: str
sample: ROOT
network_domain:
description: Network domain for the zone.
returned: success
type: str
sample: example.com
network_type:
description: Network type for the zone.
returned: success
type: str
sample: basic
local_storage_enabled:
description: Local storage offering enabled.
returned: success
type: bool
sample: false
securitygroups_enabled:
description: Security groups support is enabled.
returned: success
type: bool
sample: false
guest_cidr_address:
description: Guest CIDR address for the zone
returned: success
type: str
sample: 10.1.1.0/24
dhcp_provider:
description: DHCP provider for the zone
returned: success
type: str
sample: VirtualRouter
zone_token:
description: Zone token
returned: success
type: str
sample: ccb0a60c-79c8-3230-ab8b-8bdbe8c45bb7
tags:
description: List of resource tags associated with the zone.
returned: success
type: dict
sample: [ { "key": "foo", "value": "bar" } ]
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
cs_required_together,
)
class AnsibleCloudStackZone(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackZone, self).__init__(module)
self.returns = {
'dns1': 'dns1',
'dns2': 'dns2',
'internaldns1': 'internal_dns1',
'internaldns2': 'internal_dns2',
'ipv6dns1': 'dns1_ipv6',
'ipv6dns2': 'dns2_ipv6',
'domain': 'network_domain',
'networktype': 'network_type',
'securitygroupsenabled': 'securitygroups_enabled',
'localstorageenabled': 'local_storage_enabled',
'guestcidraddress': 'guest_cidr_address',
'dhcpprovider': 'dhcp_provider',
'allocationstate': 'allocation_state',
'zonetoken': 'zone_token',
}
self.zone = None
def _get_common_zone_args(self):
args = {
'name': self.module.params.get('name'),
'dns1': self.module.params.get('dns1'),
'dns2': self.module.params.get('dns2'),
'internaldns1': self.get_or_fallback('internal_dns1', 'dns1'),
'internaldns2': self.get_or_fallback('internal_dns2', 'dns2'),
'ipv6dns1': self.module.params.get('dns1_ipv6'),
'ipv6dns2': self.module.params.get('dns2_ipv6'),
'networktype': self.module.params.get('network_type'),
'domain': self.module.params.get('network_domain'),
'localstorageenabled': self.module.params.get('local_storage_enabled'),
'guestcidraddress': self.module.params.get('guest_cidr_address'),
'dhcpprovider': self.module.params.get('dhcp_provider'),
}
state = self.module.params.get('state')
if state in ['enabled', 'disabled']:
args['allocationstate'] = state.capitalize()
return args
def get_zone(self):
if not self.zone:
args = {}
uuid = self.module.params.get('id')
if uuid:
args['id'] = uuid
zones = self.query_api('listZones', **args)
if zones:
self.zone = zones['zone'][0]
return self.zone
args['name'] = self.module.params.get('name')
zones = self.query_api('listZones', **args)
if zones:
self.zone = zones['zone'][0]
return self.zone
def present_zone(self):
zone = self.get_zone()
if zone:
zone = self._update_zone()
else:
zone = self._create_zone()
return zone
def _create_zone(self):
required_params = [
'dns1',
]
self.module.fail_on_missing_params(required_params=required_params)
self.result['changed'] = True
args = self._get_common_zone_args()
args['domainid'] = self.get_domain(key='id')
args['securitygroupenabled'] = self.module.params.get('securitygroups_enabled')
zone = None
if not self.module.check_mode:
res = self.query_api('createZone', **args)
zone = res['zone']
return zone
def _update_zone(self):
zone = self.get_zone()
args = self._get_common_zone_args()
args['id'] = zone['id']
if self.has_changed(args, zone):
self.result['changed'] = True
if not self.module.check_mode:
res = self.query_api('updateZone', **args)
zone = res['zone']
return zone
def absent_zone(self):
zone = self.get_zone()
if zone:
self.result['changed'] = True
args = {
'id': zone['id']
}
if not self.module.check_mode:
self.query_api('deleteZone', **args)
return zone
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
id=dict(),
name=dict(required=True),
dns1=dict(),
dns2=dict(),
internal_dns1=dict(),
internal_dns2=dict(),
dns1_ipv6=dict(),
dns2_ipv6=dict(),
network_type=dict(default='Basic', choices=['Basic', 'Advanced']),
network_domain=dict(),
guest_cidr_address=dict(),
dhcp_provider=dict(),
local_storage_enabled=dict(type='bool'),
securitygroups_enabled=dict(type='bool'),
state=dict(choices=['present', 'enabled', 'disabled', 'absent'], default='present'),
domain=dict(),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
acs_zone = AnsibleCloudStackZone(module)
state = module.params.get('state')
if state in ['absent']:
zone = acs_zone.absent_zone()
else:
zone = acs_zone.present_zone()
result = acs_zone.get_result(zone)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,201 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['deprecated'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_zone_facts
short_description: Gathering facts of zones from Apache CloudStack based clouds.
description:
- Gathering facts from the API of a zone.
- Sets Ansible facts accessible by the key C(cloudstack_zone) and since version 2.6 also returns results.
deprecated:
removed_in: "2.13"
why: Transformed into an info module.
alternative: Use M(cs_zone_info) instead.
author: René Moser (@resmo)
options:
zone:
description:
- Name of the zone.
type: str
required: true
aliases: [ name ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Gather facts from a zone
cs_zone_facts:
name: ch-gva-1
register: zone
delegate_to: localhost
- name: Show the returned results of the registered variable
debug:
var: zone
- name: Show the facts by the ansible_facts key cloudstack_zone
debug:
var: cloudstack_zone
'''
RETURN = '''
---
id:
description: UUID of the zone.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the zone.
returned: success
type: str
sample: zone01
dns1:
description: First DNS for the zone.
returned: success
type: str
sample: 8.8.8.8
dns2:
description: Second DNS for the zone.
returned: success
type: str
sample: 8.8.4.4
internal_dns1:
description: First internal DNS for the zone.
returned: success
type: str
sample: 8.8.8.8
internal_dns2:
description: Second internal DNS for the zone.
returned: success
type: str
sample: 8.8.4.4
dns1_ipv6:
description: First IPv6 DNS for the zone.
returned: success
type: str
sample: "2001:4860:4860::8888"
dns2_ipv6:
description: Second IPv6 DNS for the zone.
returned: success
type: str
sample: "2001:4860:4860::8844"
allocation_state:
description: State of the zone.
returned: success
type: str
sample: Enabled
domain:
description: Domain the zone is related to.
returned: success
type: str
sample: ROOT
network_domain:
description: Network domain for the zone.
returned: success
type: str
sample: example.com
network_type:
description: Network type for the zone.
returned: success
type: str
sample: basic
local_storage_enabled:
description: Local storage offering enabled.
returned: success
type: bool
sample: false
securitygroups_enabled:
description: Security groups support is enabled.
returned: success
type: bool
sample: false
guest_cidr_address:
description: Guest CIDR address for the zone
returned: success
type: str
sample: 10.1.1.0/24
dhcp_provider:
description: DHCP provider for the zone
returned: success
type: str
sample: VirtualRouter
zone_token:
description: Zone token
returned: success
type: str
sample: ccb0a60c-79c8-3230-ab8b-8bdbe8c45bb7
tags:
description: List of resource tags associated with the zone.
returned: success
type: dict
sample: [ { "key": "foo", "value": "bar" } ]
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
)
class AnsibleCloudStackZoneFacts(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackZoneFacts, self).__init__(module)
self.returns = {
'dns1': 'dns1',
'dns2': 'dns2',
'internaldns1': 'internal_dns1',
'internaldns2': 'internal_dns2',
'ipv6dns1': 'dns1_ipv6',
'ipv6dns2': 'dns2_ipv6',
'domain': 'network_domain',
'networktype': 'network_type',
'securitygroupsenabled': 'securitygroups_enabled',
'localstorageenabled': 'local_storage_enabled',
'guestcidraddress': 'guest_cidr_address',
'dhcpprovider': 'dhcp_provider',
'allocationstate': 'allocation_state',
'zonetoken': 'zone_token',
}
def get_zone(self):
return super(AnsibleCloudStackZoneFacts, self).get_zone()
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
zone=dict(required=True, aliases=['name']),
))
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)
acs_zone_facts = AnsibleCloudStackZoneFacts(module=module)
result = acs_zone_facts.get_result_and_facts(
facts_name='cloudstack_zone',
resource=acs_zone_facts.get_zone()
)
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1,213 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016, René Moser <mail@renemoser.net>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: cs_zone_info
short_description: Gathering information about zones from Apache CloudStack based clouds.
description:
- Gathering information from the API of a zone.
author: René Moser (@resmo)
options:
zone:
description:
- Name of the zone.
- If not specified, all zones are returned
type: str
aliases: [ name ]
extends_documentation_fragment:
- community.general.cloudstack
'''
EXAMPLES = '''
- name: Gather information from a zone
cs_zone_info:
zone: ch-gva-1
register: zone
delegate_to: localhost
- name: Show the returned results of the registered variable
debug:
msg: "{{ zone }}"
- name: Gather information from all zones
cs_zone_info:
register: zones
delegate_to: localhost
- name: Show information on all zones
debug:
msg: "{{ zones }}"
'''
RETURN = '''
---
zones:
description: A list of matching zones.
type: list
returned: success
contains:
id:
description: UUID of the zone.
returned: success
type: str
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the zone.
returned: success
type: str
sample: zone01
dns1:
description: First DNS for the zone.
returned: success
type: str
sample: 8.8.8.8
dns2:
description: Second DNS for the zone.
returned: success
type: str
sample: 8.8.4.4
internal_dns1:
description: First internal DNS for the zone.
returned: success
type: str
sample: 8.8.8.8
internal_dns2:
description: Second internal DNS for the zone.
returned: success
type: str
sample: 8.8.4.4
dns1_ipv6:
description: First IPv6 DNS for the zone.
returned: success
type: str
sample: "2001:4860:4860::8888"
dns2_ipv6:
description: Second IPv6 DNS for the zone.
returned: success
type: str
sample: "2001:4860:4860::8844"
allocation_state:
description: State of the zone.
returned: success
type: str
sample: Enabled
domain:
description: Domain the zone is related to.
returned: success
type: str
sample: ROOT
network_domain:
description: Network domain for the zone.
returned: success
type: str
sample: example.com
network_type:
description: Network type for the zone.
returned: success
type: str
sample: basic
local_storage_enabled:
description: Local storage offering enabled.
returned: success
type: bool
sample: false
securitygroups_enabled:
description: Security groups support is enabled.
returned: success
type: bool
sample: false
guest_cidr_address:
description: Guest CIDR address for the zone
returned: success
type: str
sample: 10.1.1.0/24
dhcp_provider:
description: DHCP provider for the zone
returned: success
type: str
sample: VirtualRouter
zone_token:
description: Zone token
returned: success
type: str
sample: ccb0a60c-79c8-3230-ab8b-8bdbe8c45bb7
tags:
description: List of resource tags associated with the zone.
returned: success
type: dict
sample: [ { "key": "foo", "value": "bar" } ]
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cloudstack import (
AnsibleCloudStack,
cs_argument_spec,
)
class AnsibleCloudStackZoneInfo(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackZoneInfo, self).__init__(module)
self.returns = {
'dns1': 'dns1',
'dns2': 'dns2',
'internaldns1': 'internal_dns1',
'internaldns2': 'internal_dns2',
'ipv6dns1': 'dns1_ipv6',
'ipv6dns2': 'dns2_ipv6',
'domain': 'network_domain',
'networktype': 'network_type',
'securitygroupsenabled': 'securitygroups_enabled',
'localstorageenabled': 'local_storage_enabled',
'guestcidraddress': 'guest_cidr_address',
'dhcpprovider': 'dhcp_provider',
'allocationstate': 'allocation_state',
'zonetoken': 'zone_token',
}
def get_zone(self):
if self.module.params['zone']:
zones = [super(AnsibleCloudStackZoneInfo, self).get_zone()]
else:
zones = self.query_api('listZones')
if zones:
zones = zones['zone']
else:
zones = []
return {
'zones': [self.update_result(resource) for resource in zones]
}
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
zone=dict(type='str', aliases=['name']),
))
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)
acs_zone_info = AnsibleCloudStackZoneInfo(module=module)
result = acs_zone_info.get_zone()
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_account.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_affinitygroup.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_cluster.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_configuration.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_disk_offering.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_domain.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_facts.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_firewall.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_host.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_image_store.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_instance.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_instance_facts.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_instance_info.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_instance_nic.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_instance_nic_secondaryip.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_instance_password_reset.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_instancegroup.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_ip_address.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_iso.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_loadbalancer_rule.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_loadbalancer_rule_member.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_network.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_network_acl.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_network_acl_rule.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_network_offering.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_physical_network.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_pod.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_portforward.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_project.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_region.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_resourcelimit.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_role.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_role_permission.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_router.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_securitygroup.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_securitygroup_rule.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_service_offering.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_snapshot_policy.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_sshkeypair.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_staticnat.py

View file

@ -1 +0,0 @@
./cloud/cloudstack/cs_storage_pool.py

Some files were not shown because too many files have changed in this diff Show more