summaryrefslogtreecommitdiffstats
path: root/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 20:03:01 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 20:03:01 +0000
commita453ac31f3428614cceb99027f8efbdb9258a40b (patch)
treef61f87408f32a8511cbd91799f9cececb53e0374 /collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins
parentInitial commit. (diff)
downloadansible-a453ac31f3428614cceb99027f8efbdb9258a40b.tar.xz
ansible-a453ac31f3428614cceb99027f8efbdb9258a40b.zip
Adding upstream version 2.10.7+merged+base+2.10.8+dfsg.upstream/2.10.7+merged+base+2.10.8+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins')
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/doc_fragments/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/doc_fragments/cloudstack.py78
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/inventory/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/module_utils/cloudstack.py685
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_account.py450
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_affinitygroup.py230
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_cluster.py375
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_configuration.py270
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_disk_offering.py375
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_domain.py244
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_facts.py231
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_firewall.py439
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_host.py607
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_image_store.py246
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance.py1101
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_info.py370
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_nic.py285
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_nic_secondaryip.py267
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_password_reset.py151
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instancegroup.py181
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_ip_address.py279
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_iso.py436
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_loadbalancer_rule.py371
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_loadbalancer_rule_member.py345
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network.py633
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_acl.py197
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_acl_rule.py457
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_offering.py422
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_physical_network.py473
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_pod.py289
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_portforward.py398
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_project.py271
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_region.py187
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_resourcelimit.py202
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_role.py205
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_role_permission.py345
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_router.py368
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_securitygroup.py193
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_securitygroup_rule.py379
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_service_offering.py574
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_snapshot_policy.py349
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_sshkeypair.py261
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_staticnat.py249
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_storage_pool.py489
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_template.py741
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_traffic_type.py321
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_user.py436
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vlan_ip_range.py397
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vmsnapshot.py278
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_volume.py561
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpc.py391
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpc_offering.py319
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_connection.py349
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_customer_gateway.py343
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_gateway.py204
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_zone.py377
-rw-r--r--collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_zone_info.py207
58 files changed, 19881 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/doc_fragments/__init__.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/doc_fragments/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/doc_fragments/__init__.py
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/doc_fragments/cloudstack.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/doc_fragments/cloudstack.py
new file mode 100644
index 00000000..af13a3af
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/doc_fragments/cloudstack.py
@@ -0,0 +1,78 @@
+# -*- 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
+ api_verify_ssl_cert:
+ description:
+ - CA authority cert file.
+ - If not given, the C(CLOUDSTACK_VERIFY) 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(null) if not specified.
+ type: str
+requirements:
+ - python >= 2.6
+ - cs >= 0.9.0
+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.
+'''
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/inventory/__init__.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/inventory/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/inventory/__init__.py
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/module_utils/cloudstack.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/module_utils/cloudstack.py
new file mode 100644
index 00000000..ac310ecb
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/module_utils/cloudstack.py
@@ -0,0 +1,685 @@
+# -*- 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'),
+ api_verify_ssl_cert=dict(default=os.environ.get('CLOUDSTACK_VERIFY')),
+ )
+
+
+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',
+ 'verify': self.module.params.get('api_verify_ssl_cert') or config.get('verify'),
+ }
+ 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'],
+ 'api_verify_ssl_cert': api_config['verify'],
+ })
+ 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_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 in zone %s" % (self.module.params.get('pod'), self.get_zone(key='name')))
+
+ 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.module.deprecate(
+ msg="Using first zone as default is deprecated because of unreliable API, zone needs to be defined.",
+ version="2.0.0",
+ collection_name="ngine_io.cloudstack"
+ )
+ 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
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/__init__.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/__init__.py
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_account.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_account.py
new file mode 100644
index 00000000..694f62d7
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_account.py
@@ -0,0 +1,450 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+
+'''
+
+EXAMPLES = '''
+- name: create an account in domain 'CUSTOMERS'
+ ngine_io.cloudstack.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
+
+- name: Lock an existing account in domain 'CUSTOMERS'
+ ngine_io.cloudstack.cs_account:
+ name: customer_xy
+ domain: CUSTOMERS
+ state: locked
+
+- name: Disable an existing account in domain 'CUSTOMERS'
+ ngine_io.cloudstack.cs_account:
+ name: customer_xy
+ domain: CUSTOMERS
+ state: disabled
+
+- name: Enable an existing account in domain 'CUSTOMERS'
+ ngine_io.cloudstack.cs_account:
+ name: customer_xy
+ domain: CUSTOMERS
+ state: enabled
+
+- name: Remove an account in domain 'CUSTOMERS'
+ ngine_io.cloudstack.cs_account:
+ name: customer_xy
+ domain: CUSTOMERS
+ state: absent
+
+- name: Create a single user LDAP account in domain 'CUSTOMERS'
+ ngine_io.cloudstack.cs_account:
+ name: customer_xy
+ username: customer_xy
+ domain: CUSTOMERS
+ ldap_domain: cn=customer_xy,cn=team_xy,ou=People,dc=domain,dc=local
+
+- name: Create a LDAP account in domain 'CUSTOMERS' and bind it to a LDAP group
+ ngine_io.cloudstack.cs_account:
+ name: team_xy
+ username: customer_xy
+ domain: CUSTOMERS
+ ldap_domain: cn=team_xy,ou=People,dc=domain,dc=local
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_affinitygroup.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_affinitygroup.py
new file mode 100644
index 00000000..46c1b176
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_affinitygroup.py
@@ -0,0 +1,230 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_affinitygroup
+short_description: Manages affinity groups on Apache CloudStack based clouds.
+description:
+ - Create and remove affinity groups.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+
+'''
+
+EXAMPLES = '''
+- name: Create a affinity group
+ ngine_io.cloudstack.cs_affinitygroup:
+ name: haproxy
+ affinity_type: host anti-affinity
+
+- name: Remove a affinity group
+ ngine_io.cloudstack.cs_affinitygroup:
+ name: haproxy
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_cluster.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_cluster.py
new file mode 100644
index 00000000..88ea35f2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_cluster.py
@@ -0,0 +1,375 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_cluster
+short_description: Manages host clusters on Apache CloudStack based clouds.
+description:
+ - Create, update and remove clusters.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Ensure a cluster is present
+ ngine_io.cloudstack.cs_cluster:
+ name: kvm-cluster-01
+ zone: ch-zrh-ix-01
+ hypervisor: KVM
+ cluster_type: CloudManaged
+
+- name: Ensure a cluster is disabled
+ ngine_io.cloudstack.cs_cluster:
+ name: kvm-cluster-01
+ zone: ch-zrh-ix-01
+ state: disabled
+
+- name: Ensure a cluster is enabled
+ ngine_io.cloudstack.cs_cluster:
+ name: kvm-cluster-01
+ zone: ch-zrh-ix-01
+ state: enabled
+
+- name: Ensure a cluster is absent
+ ngine_io.cloudstack.cs_cluster:
+ name: kvm-cluster-01
+ zone: ch-zrh-ix-01
+ state: absent
+'''
+
+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 ..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_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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_configuration.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_configuration.py
new file mode 100644
index 00000000..dd11d932
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_configuration.py
@@ -0,0 +1,270 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Ensure global configuration
+ ngine_io.cloudstack.cs_configuration:
+ name: router.reboot.when.outofband.migrated
+ value: false
+
+- name: Ensure zone configuration
+ ngine_io.cloudstack.cs_configuration:
+ name: router.reboot.when.outofband.migrated
+ zone: ch-gva-01
+ value: true
+
+- name: Ensure storage configuration
+ ngine_io.cloudstack.cs_configuration:
+ name: storage.overprovisioning.factor
+ storage: storage01
+ value: 2.0
+
+- name: Ensure account configuration
+ ngine_io.cloudstack.cs_configuration:
+ name: allow.public.user.templates
+ value: false
+ account: acme inc
+ domain: customers
+'''
+
+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 ..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()
+ empty_value = args['value'] in [None, ''] and 'value' not in configuration
+ if self.has_changed(args, configuration, ['value']) and not empty_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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_disk_offering.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_disk_offering.py
new file mode 100644
index 00000000..b9bd4570
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_disk_offering.py
@@ -0,0 +1,375 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, 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
+
+
+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)
+version_added: 0.1.0
+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
+ 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
+ elements: str
+ 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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create a disk offering with local storage
+ ngine_io.cloudstack.cs_disk_offering:
+ name: small
+ display_text: Small 10GB
+ disk_size: 10
+ storage_type: local
+
+- name: Create or update a disk offering with shared storage
+ ngine_io.cloudstack.cs_disk_offering:
+ name: small
+ display_text: Small 10GB
+ disk_size: 10
+ storage_type: shared
+ storage_tags: SAN01
+
+- name: Remove a disk offering
+ ngine_io.cloudstack.cs_disk_offering:
+ name: small
+ state: absent
+'''
+
+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 ..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', elements='str', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_domain.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_domain.py
new file mode 100644
index 00000000..68177164
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_domain.py
@@ -0,0 +1,244 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_domain
+short_description: Manages domains on Apache CloudStack based clouds.
+description:
+ - Create, update and remove domains.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create a domain
+ ngine_io.cloudstack.cs_domain:
+ path: ROOT/customers
+ network_domain: customers.example.com
+
+- name: Create another subdomain
+ ngine_io.cloudstack.cs_domain:
+ path: ROOT/customers/xy
+ network_domain: xy.customers.example.com
+
+- name: Remove a domain
+ ngine_io.cloudstack.cs_domain:
+ path: ROOT/customers/xy
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_facts.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_facts.py
new file mode 100644
index 00000000..e60d5e80
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_facts.py
@@ -0,0 +1,231 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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
+ ngine_io.cloudstack.cs_facts:
+
+# Gather specific fact on instances
+- name: Gather cloudstack facts
+ ngine_io.cloudstack.cs_facts: filter=cloudstack_instance_id
+
+# Gather specific fact on instances with a given meta_data_host
+- name: Gather cloudstack facts
+ ngine_io.cloudstack.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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_firewall.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_firewall.py
new file mode 100644
index 00000000..f8878877
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_firewall.py
@@ -0,0 +1,439 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_firewall
+short_description: Manages firewall rules on Apache CloudStack based clouds.
+description:
+ - Creates and removes firewall rules.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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.
+ elements: str
+ 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
+ elements: dict
+ aliases: [ tag ]
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Allow inbound port 80/tcp from 1.2.3.4 to 4.3.2.1
+ ngine_io.cloudstack.cs_firewall:
+ ip_address: 4.3.2.1
+ port: 80
+ cidr: 1.2.3.4/32
+
+- name: Allow inbound tcp/udp port 53 to 4.3.2.1
+ ngine_io.cloudstack.cs_firewall:
+ ip_address: 4.3.2.1
+ port: 53
+ protocol: '{{ item }}'
+ with_items:
+ - tcp
+ - udp
+
+- name: Ensure firewall rule is removed
+ ngine_io.cloudstack.cs_firewall:
+ ip_address: 4.3.2.1
+ start_port: 8000
+ end_port: 8888
+ cidr: 17.0.0.0/8
+ state: absent
+
+- name: Allow all outbound traffic
+ ngine_io.cloudstack.cs_firewall:
+ network: my_network
+ type: egress
+ protocol: all
+
+- name: Allow only HTTP outbound traffic for an IP
+ ngine_io.cloudstack.cs_firewall:
+ network: my_network
+ type: egress
+ port: 80
+ cidr: 10.101.1.20
+'''
+
+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 ..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', elements='str', 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', elements='dict', aliases=['tag']),
+ ))
+
+ 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_host.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_host.py
new file mode 100644
index 00000000..111b4e0e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_host.py
@@ -0,0 +1,607 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_host
+short_description: Manages hosts on Apache CloudStack based clouds.
+description:
+ - Create, update and remove hosts.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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
+ elements: str
+ 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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Ensure a host is present but disabled
+ ngine_io.cloudstack.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
+
+- name: Ensure an existing host is disabled
+ ngine_io.cloudstack.cs_host:
+ name: pod01.zone01.example.com
+ zone: zone01
+ allocation_state: disabled
+
+- name: Ensure an existing host is enabled
+ ngine_io.cloudstack.cs_host:
+ name: pod01.zone01.example.com
+ zone: zone01
+ allocation_state: enabled
+
+- name: Ensure a host is absent
+ ngine_io.cloudstack.cs_host:
+ name: pod01.zone01.example.com
+ zone: zone01
+ state: absent
+'''
+
+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 ..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_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', elements='str', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_image_store.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_image_store.py
new file mode 100644
index 00000000..b97d6e26
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_image_store.py
@@ -0,0 +1,246 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_image_store
+
+short_description: Manages CloudStack Image Stores.
+
+
+description:
+ - Deploy, remove, recreate CloudStack Image Stores.
+
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+
+
+author:
+ - Patryk Cichy (@PatTheSilent)
+'''
+
+EXAMPLES = '''
+- name: Add a Image Store (NFS)
+ ngine_io.cloudstack.cs_image_store:
+ zone: zone-01
+ name: nfs-01
+ provider: NFS
+ url: nfs://192.168.21.16/exports/secondary
+
+
+# Change the NFS share URL and force a Image Store recreation
+- name: Change the NFS url
+ ngine_io.cloudstack.cs_image_store:
+ zone: zone-01
+ name: nfs-01
+ provider: NFS
+ force_recreate: yes
+ url: nfs://192.168.21.10/shares/secondary
+
+- name: delete the image store
+ ngine_io.cloudstack.cs_image_store:
+ name: nfs-01
+ zone: zone-01
+ state: absent
+
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance.py
new file mode 100644
index 00000000..97a0ef93
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance.py
@@ -0,0 +1,1101 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_instance
+short_description: Manages instances and virtual machines on Apache CloudStack based clouds.
+description:
+ - Deploy, start, update, scale, restart, restore, stop and destroy instances.
+author: René Moser (@resmo)
+version_added: 0.1.0
+options:
+ name:
+ description:
+ - Host name of the instance. C(name) can only contain ASCII letters.
+ - Name will be generated (UUID) by CloudStack if not specified and can not be changed afterwards.
+ - Either C(name) or C(display_name) is required.
+ type: str
+ display_name:
+ description:
+ - Custom display name of the instances.
+ - Display name will be set to I(name) if not specified.
+ - Either I(name) or I(display_name) is required.
+ type: str
+ group:
+ description:
+ - Group in where the new instance should be in.
+ type: str
+ state:
+ description:
+ - State of the instance.
+ type: str
+ default: present
+ choices: [ deployed, started, stopped, restarted, restored, destroyed, expunged, present, absent ]
+ service_offering:
+ description:
+ - Name or id of the service offering of the new instance.
+ - If not set, first found service offering is used.
+ type: str
+ cpu:
+ description:
+ - The number of CPUs to allocate to the instance, used with custom service offerings
+ type: int
+ cpu_speed:
+ description:
+ - The clock speed/shares allocated to the instance, used with custom service offerings
+ type: int
+ memory:
+ description:
+ - The memory allocated to the instance, used with custom service offerings
+ type: int
+ template:
+ description:
+ - Name, display text or id of the template to be used for creating the new instance.
+ - Required when using I(state=present).
+ - Mutually exclusive with I(iso) option.
+ type: str
+ iso:
+ description:
+ - Name or id of the ISO to be used for creating the new instance.
+ - Required when using I(state=present).
+ - Mutually exclusive with I(template) option.
+ type: str
+ template_filter:
+ description:
+ - Name of the filter used to search for the template or iso.
+ - Used for params I(iso) or I(template) on I(state=present).
+ - The filter C(all) was added in 2.6.
+ type: str
+ default: executable
+ choices: [ all, featured, self, selfexecutable, sharedexecutable, executable, community ]
+ aliases: [ iso_filter ]
+ hypervisor:
+ description:
+ - Name the hypervisor to be used for creating the new instance.
+ - Relevant when using I(state=present), but only considered if not set on ISO/template.
+ - If not set or found on ISO/template, first found hypervisor will be used.
+ - Possible values are C(KVM), C(VMware), C(BareMetal), C(XenServer), C(LXC), C(HyperV), C(UCS), C(OVM), C(Simulator).
+ type: str
+ keyboard:
+ description:
+ - Keyboard device type for the instance.
+ type: str
+ choices: [ 'de', 'de-ch', 'es', 'fi', 'fr', 'fr-be', 'fr-ch', 'is', 'it', 'jp', 'nl-be', 'no', 'pt', 'uk', 'us' ]
+ networks:
+ description:
+ - List of networks to use for the new instance.
+ type: list
+ elements: str
+ aliases: [ network ]
+ ip_address:
+ description:
+ - IPv4 address for default instance's network during creation.
+ type: str
+ ip6_address:
+ description:
+ - IPv6 address for default instance's network.
+ type: str
+ ip_to_networks:
+ description:
+ - "List of mappings in the form I({'network': NetworkName, 'ip': 1.2.3.4})"
+ - Mutually exclusive with I(networks) option.
+ type: list
+ elements: dict
+ aliases: [ ip_to_network ]
+ disk_offering:
+ description:
+ - Name of the disk offering to be used.
+ type: str
+ disk_size:
+ description:
+ - Disk size in GByte required if deploying instance from ISO.
+ type: int
+ root_disk_size:
+ description:
+ - Root disk size in GByte required if deploying instance with KVM hypervisor and want resize the root disk size at startup
+ (need CloudStack >= 4.4, cloud-initramfs-growroot installed and enabled in the template)
+ type: int
+ security_groups:
+ description:
+ - List of security groups the instance to be applied to.
+ type: list
+ elements: str
+ aliases: [ security_group ]
+ host:
+ description:
+ - Host on which an instance should be deployed or started on.
+ - Only considered when I(state=started) or instance is running.
+ - Requires root admin privileges.
+ 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 to be deployed in.
+ type: str
+ zone:
+ description:
+ - Name of the zone in which the instance should be deployed.
+ - If not set, default zone is used.
+ type: str
+ ssh_key:
+ description:
+ - Name of the SSH key to be deployed on the new instance.
+ type: str
+ affinity_groups:
+ description:
+ - Affinity groups names to be applied to the new instance.
+ type: list
+ elements: str
+ aliases: [ affinity_group ]
+ user_data:
+ description:
+ - Optional data (ASCII) that can be sent to the instance upon a successful deployment.
+ - The data will be automatically base64 encoded.
+ - Consider switching to HTTP_POST by using I(CLOUDSTACK_METHOD=post) to increase the HTTP_GET size limit of 2KB to 32 KB.
+ type: str
+ force:
+ description:
+ - Force stop/start the instance if required to apply changes, otherwise a running instance will not be changed.
+ type: bool
+ default: no
+ allow_root_disk_shrink:
+ description:
+ - Enables a volume shrinkage when the new size is smaller than the old one.
+ type: bool
+ default: no
+ tags:
+ description:
+ - List of tags. Tags are a list of dictionaries having keys C(key) and C(value).
+ - "If you want to delete all tags, set a empty list e.g. I(tags: [])."
+ type: list
+ elements: dict
+ aliases: [ tag ]
+ poll_async:
+ description:
+ - Poll async jobs until job has finished.
+ type: bool
+ default: yes
+ details:
+ description:
+ - Map to specify custom parameters.
+ type: dict
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+# NOTE: Names of offerings and ISOs depending on the CloudStack configuration.
+- name: create a instance from an ISO
+ ngine_io.cloudstack.cs_instance:
+ name: web-vm-1
+ iso: Linux Debian 7 64-bit
+ hypervisor: VMware
+ project: Integration
+ zone: ch-zrh-ix-01
+ service_offering: 1cpu_1gb
+ disk_offering: PerfPlus Storage
+ disk_size: 20
+ networks:
+ - Server Integration
+ - Sync Integration
+ - Storage Integration
+
+- name: for changing a running instance, use the 'force' parameter
+ ngine_io.cloudstack.cs_instance:
+ name: web-vm-1
+ display_name: web-vm-01.example.com
+ iso: Linux Debian 7 64-bit
+ service_offering: 2cpu_2gb
+ force: yes
+
+# NOTE: user_data can be used to kickstart the instance using cloud-init yaml config.
+- name: create or update a instance on Exoscale's public cloud using display_name.
+ ngine_io.cloudstack.cs_instance:
+ display_name: web-vm-1
+ template: Linux Debian 7 64-bit
+ service_offering: Tiny
+ ssh_key: john@example.com
+ tags:
+ - key: admin
+ value: john
+ - key: foo
+ value: bar
+ user_data: |
+ #cloud-config
+ packages:
+ - nginx
+
+- name: create an instance with multiple interfaces specifying the IP addresses
+ ngine_io.cloudstack.cs_instance:
+ name: web-vm-1
+ template: Linux Debian 7 64-bit
+ service_offering: Tiny
+ ip_to_networks:
+ - network: NetworkA
+ ip: 10.1.1.1
+ - network: NetworkB
+ ip: 192.0.2.1
+
+- name: ensure an instance is stopped
+ ngine_io.cloudstack.cs_instance:
+ name: web-vm-1
+ state: stopped
+
+- name: ensure an instance is running
+ ngine_io.cloudstack.cs_instance:
+ name: web-vm-1
+ state: started
+
+- name: remove an instance
+ ngine_io.cloudstack.cs_instance:
+ name: web-vm-1
+ state: absent
+'''
+
+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: if available
+ type: str
+ sample: Ge2oe7Do
+ssh_key:
+ description: Name of SSH key deployed to instance.
+ returned: if available
+ 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
+default_ip6:
+ description: Default IPv6 address of the instance.
+ returned: if available
+ type: str
+ sample: 2a04:c43:c00:a07:4b4:beff:fe00:74
+public_ip:
+ description: Public IP address with instance via static NAT rule.
+ returned: if available
+ type: str
+ sample: 1.2.3.4
+iso:
+ description: Name of ISO the instance was deployed with.
+ returned: if available
+ type: str
+ sample: Debian-8-64bit
+template:
+ description: Name of template the instance was deployed with.
+ returned: success
+ type: str
+ sample: Linux Debian 9 64-bit
+template_display_text:
+ description: Display text of template the instance was deployed with.
+ returned: success
+ type: str
+ sample: Linux Debian 9 64-bit 200G Disk (2017-10-08-622866)
+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: Hostname of hypervisor an instance is running on.
+ returned: success and instance is running
+ type: str
+ sample: host-01.example.com
+instance_name:
+ description: Internal name of the instance (ROOT admin only).
+ returned: success
+ type: str
+ sample: i-44-3992-VM
+user-data:
+ description: Optional data sent to the instance.
+ returned: success
+ type: str
+ sample: VXNlciBkYXRhIGV4YW1wbGUK
+'''
+
+import base64
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils._text import to_bytes, to_text
+from ..module_utils.cloudstack import (
+ AnsibleCloudStack,
+ cs_argument_spec,
+ cs_required_together
+)
+
+
+class AnsibleCloudStackInstance(AnsibleCloudStack):
+
+ def __init__(self, module):
+ super(AnsibleCloudStackInstance, 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',
+ 'templatedisplaytext': 'template_display_text',
+ 'keypair': 'ssh_key',
+ 'hostname': 'host',
+ }
+ self.instance = None
+ self.template = None
+ self.iso = None
+
+ def get_service_offering_id(self):
+ service_offering = self.module.params.get('service_offering')
+
+ service_offerings = self.query_api('listServiceOfferings')
+ if service_offerings:
+ if not service_offering:
+ return service_offerings['serviceoffering'][0]['id']
+
+ 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_host_id(self):
+ host_name = self.module.params.get('host')
+ if not host_name:
+ return None
+
+ args = {
+ 'type': 'routing',
+ 'zoneid': self.get_zone(key='id'),
+ }
+ hosts = self.query_api('listHosts', **args)
+ if hosts:
+ for h in hosts['host']:
+ if h['name'] == host_name:
+ return h['id']
+
+ self.fail_json(msg="Host '%s' not found" % host_name)
+
+ def get_template_or_iso(self, key=None):
+ template = self.module.params.get('template')
+ iso = self.module.params.get('iso')
+
+ if not template and not iso:
+ 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'),
+ 'isrecursive': True,
+ 'fetch_list': True,
+ }
+
+ if template:
+ if self.template:
+ return self._get_by_key(key, self.template)
+
+ rootdisksize = self.module.params.get('root_disk_size')
+ args['templatefilter'] = self.module.params.get('template_filter')
+ args['fetch_list'] = True
+ templates = self.query_api('listTemplates', **args)
+ if templates:
+ for t in templates:
+ if template in [t.get('displaytext', None), t['name'], t['id']]:
+ if rootdisksize and t['size'] > rootdisksize * 1024 ** 3:
+ continue
+ self.template = t
+ return self._get_by_key(key, self.template)
+
+ if rootdisksize:
+ more_info = " (with size <= %s)" % rootdisksize
+ else:
+ more_info = ""
+
+ self.module.fail_json(msg="Template '%s' not found%s" % (template, more_info))
+
+ elif iso:
+ if self.iso:
+ return self._get_by_key(key, self.iso)
+
+ args['isofilter'] = self.module.params.get('template_filter')
+ args['fetch_list'] = True
+ isos = self.query_api('listIsos', **args)
+ if isos:
+ for i in isos:
+ if iso in [i['displaytext'], i['name'], i['id']]:
+ self.iso = i
+ return self._get_by_key(key, self.iso)
+
+ self.module.fail_json(msg="ISO '%s' not found" % iso)
+
+ def get_instance(self):
+ instance = self.instance
+ if not instance:
+ instance_name = self.get_or_fallback('name', 'display_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_instance_user_data(self, instance):
+ # Query the user data if we need to
+ if 'userdata' in instance:
+ return instance['userdata']
+
+ user_data = ""
+ if self.get_user_data() is not None and instance.get('id'):
+ res = self.query_api('getVirtualMachineUserData', virtualmachineid=instance['id'])
+ user_data = res['virtualmachineuserdata'].get('userdata', "")
+ return user_data
+
+ def get_iptonetwork_mappings(self):
+ network_mappings = self.module.params.get('ip_to_networks')
+ if network_mappings is None:
+ return
+
+ if network_mappings and self.module.params.get('networks'):
+ self.module.fail_json(msg="networks and ip_to_networks are mutually exclusive.")
+
+ network_names = [n['network'] for n in network_mappings]
+ ids = self.get_network_ids(network_names)
+ res = []
+ for i, data in enumerate(network_mappings):
+ res.append({'networkid': ids[i], 'ip': data['ip']})
+ return res
+
+ def get_ssh_keypair(self, key=None, name=None, fail_on_missing=True):
+ ssh_key_name = name or self.module.params.get('ssh_key')
+ if ssh_key_name is None:
+ return
+
+ args = {
+ 'domainid': self.get_domain('id'),
+ 'account': self.get_account('name'),
+ 'projectid': self.get_project('id'),
+ 'name': ssh_key_name,
+ }
+ ssh_key_pairs = self.query_api('listSSHKeyPairs', **args)
+ if 'sshkeypair' in ssh_key_pairs:
+ return self._get_by_key(key=key, my_dict=ssh_key_pairs['sshkeypair'][0])
+
+ elif fail_on_missing:
+ self.module.fail_json(msg="SSH key not found: %s" % ssh_key_name)
+
+ def ssh_key_has_changed(self):
+ ssh_key_name = self.module.params.get('ssh_key')
+ if ssh_key_name is None:
+ return False
+
+ # Fails if keypair for param is inexistent
+ param_ssh_key_fp = self.get_ssh_keypair(key='fingerprint')
+
+ # CloudStack 4.5 does return keypair on instance for a non existent key.
+ instance_ssh_key_name = self.instance.get('keypair')
+ if instance_ssh_key_name is None:
+ return True
+
+ # Get fingerprint for keypair of instance but do not fail if inexistent.
+ instance_ssh_key_fp = self.get_ssh_keypair(key='fingerprint', name=instance_ssh_key_name, fail_on_missing=False)
+ if not instance_ssh_key_fp:
+ return True
+
+ # Compare fingerprints to ensure the keypair changed
+ if instance_ssh_key_fp != param_ssh_key_fp:
+ return True
+ return False
+
+ def security_groups_has_changed(self):
+ security_groups = self.module.params.get('security_groups')
+ if security_groups is None:
+ return False
+
+ security_groups = [s.lower() for s in security_groups]
+ instance_security_groups = self.instance.get('securitygroup') or []
+
+ instance_security_group_names = []
+ for instance_security_group in instance_security_groups:
+ if instance_security_group['name'].lower() not in security_groups:
+ return True
+ else:
+ instance_security_group_names.append(instance_security_group['name'].lower())
+
+ for security_group in security_groups:
+ if security_group not in instance_security_group_names:
+ return True
+ return False
+
+ def get_network_ids(self, network_names=None):
+ if network_names is None:
+ network_names = self.module.params.get('networks')
+
+ if not network_names:
+ 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'),
+ 'fetch_list': True,
+ }
+ networks = self.query_api('listNetworks', **args)
+ if not networks:
+ self.module.fail_json(msg="No networks available")
+
+ network_ids = []
+ network_displaytexts = []
+ for network_name in network_names:
+ for n in networks:
+ if network_name in [n['displaytext'], n['name'], n['id']]:
+ network_ids.append(n['id'])
+ network_displaytexts.append(n['name'])
+ break
+
+ if len(network_ids) != len(network_names):
+ self.module.fail_json(msg="Could not find all networks, networks list found: %s" % network_displaytexts)
+
+ return network_ids
+
+ def present_instance(self, start_vm=True):
+ instance = self.get_instance()
+
+ if not instance:
+ instance = self.deploy_instance(start_vm=start_vm)
+ else:
+ instance = self.recover_instance(instance=instance)
+ instance = self.update_instance(instance=instance, start_vm=start_vm)
+
+ # In check mode, we do not necessarily have an instance
+ if instance:
+ instance = self.ensure_tags(resource=instance, resource_type='UserVm')
+ # refresh instance data
+ self.instance = instance
+
+ return instance
+
+ def get_user_data(self):
+ user_data = self.module.params.get('user_data')
+ if user_data is not None:
+ user_data = to_text(base64.b64encode(to_bytes(user_data)))
+ return user_data
+
+ def get_details(self):
+ details = self.module.params.get('details')
+ cpu = self.module.params.get('cpu')
+ cpu_speed = self.module.params.get('cpu_speed')
+ memory = self.module.params.get('memory')
+ if all([cpu, cpu_speed, memory]):
+ details.extends({
+ 'cpuNumber': cpu,
+ 'cpuSpeed': cpu_speed,
+ 'memory': memory,
+ })
+
+ return details
+
+ def deploy_instance(self, start_vm=True):
+ self.result['changed'] = True
+ networkids = self.get_network_ids()
+ if networkids is not None:
+ networkids = ','.join(networkids)
+
+ args = {}
+ args['templateid'] = self.get_template_or_iso(key='id')
+ if not args['templateid']:
+ self.module.fail_json(msg="Template or ISO is required.")
+
+ args['zoneid'] = self.get_zone(key='id')
+ args['serviceofferingid'] = self.get_service_offering_id()
+ args['account'] = self.get_account(key='name')
+ args['domainid'] = self.get_domain(key='id')
+ args['projectid'] = self.get_project(key='id')
+ args['diskofferingid'] = self.get_disk_offering(key='id')
+ args['networkids'] = networkids
+ args['iptonetworklist'] = self.get_iptonetwork_mappings()
+ args['userdata'] = self.get_user_data()
+ args['keyboard'] = self.module.params.get('keyboard')
+ args['ipaddress'] = self.module.params.get('ip_address')
+ args['ip6address'] = self.module.params.get('ip6_address')
+ args['name'] = self.module.params.get('name')
+ args['displayname'] = self.get_or_fallback('display_name', 'name')
+ args['group'] = self.module.params.get('group')
+ args['keypair'] = self.get_ssh_keypair(key='name')
+ args['size'] = self.module.params.get('disk_size')
+ args['startvm'] = start_vm
+ args['rootdisksize'] = self.module.params.get('root_disk_size')
+ args['affinitygroupnames'] = self.module.params.get('affinity_groups')
+ args['details'] = self.get_details()
+ args['securitygroupnames'] = self.module.params.get('security_groups')
+ args['hostid'] = self.get_host_id()
+
+ template_iso = self.get_template_or_iso()
+ if 'hypervisor' not in template_iso:
+ args['hypervisor'] = self.get_hypervisor()
+
+ instance = None
+ if not self.module.check_mode:
+ instance = self.query_api('deployVirtualMachine', **args)
+
+ poll_async = self.module.params.get('poll_async')
+ if poll_async:
+ instance = self.poll_job(instance, 'virtualmachine')
+ return instance
+
+ def update_instance(self, instance, start_vm=True):
+ # Service offering data
+ args_service_offering = {
+ 'id': instance['id'],
+ }
+ if self.module.params.get('service_offering'):
+ args_service_offering['serviceofferingid'] = self.get_service_offering_id()
+ service_offering_changed = self.has_changed(args_service_offering, instance)
+
+ # Instance data
+ args_instance_update = {
+ 'id': instance['id'],
+ 'userdata': self.get_user_data(),
+ }
+ instance['userdata'] = self._get_instance_user_data(instance)
+ args_instance_update['ostypeid'] = self.get_os_type(key='id')
+ if self.module.params.get('group'):
+ args_instance_update['group'] = self.module.params.get('group')
+ if self.module.params.get('display_name'):
+ args_instance_update['displayname'] = self.module.params.get('display_name')
+ instance_changed = self.has_changed(args_instance_update, instance)
+
+ ssh_key_changed = self.ssh_key_has_changed()
+
+ security_groups_changed = self.security_groups_has_changed()
+
+ # Volume data
+ args_volume_update = {}
+ root_disk_size = self.module.params.get('root_disk_size')
+ root_disk_size_changed = False
+
+ if root_disk_size is not None:
+ res = self.query_api('listVolumes', type='ROOT', virtualmachineid=instance['id'])
+ [volume] = res['volume']
+
+ size = volume['size'] >> 30
+
+ args_volume_update['id'] = volume['id']
+ args_volume_update['size'] = root_disk_size
+
+ shrinkok = self.module.params.get('allow_root_disk_shrink')
+ if shrinkok:
+ args_volume_update['shrinkok'] = shrinkok
+
+ root_disk_size_changed = root_disk_size != size
+
+ changed = [
+ service_offering_changed,
+ instance_changed,
+ security_groups_changed,
+ ssh_key_changed,
+ root_disk_size_changed,
+ ]
+
+ if any(changed):
+ force = self.module.params.get('force')
+ instance_state = instance['state'].lower()
+ if instance_state == 'stopped' or force:
+ self.result['changed'] = True
+ if not self.module.check_mode:
+
+ # Ensure VM has stopped
+ instance = self.stop_instance()
+ instance = self.poll_job(instance, 'virtualmachine')
+ self.instance = instance
+
+ # Change service offering
+ if service_offering_changed:
+ res = self.query_api('changeServiceForVirtualMachine', **args_service_offering)
+ instance = res['virtualmachine']
+ self.instance = instance
+
+ # Update VM
+ if instance_changed or security_groups_changed:
+ if security_groups_changed:
+ args_instance_update['securitygroupnames'] = ','.join(self.module.params.get('security_groups'))
+ res = self.query_api('updateVirtualMachine', **args_instance_update)
+ instance = res['virtualmachine']
+ self.instance = instance
+
+ # Reset SSH key
+ if ssh_key_changed:
+ # SSH key data
+ args_ssh_key = {}
+ args_ssh_key['id'] = instance['id']
+ args_ssh_key['projectid'] = self.get_project(key='id')
+ args_ssh_key['keypair'] = self.module.params.get('ssh_key')
+ instance = self.query_api('resetSSHKeyForVirtualMachine', **args_ssh_key)
+ instance = self.poll_job(instance, 'virtualmachine')
+ self.instance = instance
+
+ # Root disk size
+ if root_disk_size_changed:
+ async_result = self.query_api('resizeVolume', **args_volume_update)
+ self.poll_job(async_result, 'volume')
+
+ # Start VM again if it was running before
+ if instance_state == 'running' and start_vm:
+ instance = self.start_instance()
+ else:
+ self.module.warn("Changes won't be applied to running instances. "
+ "Use force=true to allow the instance %s to be stopped/started." % instance['name'])
+
+ # migrate to other host
+ host_changed = all([
+ instance['state'].lower() in ['starting', 'running'],
+ instance.get('hostname') is not None,
+ self.module.params.get('host') is not None,
+ self.module.params.get('host') != instance.get('hostname')
+ ])
+ if host_changed:
+ self.result['changed'] = True
+ args_host = {
+ 'virtualmachineid': instance['id'],
+ 'hostid': self.get_host_id(),
+ }
+ if not self.module.check_mode:
+ res = self.query_api('migrateVirtualMachine', **args_host)
+ instance = self.poll_job(res, 'virtualmachine')
+
+ return instance
+
+ def recover_instance(self, instance):
+ if instance['state'].lower() in ['destroying', 'destroyed']:
+ self.result['changed'] = True
+ if not self.module.check_mode:
+ res = self.query_api('recoverVirtualMachine', id=instance['id'])
+ instance = res['virtualmachine']
+ return instance
+
+ def absent_instance(self):
+ instance = self.get_instance()
+ if instance:
+ if instance['state'].lower() not in ['expunging', 'destroying', 'destroyed']:
+ self.result['changed'] = True
+ if not self.module.check_mode:
+ res = self.query_api('destroyVirtualMachine', id=instance['id'])
+
+ poll_async = self.module.params.get('poll_async')
+ if poll_async:
+ instance = self.poll_job(res, 'virtualmachine')
+ return instance
+
+ def expunge_instance(self):
+ instance = self.get_instance()
+ if instance:
+ res = {}
+ if instance['state'].lower() in ['destroying', 'destroyed']:
+ self.result['changed'] = True
+ if not self.module.check_mode:
+ res = self.query_api('destroyVirtualMachine', id=instance['id'], expunge=True)
+
+ elif instance['state'].lower() not in ['expunging']:
+ self.result['changed'] = True
+ if not self.module.check_mode:
+ res = self.query_api('destroyVirtualMachine', id=instance['id'], expunge=True)
+
+ poll_async = self.module.params.get('poll_async')
+ if poll_async:
+ res = self.poll_job(res, 'virtualmachine')
+ return instance
+
+ def stop_instance(self):
+ instance = self.get_instance()
+ # in check mode instance may not be instantiated
+ if instance:
+ if instance['state'].lower() in ['stopping', 'stopped']:
+ return instance
+
+ if instance['state'].lower() in ['starting', 'running']:
+ self.result['changed'] = True
+ if not self.module.check_mode:
+ instance = self.query_api('stopVirtualMachine', id=instance['id'])
+
+ poll_async = self.module.params.get('poll_async')
+ if poll_async:
+ instance = self.poll_job(instance, 'virtualmachine')
+ return instance
+
+ def start_instance(self):
+ instance = self.get_instance()
+ # in check mode instance may not be instantiated
+ if instance:
+ if instance['state'].lower() in ['starting', 'running']:
+ return instance
+
+ if instance['state'].lower() in ['stopped', 'stopping']:
+ self.result['changed'] = True
+ if not self.module.check_mode:
+ args = {
+ 'id': instance['id'],
+ 'hostid': self.get_host_id(),
+ }
+ instance = self.query_api('startVirtualMachine', **args)
+
+ poll_async = self.module.params.get('poll_async')
+ if poll_async:
+ instance = self.poll_job(instance, 'virtualmachine')
+ return instance
+
+ def restart_instance(self):
+ instance = self.get_instance()
+ # in check mode instance may not be instantiated
+ if instance:
+ if instance['state'].lower() in ['running', 'starting']:
+ self.result['changed'] = True
+ if not self.module.check_mode:
+ instance = self.query_api('rebootVirtualMachine', id=instance['id'])
+
+ poll_async = self.module.params.get('poll_async')
+ if poll_async:
+ instance = self.poll_job(instance, 'virtualmachine')
+
+ elif instance['state'].lower() in ['stopping', 'stopped']:
+ instance = self.start_instance()
+ return instance
+
+ def restore_instance(self):
+ instance = self.get_instance()
+ self.result['changed'] = True
+ # in check mode instance may not be instantiated
+ if instance:
+ args = {}
+ args['templateid'] = self.get_template_or_iso(key='id')
+ args['virtualmachineid'] = instance['id']
+ res = self.query_api('restoreVirtualMachine', **args)
+
+ poll_async = self.module.params.get('poll_async')
+ if poll_async:
+ instance = self.poll_job(res, 'virtualmachine')
+ return instance
+
+ def get_result(self, instance):
+ super(AnsibleCloudStackInstance, self).get_result(instance)
+ if instance:
+ self.result['user_data'] = self._get_instance_user_data(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']:
+ if 'ipaddress' in nic:
+ self.result['default_ip'] = nic['ipaddress']
+ if 'ip6address' in nic:
+ self.result['default_ip6'] = nic['ip6address']
+ return self.result
+
+
+def main():
+ argument_spec = cs_argument_spec()
+ argument_spec.update(dict(
+ name=dict(),
+ display_name=dict(),
+ group=dict(),
+ state=dict(choices=['present', 'deployed', 'started', 'stopped', 'restarted', 'restored', 'absent', 'destroyed', 'expunged'], default='present'),
+ service_offering=dict(),
+ cpu=dict(type='int'),
+ cpu_speed=dict(type='int'),
+ memory=dict(type='int'),
+ template=dict(),
+ iso=dict(),
+ template_filter=dict(
+ default="executable",
+ aliases=['iso_filter'],
+ choices=['all', 'featured', 'self', 'selfexecutable', 'sharedexecutable', 'executable', 'community']
+ ),
+ networks=dict(type='list', elements='str', aliases=['network']),
+ ip_to_networks=dict(type='list', elements='dict', aliases=['ip_to_network']),
+ ip_address=dict(),
+ ip6_address=dict(),
+ disk_offering=dict(),
+ disk_size=dict(type='int'),
+ root_disk_size=dict(type='int'),
+ keyboard=dict(type='str', choices=['de', 'de-ch', 'es', 'fi', 'fr', 'fr-be', 'fr-ch', 'is', 'it', 'jp', 'nl-be', 'no', 'pt', 'uk', 'us']),
+ hypervisor=dict(),
+ host=dict(),
+ security_groups=dict(type='list', elements='str', aliases=['security_group']),
+ affinity_groups=dict(type='list', elements='str', aliases=['affinity_group']),
+ domain=dict(),
+ account=dict(),
+ project=dict(),
+ user_data=dict(),
+ zone=dict(),
+ ssh_key=dict(),
+ force=dict(type='bool', default=False),
+ tags=dict(type='list', elements='dict', aliases=['tag']),
+ details=dict(type='dict'),
+ poll_async=dict(type='bool', default=True),
+ allow_root_disk_shrink=dict(type='bool', default=False),
+ ))
+
+ required_together = cs_required_together()
+ required_together.extend([
+ ['cpu', 'cpu_speed', 'memory'],
+ ])
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_together=required_together,
+ required_one_of=(
+ ['display_name', 'name'],
+ ),
+ mutually_exclusive=(
+ ['template', 'iso'],
+ ),
+ supports_check_mode=True
+ )
+
+ acs_instance = AnsibleCloudStackInstance(module)
+
+ state = module.params.get('state')
+
+ if state in ['absent', 'destroyed']:
+ instance = acs_instance.absent_instance()
+
+ elif state in ['expunged']:
+ instance = acs_instance.expunge_instance()
+
+ elif state in ['restored']:
+ acs_instance.present_instance()
+ instance = acs_instance.restore_instance()
+
+ elif state in ['present', 'deployed']:
+ instance = acs_instance.present_instance()
+
+ elif state in ['stopped']:
+ acs_instance.present_instance(start_vm=False)
+ instance = acs_instance.stop_instance()
+
+ elif state in ['started']:
+ acs_instance.present_instance()
+ instance = acs_instance.start_instance()
+
+ elif state in ['restarted']:
+ acs_instance.present_instance()
+ instance = acs_instance.restart_instance()
+
+ if instance and 'state' in instance and instance['state'].lower() == 'error':
+ module.fail_json(msg="Instance named '%s' in error state." % module.params.get('name'))
+
+ result = acs_instance.get_result(instance)
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_info.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_info.py
new file mode 100644
index 00000000..5b111bec
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_info.py
@@ -0,0 +1,370 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Gather instance information
+ ngine_io.cloudstack.cs_instance_info:
+ name: web-vm-1
+ register: vm
+
+- name: Show the returned results of the registered variable
+ debug:
+ msg: "{{ vm }}"
+
+- name: Gather information from all instances
+ ngine_io.cloudstack.cs_instance_info:
+ 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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_nic.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_nic.py
new file mode 100644
index 00000000..11ea45fc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_nic.py
@@ -0,0 +1,285 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017, Marc-Aurèle Brothier @marcaurele
+# 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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Add a nic on another network
+ ngine_io.cloudstack.cs_instance_nic:
+ vm: privnet
+ network: privNetForBasicZone
+
+- name: Ensure IP address on a nic
+ ngine_io.cloudstack.cs_instance_nic:
+ vm: privnet
+ ip_address: 10.10.11.32
+ network: privNetForBasicZone
+
+- name: Remove a secondary nic
+ ngine_io.cloudstack.cs_instance_nic:
+ vm: privnet
+ state: absent
+ network: privNetForBasicZone
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_nic_secondaryip.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_nic_secondaryip.py
new file mode 100644
index 00000000..7dabe7d7
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_nic_secondaryip.py
@@ -0,0 +1,267 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+
+'''
+
+EXAMPLES = '''
+- name: Assign a specific IP to the default NIC of the VM
+ ngine_io.cloudstack.cs_instance_nic_secondaryip:
+ vm: customer_xy
+ vm_guest_ip: 10.10.10.10
+
+# 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
+ ngine_io.cloudstack.cs_instance_nic_secondaryip:
+ vm: customer_xy
+
+- name: Remove a specific IP from the default NIC
+ ngine_io.cloudstack.cs_instance_nic_secondaryip:
+ vm: customer_xy
+ vm_guest_ip: 10.10.10.10
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_password_reset.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_password_reset.py
new file mode 100644
index 00000000..58b9b741
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instance_password_reset.py
@@ -0,0 +1,151 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: stop the virtual machine before resetting the password
+ ngine_io.cloudstack.cs_instance:
+ name: myvirtualmachine
+ state: stopped
+
+- name: reset and get new default password
+ ngine_io.cloudstack.cs_instance_password_reset:
+ vm: myvirtualmachine
+ register: root
+
+- debug:
+ msg: "new default password is {{ root.password }}"
+
+- name: boot the virtual machine to activate the new password
+ ngine_io.cloudstack.cs_instance:
+ name: myvirtualmachine
+ state: started
+ 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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instancegroup.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instancegroup.py
new file mode 100644
index 00000000..56783b2a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_instancegroup.py
@@ -0,0 +1,181 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_instancegroup
+short_description: Manages instance groups on Apache CloudStack based clouds.
+description:
+ - Create and remove instance groups.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create an instance group
+ ngine_io.cloudstack.cs_instancegroup:
+ name: loadbalancers
+
+- name: Remove an instance group
+ ngine_io.cloudstack.cs_instancegroup:
+ name: loadbalancers
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_ip_address.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_ip_address.py
new file mode 100644
index 00000000..04236d3d
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_ip_address.py
@@ -0,0 +1,279 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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
+ elements: dict
+ aliases: [ tag ]
+ poll_async:
+ description:
+ - Poll async jobs until job has finished.
+ type: bool
+ default: yes
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Associate an IP address conditionally
+ ngine_io.cloudstack.cs_ip_address:
+ network: My Network
+ register: ip_address
+ when: instance.public_ip is undefined
+
+- name: Disassociate an IP address
+ ngine_io.cloudstack.cs_ip_address:
+ ip_address: 1.2.3.4
+ state: absent
+
+- name: Associate an IP address with tags
+ ngine_io.cloudstack.cs_ip_address:
+ network: My Network
+ tags:
+ - key: myCustomID
+ value: 5510c31a-416e-11e8-9013-02000a6b00bf
+ register: ip_address
+
+- name: Disassociate an IP address with tags
+ ngine_io.cloudstack.cs_ip_address:
+ state: absent
+ tags:
+ - key: myCustomID
+ value: 5510c31a-416e-11e8-9013-02000a6b00bf
+'''
+
+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 ..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 = {
+ # ipaddress only works with CloudStack >=v4.13
+ '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'),
+ # 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', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_iso.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_iso.py
new file mode 100644
index 00000000..cecca5eb
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_iso.py
@@ -0,0 +1,436 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_iso
+short_description: Manages ISO images on Apache CloudStack based clouds.
+description:
+ - Register and remove ISO images.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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
+ elements: dict
+ aliases: [ tag ]
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Register an ISO if ISO name does not already exist
+ ngine_io.cloudstack.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)
+
+- name: Register an ISO with given name if ISO md5 checksum does not already exist
+ ngine_io.cloudstack.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
+
+- name: Remove an ISO by name
+ ngine_io.cloudstack.cs_iso:
+ name: Debian 7 64-bit
+ state: absent
+
+- name: Remove an ISO by checksum
+ ngine_io.cloudstack.cs_iso:
+ name: Debian 7 64-bit
+ checksum: 0b31bccccb048d20b551f70830bb7ad0
+ state: absent
+'''
+
+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 ..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', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_loadbalancer_rule.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_loadbalancer_rule.py
new file mode 100644
index 00000000..12a7d13a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_loadbalancer_rule.py
@@ -0,0 +1,371 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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
+ 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
+ elements: dict
+ aliases: [ tag ]
+ network:
+ description:
+ - Name of the network.
+ type: str
+ vpc:
+ description:
+ - Name of the VPC.
+ type: str
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create a load balancer rule
+ ngine_io.cloudstack.cs_loadbalancer_rule:
+ name: balance_http
+ public_ip: 1.2.3.4
+ algorithm: leastconn
+ public_port: 80
+ private_port: 8080
+
+- name: Update algorithm of an existing load balancer rule
+ ngine_io.cloudstack.cs_loadbalancer_rule:
+ name: balance_http
+ public_ip: 1.2.3.4
+ algorithm: roundrobin
+ public_port: 80
+ private_port: 8080
+
+- name: Delete a load balancer rule
+ ngine_io.cloudstack.cs_loadbalancer_rule:
+ name: balance_http
+ public_ip: 1.2.3.4
+ state: absent
+'''
+
+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 ..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', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_loadbalancer_rule_member.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_loadbalancer_rule_member.py
new file mode 100644
index 00000000..6922bb83
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_loadbalancer_rule_member.py
@@ -0,0 +1,345 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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
+ elements: str
+ 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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Add VMs to an existing load balancer
+ ngine_io.cloudstack.cs_loadbalancer_rule_member:
+ name: balance_http
+ vms:
+ - web01
+ - web02
+
+- name: Remove a VM from an existing load balancer
+ ngine_io.cloudstack.cs_loadbalancer_rule_member:
+ name: balance_http
+ vms:
+ - web01
+ - web02
+ state: absent
+
+
+# Rolling upgrade of hosts
+- hosts: webservers
+ serial: 1
+ pre_tasks:
+ - name: Remove from load balancer
+ ngine_io.cloudstack.cs_loadbalancer_rule_member:
+ name: balance_http
+ vm: "{{ ansible_hostname }}"
+ state: absent
+ tasks:
+ # Perform update
+ post_tasks:
+ - name: Add to load balancer
+ ngine_io.cloudstack.cs_loadbalancer_rule_member:
+ name: balance_http
+ vm: "{{ ansible_hostname }}"
+ state: present
+
+'''
+
+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 ..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', elements='str'),
+ 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network.py
new file mode 100644
index 00000000..3dc37999
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network.py
@@ -0,0 +1,633 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_network
+short_description: Manages networks on Apache CloudStack based clouds.
+description:
+ - Create, update, restart and delete networks.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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
+ elements: dict
+ aliases: [ tag ]
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create a network
+ ngine_io.cloudstack.cs_network:
+ name: my network
+ zone: gva-01
+ network_offering: DefaultIsolatedNetworkOfferingWithSourceNatService
+ network_domain: example.com
+
+- name: Create a network with start and end IP
+ ngine_io.cloudstack.cs_network:
+ name: Private Network
+ network_offering: PrivNet
+ start_ip: 10.12.9.10
+ end_ip: 10.12.9.100
+ netmask: 255.255.255.0
+ zone: gva-01
+
+- name: Create a VPC tier
+ ngine_io.cloudstack.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
+
+- name: Update a network
+ ngine_io.cloudstack.cs_network:
+ name: my network
+ display_text: network of domain example.local
+ network_domain: example.local
+
+- name: Restart a network with clean up
+ ngine_io.cloudstack.cs_network:
+ name: my network
+ clean_up: yes
+ state: restarted
+
+- name: Remove a network
+ ngine_io.cloudstack.cs_network:
+ name: my network
+ state: absent
+'''
+
+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 ..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', elements='dict', aliases=['tag']),
+ ))
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_together=cs_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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_acl.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_acl.py
new file mode 100644
index 00000000..b6cd0e18
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_acl.py
@@ -0,0 +1,197 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: create a network ACL
+ ngine_io.cloudstack.cs_network_acl:
+ name: Webserver ACL
+ description: a more detailed description of the ACL
+ vpc: customers
+
+- name: remove a network ACL
+ ngine_io.cloudstack.cs_network_acl:
+ name: Webserver ACL
+ vpc: customers
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_acl_rule.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_acl_rule.py
new file mode 100644
index 00000000..acce9e2c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_acl_rule.py
@@ -0,0 +1,457 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+options:
+ network_acl:
+ description:
+ - Name of the network ACL.
+ type: str
+ required: true
+ aliases: [ acl ]
+ cidrs:
+ description:
+ - CIDRs of the rule.
+ type: list
+ elements: str
+ 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
+ elements: dict
+ 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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: create a network ACL rule, allow port 80 ingress
+ ngine_io.cloudstack.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
+
+- name: create a network ACL rule, deny port range 8000-9000 ingress for 10.20.0.0/16 and 10.22.0.0/16
+ ngine_io.cloudstack.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
+
+- name: remove a network ACL rule
+ ngine_io.cloudstack.cs_network_acl_rule:
+ network_acl: web
+ rule_position: 1
+ vpc: my vpc
+ state: absent
+'''
+
+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 ..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', elements='str', 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', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_offering.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_offering.py
new file mode 100644
index 00000000..a5c5cb93
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_network_offering.py
@@ -0,0 +1,422 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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
+ elements: str
+ 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
+ elements: dict
+ 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
+ elements: str
+ 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
+ elements: dict
+ 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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create a network offering and enable it
+ ngine_io.cloudstack.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' }
+
+- name: Remove a network offering
+ ngine_io.cloudstack.cs_network_offering:
+ name: my_network_offering
+ state: absent
+'''
+
+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 ..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', elements='str', 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', elements='dict'),
+ 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', elements='str', aliases=['service_capability']),
+ service_offering=dict(),
+ service_providers=dict(type='list', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_physical_network.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_physical_network.py
new file mode 100644
index 00000000..2148afc2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_physical_network.py
@@ -0,0 +1,473 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (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
+
+
+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)
+version_added: 0.1.0
+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
+ elements: str
+ nsps_disabled:
+ description:
+ - List of Network Service Providers to disable.
+ type: list
+ elements: str
+ 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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Ensure a network is present
+ ngine_io.cloudstack.cs_physical_network:
+ name: net01
+ zone: zone01
+ isolation_method: VLAN
+ broadcast_domain_range: ZONE
+
+- name: Set a tag on a network
+ ngine_io.cloudstack.cs_physical_network:
+ name: net01
+ tag: overlay
+
+- name: Remove tag on a network
+ ngine_io.cloudstack.cs_physical_network:
+ name: net01
+ tag: ""
+
+- name: Ensure a network is enabled with specific nsps enabled
+ ngine_io.cloudstack.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
+
+- name: Ensure a network is disabled
+ ngine_io.cloudstack.cs_physical_network:
+ name: net01
+ zone: zone01
+ state: disabled
+
+- name: Ensure a network is enabled
+ ngine_io.cloudstack.cs_physical_network:
+ name: net01
+ zone: zone01
+ state: enabled
+
+- name: Ensure a network is absent
+ ngine_io.cloudstack.cs_physical_network:
+ name: net01
+ zone: zone01
+ state: absent
+'''
+
+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 ..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', elements='str'),
+ nsps_enabled=dict(type='list', elements='str'),
+ 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_pod.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_pod.py
new file mode 100644
index 00000000..90ab2ef5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_pod.py
@@ -0,0 +1,289 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_pod
+short_description: Manages pods on Apache CloudStack based clouds.
+description:
+ - Create, update, delete pods.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Ensure a pod is present
+ ngine_io.cloudstack.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
+
+- name: Ensure a pod is disabled
+ ngine_io.cloudstack.cs_pod:
+ name: pod1
+ zone: ch-zrh-ix-01
+ state: disabled
+
+- name: Ensure a pod is enabled
+ ngine_io.cloudstack.cs_pod:
+ name: pod1
+ zone: ch-zrh-ix-01
+ state: enabled
+
+- name: Ensure a pod is absent
+ ngine_io.cloudstack.cs_pod:
+ name: pod1
+ zone: ch-zrh-ix-01
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_portforward.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_portforward.py
new file mode 100644
index 00000000..3c80ccbb
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_portforward.py
@@ -0,0 +1,398 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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.
+ - Not supported when forwarding ports in a VPC.
+ - 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. Required when forwarding ports in a VPC.
+ 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
+ elements: dict
+ aliases: [ tag ]
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: 1.2.3.4:80 -> web01:8080
+ ngine_io.cloudstack.cs_portforward:
+ ip_address: 1.2.3.4
+ vm: web01
+ public_port: 80
+ private_port: 8080
+
+- name: forward SSH and open firewall
+ ngine_io.cloudstack.cs_portforward:
+ ip_address: '{{ public_ip }}'
+ vm: '{{ inventory_hostname }}'
+ public_port: '{{ ansible_ssh_port }}'
+ private_port: 22
+ open_firewall: true
+
+- name: forward DNS traffic, but do not open firewall
+ ngine_io.cloudstack.cs_portforward:
+ ip_address: 1.2.3.4
+ vm: '{{ inventory_hostname }}'
+ public_port: 53
+ private_port: 53
+ protocol: udp
+
+- name: remove ssh port forwarding
+ ngine_io.cloudstack.cs_portforward:
+ ip_address: 1.2.3.4
+ public_port: 22
+ private_port: 22
+ state: absent
+
+- name: forward SSH in backend tier of VPC
+ ngine_io.cloudstack.cs_portforward:
+ ip_address: '{{ public_ip }}'
+ vm: '{{ inventory_hostname }}'
+ public_port: '{{ ansible_ssh_port }}'
+ private_port: 22
+ vpc: myVPC
+ network: backend
+'''
+
+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 ..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', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_project.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_project.py
new file mode 100644
index 00000000..c52444ed
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_project.py
@@ -0,0 +1,271 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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
+ elements: dict
+ aliases: [ tag ]
+ poll_async:
+ description:
+ - Poll async jobs until job has finished.
+ type: bool
+ default: yes
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create a project
+ ngine_io.cloudstack.cs_project:
+ name: web
+ tags:
+ - { key: admin, value: john }
+ - { key: foo, value: bar }
+
+- name: Rename a project
+ ngine_io.cloudstack.cs_project:
+ name: web
+ display_text: my web project
+
+- name: Suspend an existing project
+ ngine_io.cloudstack.cs_project:
+ name: web
+ state: suspended
+
+- name: Activate an existing project
+ ngine_io.cloudstack.cs_project:
+ name: web
+ state: active
+
+- name: Remove a project
+ ngine_io.cloudstack.cs_project:
+ name: web
+ state: absent
+'''
+
+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 ..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', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_region.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_region.py
new file mode 100644
index 00000000..327f7c1e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_region.py
@@ -0,0 +1,187 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_region
+short_description: Manages regions on Apache CloudStack based clouds.
+description:
+ - Add, update and remove regions.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: create a region
+ ngine_io.cloudstack.cs_region:
+ id: 2
+ name: geneva
+ endpoint: https://cloud.gva.example.com
+
+- name: remove a region with ID 2
+ ngine_io.cloudstack.cs_region:
+ id: 2
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_resourcelimit.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_resourcelimit.py
new file mode 100644
index 00000000..fdca3bdd
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_resourcelimit.py
@@ -0,0 +1,202 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Update a resource limit for instances of a domain
+ ngine_io.cloudstack.cs_resourcelimit:
+ type: instance
+ limit: 10
+ domain: customers
+
+- name: Update a resource limit for instances of an account
+ ngine_io.cloudstack.cs_resourcelimit:
+ type: instance
+ limit: 12
+ account: moserre
+ domain: customers
+'''
+
+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 ..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=list(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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_role.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_role.py
new file mode 100644
index 00000000..01f23479
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_role.py
@@ -0,0 +1,205 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_role
+short_description: Manages user roles on Apache CloudStack based clouds.
+description:
+ - Create, update, delete user roles.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Ensure an user role is present
+ ngine_io.cloudstack.cs_role:
+ name: myrole_user
+
+- name: Ensure a role having particular ID is named as myrole_user
+ ngine_io.cloudstack.cs_role:
+ name: myrole_user
+ id: 04589590-ac63-4ffc-93f5-b698b8ac38b6
+
+- name: Ensure a role is absent
+ ngine_io.cloudstack.cs_role:
+ name: myrole_user
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_role_permission.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_role_permission.py
new file mode 100644
index 00000000..8755f158
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_role_permission.py
@@ -0,0 +1,345 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create a role permission
+ ngine_io.cloudstack.cs_role_permission:
+ role: My_Custom_role
+ name: createVPC
+ permission: allow
+ description: My comments
+
+- name: Remove a role permission
+ ngine_io.cloudstack.cs_role_permission:
+ state: absent
+ role: My_Custom_role
+ name: createVPC
+
+- name: Update a system role permission
+ ngine_io.cloudstack.cs_role_permission:
+ role: Domain Admin
+ name: createVPC
+ permission: deny
+
+- name: Update rules order. Move the rule at the top of list
+ ngine_io.cloudstack.cs_role_permission:
+ role: Domain Admin
+ name: createVPC
+ parent: 0
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_router.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_router.py
new file mode 100644
index 00000000..e14a7609
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_router.py
@@ -0,0 +1,368 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+# Ensure the router has the desired service offering, no matter if
+# the router is running or not.
+- name: Present router
+ ngine_io.cloudstack.cs_router:
+ name: r-40-VM
+ service_offering: System Offering for Software Router
+
+- name: Ensure started
+ ngine_io.cloudstack.cs_router:
+ name: r-40-VM
+ state: started
+
+# Ensure started with desired service offering.
+# If the service offerings changes, router will be rebooted.
+- name: Ensure started with desired service offering
+ ngine_io.cloudstack.cs_router:
+ name: r-40-VM
+ service_offering: System Offering for Software Router
+ state: started
+
+- name: Ensure stopped
+ ngine_io.cloudstack.cs_router:
+ name: r-40-VM
+ state: stopped
+
+- name: Remove a router
+ ngine_io.cloudstack.cs_router:
+ name: r-40-VM
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_securitygroup.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_securitygroup.py
new file mode 100644
index 00000000..5d8ba2b9
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_securitygroup.py
@@ -0,0 +1,193 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_securitygroup
+short_description: Manages security groups on Apache CloudStack based clouds.
+description:
+ - Create and remove security groups.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: create a security group
+ ngine_io.cloudstack.cs_securitygroup:
+ name: default
+ description: default security group
+
+- name: remove a security group
+ ngine_io.cloudstack.cs_securitygroup:
+ name: default
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_securitygroup_rule.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_securitygroup_rule.py
new file mode 100644
index 00000000..2bd269e8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_securitygroup_rule.py
@@ -0,0 +1,379 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+---
+- name: allow inbound port 80/tcp from 1.2.3.4 added to security group 'default'
+ ngine_io.cloudstack.cs_securitygroup_rule:
+ security_group: default
+ port: 80
+ cidr: 1.2.3.4/32
+
+- name: allow tcp/udp outbound added to security group 'default'
+ ngine_io.cloudstack.cs_securitygroup_rule:
+ security_group: default
+ type: egress
+ start_port: 1
+ end_port: 65535
+ protocol: '{{ item }}'
+ with_items:
+ - tcp
+ - udp
+
+- name: allow inbound icmp from 0.0.0.0/0 added to security group 'default'
+ ngine_io.cloudstack.cs_securitygroup_rule:
+ security_group: default
+ protocol: icmp
+ icmp_code: -1
+ icmp_type: -1
+
+- name: remove rule inbound port 80/tcp from 0.0.0.0/0 from security group 'default'
+ ngine_io.cloudstack.cs_securitygroup_rule:
+ security_group: default
+ port: 80
+ state: absent
+
+- name: allow inbound port 80/tcp from security group web added to security group 'default'
+ ngine_io.cloudstack.cs_securitygroup_rule:
+ security_group: default
+ port: 80
+ user_security_group: web
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_service_offering.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_service_offering.py
new file mode 100644
index 00000000..8d17d521
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_service_offering.py
@@ -0,0 +1,574 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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
+ elements: str
+ 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
+ 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
+ elements: dict
+ 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
+ elements: str
+ aliases:
+ - storage_tag
+ is_customized:
+ description:
+ - Whether the offering is customizable or not.
+ type: bool
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create a non-volatile compute service offering with local storage
+ ngine_io.cloudstack.cs_service_offering:
+ name: Micro
+ display_text: Micro 512mb 1cpu
+ cpu_number: 1
+ cpu_speed: 2198
+ memory: 512
+ host_tags: eco
+ storage_type: local
+
+- name: Create a volatile compute service offering with shared storage
+ ngine_io.cloudstack.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
+
+- name: Create or update a volatile compute service offering with shared storage
+ ngine_io.cloudstack.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
+
+- name: Create or update a custom compute service offering
+ ngine_io.cloudstack.cs_service_offering:
+ name: custom
+ display_text: custom compute offer
+ is_customized: yes
+ storage_type: shared
+ host_tags: eco
+ storage_tags: eco
+
+- name: Remove a compute service offering
+ ngine_io.cloudstack.cs_service_offering:
+ name: Tiny
+ state: absent
+
+- name: Create or update a system offering for the console proxy
+ ngine_io.cloudstack.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
+
+- name: Remove a system offering
+ ngine_io.cloudstack.cs_service_offering:
+ name: System Offering for Console Proxy 2GB
+ is_system: yes
+ state: absent
+'''
+
+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 ..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', elements='str', 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', elements='dict'),
+ storage_type=dict(choices=['local', 'shared']),
+ system_vm_type=dict(choices=['domainrouter', 'consoleproxy', 'secondarystoragevm']),
+ storage_tags=dict(type='list', elements='str', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_snapshot_policy.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_snapshot_policy.py
new file mode 100644
index 00000000..bd11f367
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_snapshot_policy.py
@@ -0,0 +1,349 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: ensure a snapshot policy daily at 1h00 UTC
+ ngine_io.cloudstack.cs_snapshot_policy:
+ volume: ROOT-478
+ schedule: '00:1'
+ max_snaps: 3
+
+- name: ensure a snapshot policy daily at 1h00 UTC on the second DATADISK of VM web-01
+ ngine_io.cloudstack.cs_snapshot_policy:
+ vm: web-01
+ volume_type: DATADISK
+ device_id: 2
+ schedule: '00:1'
+ max_snaps: 3
+
+- name: ensure a snapshot policy hourly at minute 5 UTC
+ ngine_io.cloudstack.cs_snapshot_policy:
+ volume: ROOT-478
+ schedule: '5'
+ interval_type: hourly
+ max_snaps: 1
+
+- name: ensure a snapshot policy weekly on Sunday at 05h00, TZ Europe/Zurich
+ ngine_io.cloudstack.cs_snapshot_policy:
+ volume: ROOT-478
+ schedule: '00:5:1'
+ interval_type: weekly
+ max_snaps: 1
+ time_zone: 'Europe/Zurich'
+
+- name: ensure a snapshot policy is absent
+ ngine_io.cloudstack.cs_snapshot_policy:
+ volume: ROOT-478
+ interval_type: hourly
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_sshkeypair.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_sshkeypair.py
new file mode 100644
index 00000000..3e32171c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_sshkeypair.py
@@ -0,0 +1,261 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: create a new private / public key pair
+ ngine_io.cloudstack.cs_sshkeypair:
+ name: linus@example.com
+ register: key
+
+- debug:
+ msg: 'Private key is {{ key.private_key }}'
+
+- name: remove a public key by its name
+ ngine_io.cloudstack.cs_sshkeypair:
+ name: linus@example.com
+ state: absent
+
+- name: register your existing local public key
+ ngine_io.cloudstack.cs_sshkeypair:
+ name: linus@example.com
+ public_key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_staticnat.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_staticnat.py
new file mode 100644
index 00000000..5d223715
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_staticnat.py
@@ -0,0 +1,249 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create a static NAT for IP 1.2.3.4 to web01
+ ngine_io.cloudstack.cs_staticnat:
+ ip_address: 1.2.3.4
+ vm: web01
+
+- name: Remove a static NAT
+ ngine_io.cloudstack.cs_staticnat:
+ ip_address: 1.2.3.4
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_storage_pool.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_storage_pool.py
new file mode 100644
index 00000000..e4034587
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_storage_pool.py
@@ -0,0 +1,489 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017, Netservers Ltd. <support@netservers.co.uk>
+# 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
+
+
+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)
+version_added: 0.1.0
+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
+ elements: str
+ 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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: ensure a zone scoped storage_pool is present
+ ngine_io.cloudstack.cs_storage_pool:
+ zone: zone01
+ storage_url: rbd://admin:SECRET@ceph-mons.domain/poolname
+ provider: DefaultPrimary
+ name: Ceph RBD
+ scope: zone
+ hypervisor: KVM
+
+- name: ensure a cluster scoped storage_pool is disabled
+ ngine_io.cloudstack.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
+
+- name: ensure a cluster scoped storage_pool is in maintenance
+ ngine_io.cloudstack.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
+
+- name: ensure a storage_pool is absent
+ ngine_io.cloudstack.cs_storage_pool:
+ name: Ceph RBD
+ state: absent
+'''
+
+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 ..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_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', elements='str', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_template.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_template.py
new file mode 100644
index 00000000..16902595
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_template.py
@@ -0,0 +1,741 @@
+#!/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
+
+
+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, extract and delete templates.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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_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
+ elements: str
+ 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
+ elements: dict
+ aliases: [ tag ]
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: register a systemvm template
+ ngine_io.cloudstack.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)
+
+- name: Create a template from a stopped virtual machine's volume
+ ngine_io.cloudstack.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
+
+
+# Note: Use template_find_option(s) when a template name is not unique
+- name: Create a template from a stopped virtual machine's volume
+ ngine_io.cloudstack.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
+
+- name: create a template from a virtual machine's root volume snapshot
+ ngine_io.cloudstack.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
+
+- name: Remove a template
+ ngine_io.cloudstack.cs_template:
+ name: systemvm-4.2
+ cross_zones: yes
+ state: absent
+'''
+
+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 ..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")
+
+ if self.module.params.get('cross_zones'):
+ self.module.warn('cross_zones parameter is ignored when state is extracted')
+
+ 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_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',
+ elements='str',
+ choices=['display_text', 'checksum', 'cross_zones'],
+ aliases=['template_find_option'],
+ default=[],
+ ),
+ hypervisor=dict(),
+ requires_hvm=dict(type='bool'),
+ password_enabled=dict(type='bool', no_log=False),
+ 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', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_traffic_type.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_traffic_type.py
new file mode 100644
index 00000000..22ec3ec3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_traffic_type.py
@@ -0,0 +1,321 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (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
+
+
+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:
+- ngine_io.cloudstack.cloudstack
+author:
+ - Patryk Cichy (@PatTheSilent)
+version_added: 0.1.0
+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
+ ngine_io.cloudstack.cs_traffic_type:
+ physical_network: public-network
+ traffic_type: Guest
+ zone: test-zone
+
+- name: update traffic type
+ ngine_io.cloudstack.cs_traffic_type:
+ physical_network: public-network
+ traffic_type: Guest
+ kvm_networklabel: cloudbr0
+ zone: test-zone
+
+- name: remove traffic type
+ ngine_io.cloudstack.cs_traffic_type:
+ physical_network: public-network
+ traffic_type: Public
+ state: absent
+ zone: test-zone
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_user.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_user.py
new file mode 100644
index 00000000..816e5f70
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_user.py
@@ -0,0 +1,436 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create an user in domain 'CUSTOMERS'
+ ngine_io.cloudstack.cs_user:
+ account: developers
+ username: johndoe
+ password: S3Cur3
+ last_name: Doe
+ first_name: John
+ email: john.doe@example.com
+ domain: CUSTOMERS
+
+- name: Lock an existing user in domain 'CUSTOMERS'
+ ngine_io.cloudstack.cs_user:
+ username: johndoe
+ domain: CUSTOMERS
+ state: locked
+
+- name: Disable an existing user in domain 'CUSTOMERS'
+ ngine_io.cloudstack.cs_user:
+ username: johndoe
+ domain: CUSTOMERS
+ state: disabled
+
+- name: Enable/unlock an existing user in domain 'CUSTOMERS'
+ ngine_io.cloudstack.cs_user:
+ username: johndoe
+ domain: CUSTOMERS
+ state: enabled
+
+- name: Remove an user in domain 'CUSTOMERS'
+ ngine_io.cloudstack.cs_user:
+ name: customer_xy
+ domain: CUSTOMERS
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vlan_ip_range.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vlan_ip_range.py
new file mode 100644
index 00000000..aaca4158
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vlan_ip_range.py
@@ -0,0 +1,397 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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
+ pod:
+ description:
+ - Name of the pod.
+ type: str
+ version_added: 1.0.0
+ 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
+ version_added: 1.0.0
+ for_system_vms:
+ description:
+ - C(yes) if IP range is set to system vms, C(no) if not
+ type: bool
+ default: no
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: create a VLAN IP range for network test
+ ngine_io.cloudstack.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
+
+- name: remove a VLAN IP range for network test
+ ngine_io.cloudstack.cs_vlan_ip_range:
+ state: absent
+ network: test
+ start_ip: 10.2.4.10
+ end_ip: 10.2.4.100
+ zone: zone-02
+'''
+
+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 ..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'),
+ 'forsystemvms': self.module.params.get('for_system_vms'),
+ 'podid': self.get_pod(key='id'),
+ }
+ 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'),
+ pod=dict(type='str'),
+ for_virtual_network=dict(type='bool', default=False),
+ for_system_vms=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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vmsnapshot.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vmsnapshot.py
new file mode 100644
index 00000000..83b77a70
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vmsnapshot.py
@@ -0,0 +1,278 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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
+ elements: dict
+ aliases: [ tag ]
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create a VM snapshot of disk and memory before an upgrade
+ ngine_io.cloudstack.cs_vmsnapshot:
+ name: Snapshot before upgrade
+ vm: web-01
+ snapshot_memory: yes
+
+- name: Revert a VM to a snapshot after a failed upgrade
+ ngine_io.cloudstack.cs_vmsnapshot:
+ name: Snapshot before upgrade
+ vm: web-01
+ state: revert
+
+- name: Remove a VM snapshot after successful upgrade
+ ngine_io.cloudstack.cs_vmsnapshot:
+ name: Snapshot before upgrade
+ vm: web-01
+ state: absent
+'''
+
+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 ..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', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_volume.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_volume.py
new file mode 100644
index 00000000..5947675c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_volume.py
@@ -0,0 +1,561 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2015, Jefferson Girão <jefferson@girao.net>
+# 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
+
+
+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)
+version_added: 0.1.0
+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.
+ 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
+ elements: dict
+ 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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: create volume within project and zone with specified storage options
+ ngine_io.cloudstack.cs_volume:
+ name: web-vm-1-volume
+ project: Integration
+ zone: ch-zrh-ix-01
+ disk_offering: PerfPlus Storage
+ size: 20
+
+- name: create/attach volume to instance
+ ngine_io.cloudstack.cs_volume:
+ name: web-vm-1-volume
+ disk_offering: PerfPlus Storage
+ size: 20
+ vm: web-vm-1
+ state: attached
+
+- name: detach volume
+ ngine_io.cloudstack.cs_volume:
+ name: web-vm-1-volume
+ state: detached
+
+- name: remove volume
+ ngine_io.cloudstack.cs_volume:
+ name: web-vm-1-volume
+ state: absent
+
+- name: Extract DATA volume to make it downloadable
+ ngine_io.cloudstack.cs_volume:
+ state: extracted
+ name: web-vm-1-volume
+ register: data_vol_out
+
+- name: Create new volume by downloading source volume
+ ngine_io.cloudstack.cs_volume:
+ state: uploaded
+ name: web-vm-1-volume-2
+ format: VHD
+ url: "{{ data_vol_out.url }}"
+'''
+
+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 ..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', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpc.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpc.py
new file mode 100644
index 00000000..45837dd2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpc.py
@@ -0,0 +1,391 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_vpc
+short_description: "Manages VPCs on Apache CloudStack based clouds."
+description:
+ - Create, update and delete VPCs.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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
+ elements: dict
+ aliases: [ tag ]
+ poll_async:
+ description:
+ - Poll async jobs until job has finished.
+ default: yes
+ type: bool
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Ensure a VPC is present but not started after creating
+ ngine_io.cloudstack.cs_vpc:
+ name: my_vpc
+ display_text: My example VPC
+ cidr: 10.10.0.0/16
+ state: stopped
+
+- name: Ensure a VPC is present and started after creating
+ ngine_io.cloudstack.cs_vpc:
+ name: my_vpc
+ display_text: My example VPC
+ cidr: 10.10.0.0/16
+
+- name: Ensure a VPC is absent
+ ngine_io.cloudstack.cs_vpc:
+ name: my_vpc
+ state: absent
+
+- name: Ensure a VPC is restarted with clean up
+ ngine_io.cloudstack.cs_vpc:
+ name: my_vpc
+ clean_up: yes
+ state: restarted
+'''
+
+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 ..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', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpc_offering.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpc_offering.py
new file mode 100644
index 00000000..be129a1e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpc_offering.py
@@ -0,0 +1,319 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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
+ elements: dict
+ 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
+ elements: str
+ 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
+ elements: dict
+ aliases: [ service_provider ]
+ poll_async:
+ description:
+ - Poll async jobs until job has finished.
+ default: yes
+ type: bool
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Create a vpc offering and enable it
+ ngine_io.cloudstack.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'}
+
+- name: Create a vpc offering with redundant router
+ ngine_io.cloudstack.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}
+
+- name: Create a region level vpc offering with distributed router
+ ngine_io.cloudstack.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}
+
+- name: Remove a vpc offering
+ ngine_io.cloudstack.cs_vpc_offering:
+ name: my_vpc_offering
+ state: absent
+'''
+
+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 ..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', elements='dict', aliases=['service_capability']),
+ service_offering=dict(),
+ supported_services=dict(type='list', elements='str', aliases=['supported_service']),
+ service_providers=dict(type='list', elements='dict', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_connection.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_connection.py
new file mode 100644
index 00000000..4f77b561
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_connection.py
@@ -0,0 +1,349 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = r'''
+- name: Create a VPN connection with activated VPN gateway
+ ngine_io.cloudstack.cs_vpn_connection:
+ vpn_customer_gateway: my vpn connection
+ vpc: my vpc
+
+- name: Create a VPN connection and force VPN gateway activation
+ ngine_io.cloudstack.cs_vpn_connection:
+ vpn_customer_gateway: my vpn connection
+ vpc: my vpc
+ force: yes
+
+- name: Remove a vpn connection
+ ngine_io.cloudstack.cs_vpn_connection:
+ vpn_customer_gateway: my vpn connection
+ vpc: my vpc
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_customer_gateway.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_customer_gateway.py
new file mode 100644
index 00000000..915784cc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_customer_gateway.py
@@ -0,0 +1,343 @@
+#!/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
+
+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)
+version_added: 0.1.0
+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
+ elements: str
+ 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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = r'''
+- name: Create a vpn customer gateway
+ ngine_io.cloudstack.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"
+
+- name: Remove a vpn customer gateway
+ ngine_io.cloudstack.cs_vpn_customer_gateway:
+ name: my vpn customer gateway
+ state: absent
+'''
+
+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 ..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', elements='str', 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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_gateway.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_gateway.py
new file mode 100644
index 00000000..7bc3ab0b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_vpn_gateway.py
@@ -0,0 +1,204 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Ensure a vpn gateway is present
+ ngine_io.cloudstack.cs_vpn_gateway:
+ vpc: my VPC
+
+- name: Ensure a vpn gateway is absent
+ ngine_io.cloudstack.cs_vpn_gateway:
+ vpc: my VPC
+ state: absent
+'''
+
+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 ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_zone.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_zone.py
new file mode 100644
index 00000000..51348086
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_zone.py
@@ -0,0 +1,377 @@
+#!/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
+
+
+DOCUMENTATION = '''
+---
+module: cs_zone
+short_description: Manages zones on Apache CloudStack based clouds.
+description:
+ - Create, update and remove zones.
+author: René Moser (@resmo)
+version_added: 0.1.0
+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:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Ensure a zone is present
+ ngine_io.cloudstack.cs_zone:
+ name: ch-zrh-ix-01
+ dns1: 8.8.8.8
+ dns2: 8.8.4.4
+ network_type: basic
+
+- name: Ensure a zone is disabled
+ ngine_io.cloudstack.cs_zone:
+ name: ch-zrh-ix-01
+ state: disabled
+
+- name: Ensure a zone is enabled
+ ngine_io.cloudstack.cs_zone:
+ name: ch-zrh-ix-01
+ state: enabled
+
+- name: Ensure a zone is absent
+ ngine_io.cloudstack.cs_zone:
+ name: ch-zrh-ix-01
+ state: absent
+'''
+
+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: list
+ sample: [ { "key": "foo", "value": "bar" } ]
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..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()
diff --git a/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_zone_info.py b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_zone_info.py
new file mode 100644
index 00000000..1581cdbf
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/ngine_io/cloudstack/plugins/modules/cs_zone_info.py
@@ -0,0 +1,207 @@
+#!/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
+
+
+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)
+version_added: 0.1.0
+options:
+ zone:
+ description:
+ - Name of the zone.
+ - If not specified, all zones are returned
+ type: str
+ aliases: [ name ]
+extends_documentation_fragment:
+- ngine_io.cloudstack.cloudstack
+'''
+
+EXAMPLES = '''
+- name: Gather information from a zone
+ ngine_io.cloudstack.cs_zone_info:
+ zone: ch-gva-1
+ register: zone
+
+- name: Show the returned results of the registered variable
+ debug:
+ msg: "{{ zone }}"
+
+- name: Gather information from all zones
+ ngine_io.cloudstack.cs_zone_info:
+ register: zones
+
+- 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: list
+ sample: [ { "key": "foo", "value": "bar" } ]
+'''
+
+from ansible.module_utils.basic import AnsibleModule
+from ..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()