Relocating extras into lib/ansible/modules/ after merge

This commit is contained in:
James Cammarata
2016-12-08 00:36:57 -05:00
committed by Matt Clay
parent c65ba07d2c
commit 011ea55a8f
596 changed files with 0 additions and 266 deletions

View File

@@ -0,0 +1,385 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_account
short_description: Manages accounts on Apache CloudStack based clouds.
description:
- Create, disable, lock, enable and remove accounts.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
name:
description:
- Name of account.
required: true
username:
description:
- Username of the user to be created if account did not exist.
- Required on C(state=present).
required: false
default: null
password:
description:
- Password of the user to be created if account did not exist.
- Required on C(state=present).
required: false
default: null
first_name:
description:
- First name of the user to be created if account did not exist.
- Required on C(state=present).
required: false
default: null
last_name:
description:
- Last name of the user to be created if account did not exist.
- Required on C(state=present).
required: false
default: null
email:
description:
- Email of the user to be created if account did not exist.
- Required on C(state=present).
required: false
default: null
timezone:
description:
- Timezone of the user to be created if account did not exist.
required: false
default: null
network_domain:
description:
- Network domain of the account.
required: false
default: null
account_type:
description:
- Type of the account.
required: false
default: 'user'
choices: [ 'user', 'root_admin', 'domain_admin' ]
domain:
description:
- Domain the account is related to.
required: false
default: 'ROOT'
state:
description:
- State of the account.
- C(unlocked) is an alias for C(enabled).
required: false
default: 'present'
choices: [ 'present', 'absent', 'enabled', 'disabled', 'locked', 'unlocked' ]
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# create an account in domain 'CUSTOMERS'
local_action:
module: cs_account
name: customer_xy
username: customer_xy
password: S3Cur3
last_name: Doe
first_name: John
email: john.doe@example.com
domain: CUSTOMERS
# Lock an existing account in domain 'CUSTOMERS'
local_action:
module: cs_account
name: customer_xy
domain: CUSTOMERS
state: locked
# Disable an existing account in domain 'CUSTOMERS'
local_action:
module: cs_account
name: customer_xy
domain: CUSTOMERS
state: disabled
# Enable an existing account in domain 'CUSTOMERS'
local_action:
module: cs_account
name: customer_xy
domain: CUSTOMERS
state: enabled
# Remove an account in domain 'CUSTOMERS'
local_action:
module: cs_account
name: customer_xy
domain: CUSTOMERS
state: absent
'''
RETURN = '''
---
id:
description: UUID of the account.
returned: success
type: string
sample: 87b1e0ce-4e01-11e4-bb66-0050569e64b8
name:
description: Name of the account.
returned: success
type: string
sample: linus@example.com
account_type:
description: Type of the account.
returned: success
type: string
sample: user
state:
description: State of the account.
returned: success
type: string
sample: enabled
network_domain:
description: Network domain of the account.
returned: success
type: string
sample: example.local
domain:
description: Domain the account is related.
returned: success
type: string
sample: ROOT
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
class AnsibleCloudStackAccount(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackAccount, self).__init__(module)
self.returns = {
'networkdomain': 'network_domain',
}
self.account = None
self.account_types = {
'user': 0,
'root_admin': 1,
'domain_admin': 2,
}
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'),
}
accounts = self.cs.listAccounts(**args)
if accounts:
account_name = self.module.params.get('name')
for a in accounts['account']:
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.cs.enableAccount(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.disableAccount(**args)
if 'errortext' in account:
self.module.fail_json(msg="Failed: '%s'" % account['errortext'])
poll_async = self.module.params.get('poll_async')
if poll_async:
account = self.poll_job(account, 'account')
return account
def present_account(self):
required_params = [
'email',
'username',
'password',
'first_name',
'last_name',
]
self.module.fail_on_missing_params(required_params=required_params)
account = self.get_account()
if not account:
self.result['changed'] = True
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')
}
if not self.module.check_mode:
res = self.cs.createAccount(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.deleteAccount(id=account['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(default=None),
domain=dict(default='ROOT'),
email=dict(default=None),
first_name=dict(default=None),
last_name=dict(default=None),
username=dict(default=None),
password=dict(default=None, no_log=True),
timezone=dict(default=None),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,255 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_affinitygroup
short_description: Manages affinity groups on Apache CloudStack based clouds.
description:
- Create and remove affinity groups.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the affinity group.
required: true
affinty_type:
description:
- Type of the affinity group. If not specified, first found affinity type is used.
required: false
default: null
description:
description:
- Description of the affinity group.
required: false
default: null
state:
description:
- State of the affinity group.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
domain:
description:
- Domain the affinity group is related to.
required: false
default: null
account:
description:
- Account the affinity group is related to.
required: false
default: null
project:
description:
- Name of the project the affinity group is related to.
required: false
default: null
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Create a affinity group
- local_action:
module: cs_affinitygroup
name: haproxy
affinty_type: host anti-affinity
# Remove a affinity group
- local_action:
module: cs_affinitygroup
name: haproxy
state: absent
'''
RETURN = '''
---
id:
description: UUID of the affinity group.
returned: success
type: string
sample: 87b1e0ce-4e01-11e4-bb66-0050569e64b8
name:
description: Name of affinity group.
returned: success
type: string
sample: app
description:
description: Description of affinity group.
returned: success
type: string
sample: application affinity group
affinity_type:
description: Type of affinity group.
returned: success
type: string
sample: host anti-affinity
project:
description: Name of project the affinity group is related to.
returned: success
type: string
sample: Production
domain:
description: Domain the affinity group is related to.
returned: success
type: string
sample: example domain
account:
description: Account the affinity group is related to.
returned: success
type: string
sample: example account
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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.cs.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('affinty_type')
affinity_types = self.cs.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 '%s' not found" % 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.cs.createAffinityGroup(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.deleteAffinityGroup(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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),
affinty_type=dict(default=None),
description=dict(default=None),
state=dict(choices=['present', 'absent'], default='present'),
domain=dict(default=None),
account=dict(default=None),
project=dict(default=None),
poll_async=dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,421 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_cluster
short_description: Manages host clusters on Apache CloudStack based clouds.
description:
- Create, update and remove clusters.
version_added: "2.1"
author: "René Moser (@resmo)"
options:
name:
description:
- name of the cluster.
required: true
zone:
description:
- Name of the zone in which the cluster belongs to.
- If not set, default zone is used.
required: false
default: null
pod:
description:
- Name of the pod in which the cluster belongs to.
required: false
default: null
cluster_type:
description:
- Type of the cluster.
- Required if C(state=present)
required: false
default: null
choices: [ 'CloudManaged', 'ExternalManaged' ]
hypervisor:
description:
- Name the hypervisor to be used.
- Required if C(state=present).
required: false
default: none
choices: [ 'KVM', 'VMware', 'BareMetal', 'XenServer', 'LXC', 'HyperV', 'UCS', 'OVM' ]
url:
description:
- URL for the cluster
required: false
default: null
username:
description:
- Username for the cluster.
required: false
default: null
password:
description:
- Password for the cluster.
required: false
default: null
guest_vswitch_name:
description:
- Name of virtual switch used for guest traffic in the cluster.
- This would override zone wide traffic label setting.
required: false
default: null
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)
required: false
default: null
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.
required: false
default: null
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)
required: false
default: null
choices: [ 'vmwaresvs', 'vmwaredvs' ]
vms_ip_address:
description:
- IP address of the VSM associated with this cluster.
required: false
default: null
vms_username:
description:
- Username for the VSM associated with this cluster.
required: false
default: null
vms_password:
description:
- Password for the VSM associated with this cluster.
required: false
default: null
ovm3_cluster:
description:
- Ovm3 native OCFS2 clustering enabled for cluster.
required: false
default: null
ovm3_pool:
description:
- Ovm3 native pooling enabled for cluster.
required: false
default: null
ovm3_vip:
description:
- Ovm3 vip to use for pool (and cluster).
required: false
default: null
state:
description:
- State of the cluster.
required: false
default: 'present'
choices: [ 'present', 'absent', 'disabled', 'enabled' ]
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Ensure a cluster is present
- local_action:
module: cs_cluster
name: kvm-cluster-01
zone: ch-zrh-ix-01
hypervisor: KVM
cluster_type: CloudManaged
# Ensure a cluster is disabled
- local_action:
module: cs_cluster
name: kvm-cluster-01
zone: ch-zrh-ix-01
state: disabled
# Ensure a cluster is enabled
- local_action:
module: cs_cluster
name: kvm-cluster-01
zone: ch-zrh-ix-01
state: enabled
# Ensure a cluster is absent
- local_action:
module: cs_cluster
name: kvm-cluster-01
zone: ch-zrh-ix-01
state: absent
'''
RETURN = '''
---
id:
description: UUID of the cluster.
returned: success
type: string
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the cluster.
returned: success
type: string
sample: cluster01
allocation_state:
description: State of the cluster.
returned: success
type: string
sample: Enabled
cluster_type:
description: Type of the cluster.
returned: success
type: string
sample: ExternalManaged
cpu_overcommit_ratio:
description: The CPU overcommit ratio of the cluster.
returned: success
type: string
sample: 1.0
memory_overcommit_ratio:
description: The memory overcommit ratio of the cluster.
returned: success
type: string
sample: 1.0
managed_state:
description: Whether this cluster is managed by CloudStack.
returned: success
type: string
sample: Managed
ovm3_vip:
description: Ovm3 VIP to use for pooling and/or clustering
returned: success
type: string
sample: 10.10.10.101
hypervisor:
description: Hypervisor of the cluster
returned: success
type: string
sample: VMware
zone:
description: Name of zone the cluster is in.
returned: success
type: string
sample: ch-gva-2
pod:
description: Name of pod the cluster is in.
returned: success
type: string
sample: pod01
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
class AnsibleCloudStackCluster(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackCluster, self).__init__(module)
self.returns = {
'allocationstate': 'allocation_state',
'hypervisortype': 'hypervisor',
'clustertype': 'cluster_type',
'podname': 'pod',
'managedstate': 'managed_state',
'memoryovercommitratio': 'memory_overcommit_ratio',
'cpuovercommitratio': 'cpu_overcommit_ratio',
'ovm3vip': 'ovm3_vip',
}
self.cluster = None
def _get_common_cluster_args(self):
args = {
'clustername': self.module.params.get('name'),
'hypervisor': self.module.params.get('hypervisor'),
'clustertype': self.module.params.get('cluster_type'),
}
state = self.module.params.get('state')
if state in ['enabled', 'disabled']:
args['allocationstate'] = state.capitalize()
return args
def get_pod(self, key=None):
args = {
'name': self.module.params.get('pod'),
'zoneid': self.get_zone(key='id'),
}
pods = self.cs.listPods(**args)
if pods:
return self._get_by_key(key, pods['pod'][0])
self.module.fail_json(msg="Pod %s not found in zone %s." % (self.module.params.get('pod'), self.get_zone(key='name')))
def get_cluster(self):
if not self.cluster:
args = {}
uuid = self.module.params.get('id')
if uuid:
args['id'] = uuid
clusters = self.cs.listClusters(**args)
if clusters:
self.cluster = clusters['cluster'][0]
return self.cluster
args['name'] = self.module.params.get('name')
clusters = self.cs.listClusters(**args)
if clusters:
self.cluster = clusters['cluster'][0]
# fix differnt 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.cs.addCluster(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
# 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.cs.updateCluster(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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:
res = self.cs.deleteCluster(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
return cluster
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name=dict(required=True),
zone=dict(default=None),
pod=dict(default=None),
cluster_type=dict(choices=['CloudManaged', 'ExternalManaged'], default=None),
hypervisor=dict(choices=CS_HYPERVISORS, default=None),
state=dict(choices=['present', 'enabled', 'disabled', 'absent'], default='present'),
url=dict(default=None),
username=dict(default=None),
password=dict(default=None, no_log=True),
guest_vswitch_name=dict(default=None),
guest_vswitch_type=dict(choices=['vmwaresvs', 'vmwaredvs'], default=None),
public_vswitch_name=dict(default=None),
public_vswitch_type=dict(choices=['vmwaresvs', 'vmwaredvs'], default=None),
vms_ip_address=dict(default=None),
vms_username=dict(default=None),
vms_password=dict(default=None, no_log=True),
ovm3_cluster=dict(default=None),
ovm3_pool=dict(default=None),
ovm3_vip=dict(default=None),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,292 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_configuration
short_description: Manages configuration on Apache CloudStack based clouds.
description:
- Manages global, zone, account, storage and cluster configurations.
version_added: "2.1"
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the configuration.
required: true
value:
description:
- Value of the configuration.
required: true
account:
description:
- Ensure the value for corresponding account.
required: false
default: null
domain:
description:
- Domain the account is related to.
- Only considered if C(account) is used.
required: false
default: ROOT
zone:
description:
- Ensure the value for corresponding zone.
required: false
default: null
storage:
description:
- Ensure the value for corresponding storage pool.
required: false
default: null
cluster:
description:
- Ensure the value for corresponding cluster.
required: false
default: null
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Ensure global configuration
- local_action:
module: cs_configuration
name: router.reboot.when.outofband.migrated
value: false
# Ensure zone configuration
- local_action:
module: cs_configuration
name: router.reboot.when.outofband.migrated
zone: ch-gva-01
value: true
# Ensure storage configuration
- local_action:
module: cs_configuration
name: storage.overprovisioning.factor
storage: storage01
value: 2.0
# Ensure account configuration
- local_action:
module: cs_configuration:
name: allow.public.user.templates
value: false
account: acme inc
domain: customers
'''
RETURN = '''
---
category:
description: Category of the configuration.
returned: success
type: string
sample: Advanced
scope:
description: Scope (zone/cluster/storagepool/account) of the parameter that needs to be updated.
returned: success
type: string
sample: storagepool
description:
description: Description of the configuration.
returned: success
type: string
sample: Setup the host to do multipath
name:
description: Name of the configuration.
returned: success
type: string
sample: zone.vlan.capacity.notificationthreshold
value:
description: Value of the configuration.
returned: success
type: string
sample: "0.75"
account:
description: Account of the configuration.
returned: success
type: string
sample: admin
Domain:
description: Domain of account of the configuration.
returned: success
type: string
sample: ROOT
zone:
description: Zone of the configuration.
returned: success
type: string
sample: ch-gva-01
cluster:
description: Cluster of the configuration.
returned: success
type: string
sample: cluster01
storage:
description: Storage of the configuration.
returned: success
type: string
sample: storage01
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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 = {}
args['name'] = self.module.params.get('name')
args['accountid'] = self.get_account(key='id')
args['storageid'] = self.get_storage(key='id')
args['zoneid'] = self.get_zone(key='id')
args['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 = {}
args['name'] = cluster_name
clusters = self.cs.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 = {}
args['name'] = storage_pool_name
storage_pools = self.cs.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()
configurations = self.cs.listConfigurations(**args)
if not configurations:
self.module.fail_json(msg="Configuration %s not found." % args['name'])
configuration = configurations['configuration'][0]
return configuration
def get_value(self):
value = str(self.module.params.get('value'))
if value in ('True', 'False'):
value = value.lower()
return value
def present_configuration(self):
configuration = self.get_configuration()
args = self._get_common_configuration_args()
args['value'] = self.get_value()
if self.has_changed(args, configuration, ['value']):
self.result['changed'] = True
if not self.module.check_mode:
res = self.cs.updateConfiguration(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(default=None),
storage = dict(default=None),
cluster = dict(default=None),
account = dict(default=None),
domain = dict(default='ROOT')
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
acs_configuration = AnsibleCloudStackConfiguration(module)
configuration = acs_configuration.present_configuration()
result = acs_configuration.get_result(configuration)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,274 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_domain
short_description: Manages domains on Apache CloudStack based clouds.
description:
- Create, update and remove domains.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
path:
description:
- Path of the domain.
- Prefix C(ROOT/) or C(/ROOT/) in path is optional.
required: true
network_domain:
description:
- Network domain for networks in the domain.
required: false
default: null
clean_up:
description:
- Clean up all domain resources like child domains and accounts.
- Considered on C(state=absent).
required: false
default: false
state:
description:
- State of the domain.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Create a domain
local_action:
module: cs_domain
path: ROOT/customers
network_domain: customers.example.com
# Create another subdomain
local_action:
module: cs_domain
path: ROOT/customers/xy
network_domain: xy.customers.example.com
# Remove a domain
local_action:
module: cs_domain
path: ROOT/customers/xy
state: absent
'''
RETURN = '''
---
id:
description: UUID of the domain.
returned: success
type: string
sample: 87b1e0ce-4e01-11e4-bb66-0050569e64b8
name:
description: Name of the domain.
returned: success
type: string
sample: customers
path:
description: Domain path.
returned: success
type: string
sample: /ROOT/customers
parent_domain:
description: Parent domain of the domain.
returned: success
type: string
sample: ROOT
network_domain:
description: Network domain of the domain.
returned: success
type: string
sample: example.local
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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 = {}
args['listall'] = True
domains = self.cs.listDomains(**args)
if domains:
for d in domains['domain']:
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 = {}
args['name'] = self.get_name()
args['parentdomainid'] = self.get_parent_domain(key='id')
args['networkdomain'] = self.module.params.get('network_domain')
if not self.module.check_mode:
res = self.cs.createDomain(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
domain = res['domain']
return domain
def update_domain(self, domain):
args = {}
args['id'] = domain['id']
args['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.cs.updateDomain(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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 = {}
args['id'] = domain['id']
args['cleanup'] = self.module.params.get('clean_up')
res = self.cs.deleteDomain(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(default=None),
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
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,226 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
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.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
filter:
description:
- Filter for a specific fact.
required: false
default: null
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
requirements: [ 'yaml' ]
'''
EXAMPLES = '''
# Gather all facts on instances
- name: Gather cloudstack facts
cs_facts:
# Gather specific fact on instances
- name: Gather cloudstack facts
cs_facts: filter=cloudstack_instance_id
'''
RETURN = '''
---
cloudstack_availability_zone:
description: zone the instance is deployed in.
returned: success
type: string
sample: ch-gva-2
cloudstack_instance_id:
description: UUID of the instance.
returned: success
type: string
sample: ab4e80b0-3e7e-4936-bdc5-e334ba5b0139
cloudstack_local_hostname:
description: local hostname of the instance.
returned: success
type: string
sample: VM-ab4e80b0-3e7e-4936-bdc5-e334ba5b0139
cloudstack_local_ipv4:
description: local IPv4 of the instance.
returned: success
type: string
sample: 185.19.28.35
cloudstack_public_hostname:
description: public IPv4 of the router. Same as C(cloudstack_public_ipv4).
returned: success
type: string
sample: VM-ab4e80b0-3e7e-4936-bdc5-e334ba5b0139
cloudstack_public_ipv4:
description: public IPv4 of the router.
returned: success
type: string
sample: 185.19.28.35
cloudstack_service_offering:
description: service offering of the instance.
returned: success
type: string
sample: Micro 512mb 1cpu
cloudstack_user_data:
description: data of the instance provided by users.
returned: success
type: dict
sample: { "bla": "foo" }
'''
import os
try:
import yaml
has_lib_yaml = True
except ImportError:
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):
self.facts = ansible_facts(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.iteritems():
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.load(self._fetch(CS_USERDATA_BASE_URL))
except:
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 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',
]),
),
supports_check_mode=False
)
if not has_lib_yaml:
module.fail_json(msg="missing python library: yaml")
cs_facts = CloudStackFacts().run()
cs_facts_result = dict(changed=False, ansible_facts=cs_facts)
module.exit_json(**cs_facts_result)
from ansible.module_utils.basic import *
from ansible.module_utils.urls import *
from ansible.module_utils.facts import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,433 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_firewall
short_description: Manages firewall rules on Apache CloudStack based clouds.
description:
- Creates and removes firewall rules.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
ip_address:
description:
- Public IP address the ingress rule is assigned to.
- Required if C(type=ingress).
required: false
default: null
network:
description:
- Network the egress rule is related to.
- Required if C(type=egress).
required: false
default: null
state:
description:
- State of the firewall rule.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
type:
description:
- Type of the firewall rule.
required: false
default: 'ingress'
choices: [ 'ingress', 'egress' ]
protocol:
description:
- Protocol of the firewall rule.
- C(all) is only available if C(type=egress)
required: false
default: 'tcp'
choices: [ 'tcp', 'udp', 'icmp', 'all' ]
cidr:
description:
- CIDR (full notation) to be used for firewall rule.
required: false
default: '0.0.0.0/0'
start_port:
description:
- Start port for this rule. Considered if C(protocol=tcp) or C(protocol=udp).
required: false
default: null
aliases: [ 'port' ]
end_port:
description:
- End port for this rule. Considered if C(protocol=tcp) or C(protocol=udp). If not specified, equal C(start_port).
required: false
default: null
icmp_type:
description:
- Type of the icmp message being sent. Considered if C(protocol=icmp).
required: false
default: null
icmp_code:
description:
- Error code for this icmp message. Considered if C(protocol=icmp).
required: false
default: null
domain:
description:
- Domain the firewall rule is related to.
required: false
default: null
account:
description:
- Account the firewall rule is related to.
required: false
default: null
project:
description:
- Name of the project the firewall rule is related to.
required: false
default: null
zone:
description:
- Name of the zone in which the virtual machine is in.
- If not set, default zone is used.
required: false
default: null
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Allow inbound port 80/tcp from 1.2.3.4 to 4.3.2.1
- local_action:
module: cs_firewall
ip_address: 4.3.2.1
port: 80
cidr: 1.2.3.4/32
# Allow inbound tcp/udp port 53 to 4.3.2.1
- local_action:
module: cs_firewall
ip_address: 4.3.2.1
port: 53
protocol: '{{ item }}'
with_items:
- tcp
- udp
# Ensure firewall rule is removed
- local_action:
module: cs_firewall
ip_address: 4.3.2.1
start_port: 8000
end_port: 8888
cidr: 17.0.0.0/8
state: absent
# Allow all outbound traffic
- local_action:
module: cs_firewall
network: my_network
type: egress
protocol: all
# Allow only HTTP outbound traffic for an IP
- local_action:
module: cs_firewall
network: my_network
type: egress
port: 80
cidr: 10.101.1.20
'''
RETURN = '''
---
id:
description: UUID of the rule.
returned: success
type: string
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
ip_address:
description: IP address of the rule if C(type=ingress)
returned: success
type: string
sample: 10.100.212.10
type:
description: Type of the rule.
returned: success
type: string
sample: ingress
cidr:
description: CIDR of the rule.
returned: success
type: string
sample: 0.0.0.0/0
protocol:
description: Protocol of the rule.
returned: success
type: string
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: string
sample: my_network
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
class AnsibleCloudStackFirewall(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackFirewall, self).__init__(module)
self.returns = {
'cidrlist': 'cidr',
'startport': 'start_port',
'endpoint': '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:
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')
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 = {}
args['account'] = self.get_account('name')
args['domainid'] = self.get_domain('id')
args['projectid'] = self.get_project('id')
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")
firewall_rules = self.cs.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")
firewall_rules = self.cs.listFirewallRules(**args)
if firewall_rules and 'firewallrule' in firewall_rules:
for rule in firewall_rules['firewallrule']:
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._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_cidr_match(self, rule, cidr):
return cidr == rule['cidrlist']
def create_firewall_rule(self):
firewall_rule = self.get_firewall_rule()
if not firewall_rule:
self.result['changed'] = True
args = {}
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')
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.cs.createEgressFirewallRule(**args)
else:
args['ipaddressid'] = self.get_ip_address('id')
res = self.cs.createFirewallRule(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
poll_async = self.module.params.get('poll_async')
if poll_async:
firewall_rule = self.poll_job(res, 'firewallrule')
return firewall_rule
def remove_firewall_rule(self):
firewall_rule = self.get_firewall_rule()
if firewall_rule:
self.result['changed'] = True
args = {}
args['id'] = firewall_rule['id']
fw_type = self.module.params.get('type')
if not self.module.check_mode:
if fw_type == 'egress':
res = self.cs.deleteEgressFirewallRule(**args)
else:
res = self.cs.deleteFirewallRule(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
poll_async = self.module.params.get('poll_async')
if poll_async:
res = 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')
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
ip_address = dict(default=None),
network = dict(default=None),
cidr = dict(default='0.0.0.0/0'),
protocol = dict(choices=['tcp', 'udp', 'icmp', 'all'], default='tcp'),
type = dict(choices=['ingress', 'egress'], default='ingress'),
icmp_type = dict(type='int', default=None),
icmp_code = dict(type='int', default=None),
start_port = dict(type='int', aliases=['port'], default=None),
end_port = dict(type='int', default=None),
state = dict(choices=['present', 'absent'], default='present'),
zone = dict(default=None),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
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,
required_one_of = (
['ip_address', 'network'],
),
mutually_exclusive = (
['icmp_type', 'start_port'],
['icmp_type', 'end_port'],
['ip_address', 'network'],
),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,278 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_instance_facts
short_description: Gathering facts from the API of instances from Apache CloudStack based clouds.
description:
- Gathering facts from the API of an instance.
version_added: "2.1"
author: "René Moser (@resmo)"
options:
name:
description:
- Name or display name of the instance.
required: true
domain:
description:
- Domain the instance is related to.
required: false
default: null
account:
description:
- Account the instance is related to.
required: false
default: null
project:
description:
- Project the instance is related to.
required: false
default: null
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
- cs_instance_facts:
name: web-vm-1
delegate_to: localhost
- debug:
var: cloudstack_instance
'''
RETURN = '''
---
cloudstack_instance.id:
description: UUID of the instance.
returned: success
type: string
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
cloudstack_instance.name:
description: Name of the instance.
returned: success
type: string
sample: web-01
cloudstack_instance.display_name:
description: Display name of the instance.
returned: success
type: string
sample: web-01
cloudstack_instance.group:
description: Group name of the instance is related.
returned: success
type: string
sample: web
created:
description: Date of the instance was created.
returned: success
type: string
sample: 2014-12-01T14:57:57+0100
cloudstack_instance.password_enabled:
description: True if password setting is enabled.
returned: success
type: boolean
sample: true
cloudstack_instance.password:
description: The password of the instance if exists.
returned: success
type: string
sample: Ge2oe7Do
cloudstack_instance.ssh_key:
description: Name of SSH key deployed to instance.
returned: success
type: string
sample: key@work
cloudstack_instance.domain:
description: Domain the instance is related to.
returned: success
type: string
sample: example domain
cloudstack_instance.account:
description: Account the instance is related to.
returned: success
type: string
sample: example account
cloudstack_instance.project:
description: Name of project the instance is related to.
returned: success
type: string
sample: Production
cloudstack_instance.default_ip:
description: Default IP address of the instance.
returned: success
type: string
sample: 10.23.37.42
cloudstack_instance.public_ip:
description: Public IP address with instance via static NAT rule.
returned: success
type: string
sample: 1.2.3.4
cloudstack_instance.iso:
description: Name of ISO the instance was deployed with.
returned: success
type: string
sample: Debian-8-64bit
cloudstack_instance.template:
description: Name of template the instance was deployed with.
returned: success
type: string
sample: Debian-8-64bit
cloudstack_instance.service_offering:
description: Name of the service offering the instance has.
returned: success
type: string
sample: 2cpu_2gb
cloudstack_instance.zone:
description: Name of zone the instance is in.
returned: success
type: string
sample: ch-gva-2
cloudstack_instance.state:
description: State of the instance.
returned: success
type: string
sample: Running
cloudstack_instance.security_groups:
description: Security groups the instance is in.
returned: success
type: list
sample: '[ "default" ]'
cloudstack_instance.affinity_groups:
description: Affinity groups the instance is in.
returned: success
type: list
sample: '[ "webservers" ]'
cloudstack_instance.tags:
description: List of resource tags associated with the instance.
returned: success
type: dict
sample: '[ { "key": "foo", "value": "bar" } ]'
cloudstack_instance.hypervisor:
description: Hypervisor related to this instance.
returned: success
type: string
sample: KVM
cloudstack_instance.instance_name:
description: Internal name of the instance (ROOT admin only).
returned: success
type: string
sample: i-44-3992-VM
'''
import base64
# import cloudstack common
from ansible.module_utils.cloudstack import *
class AnsibleCloudStackInstanceFacts(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackInstanceFacts, self).__init__(module)
self.instance = None
self.returns = {
'group': 'group',
'hypervisor': 'hypervisor',
'instancename': 'instance_name',
'publicip': 'public_ip',
'passwordenabled': 'password_enabled',
'password': 'password',
'serviceofferingname': 'service_offering',
'isoname': 'iso',
'templatename': 'template',
'keypair': 'ssh_key',
}
self.facts = {
'cloudstack_instance': None,
}
def get_instance(self):
instance = self.instance
if not instance:
instance_name = self.module.params.get('name')
args = {}
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['projectid'] = self.get_project(key='id')
# Do not pass zoneid, as the instance name must be unique across zones.
instances = self.cs.listVirtualMachines(**args)
if instances:
for v in instances['virtualmachine']:
if instance_name.lower() in [ v['name'].lower(), v['displayname'].lower(), v['id'] ]:
self.instance = v
break
return self.instance
def run(self):
instance = self.get_instance()
if not instance:
self.module.fail_json(msg="Instance not found: %s" % self.module.params.get('name'))
self.facts['cloudstack_instance'] = self.get_result(instance)
return self.facts
def get_result(self, instance):
super(AnsibleCloudStackInstanceFacts, self).get_result(instance)
if instance:
if 'securitygroup' in instance:
security_groups = []
for securitygroup in instance['securitygroup']:
security_groups.append(securitygroup['name'])
self.result['security_groups'] = security_groups
if 'affinitygroup' in instance:
affinity_groups = []
for affinitygroup in instance['affinitygroup']:
affinity_groups.append(affinitygroup['name'])
self.result['affinity_groups'] = affinity_groups
if 'nic' in instance:
for nic in instance['nic']:
if nic['isdefault'] and 'ipaddress' in nic:
self.result['default_ip'] = nic['ipaddress']
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name = dict(required=True),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
))
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=False,
)
cs_instance_facts = AnsibleCloudStackInstanceFacts(module=module).run()
cs_facts_result = dict(changed=False, ansible_facts=cs_instance_facts)
module.exit_json(**cs_facts_result)
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,205 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_instancegroup
short_description: Manages instance groups on Apache CloudStack based clouds.
description:
- Create and remove instance groups.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the instance group.
required: true
domain:
description:
- Domain the instance group is related to.
required: false
default: null
account:
description:
- Account the instance group is related to.
required: false
default: null
project:
description:
- Project the instance group is related to.
required: false
default: null
state:
description:
- State of the instance group.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Create an instance group
- local_action:
module: cs_instancegroup
name: loadbalancers
# Remove an instance group
- local_action:
module: cs_instancegroup
name: loadbalancers
state: absent
'''
RETURN = '''
---
id:
description: UUID of the instance group.
returned: success
type: string
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the instance group.
returned: success
type: string
sample: webservers
created:
description: Date when the instance group was created.
returned: success
type: string
sample: 2015-05-03T15:05:51+0200
domain:
description: Domain the instance group is related to.
returned: success
type: string
sample: example domain
account:
description: Account the instance group is related to.
returned: success
type: string
sample: example account
project:
description: Project the instance group is related to.
returned: success
type: string
sample: example project
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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 = {}
args['account'] = self.get_account('name')
args['domainid'] = self.get_domain('id')
args['projectid'] = self.get_project('id')
instance_groups = self.cs.listInstanceGroups(**args)
if instance_groups:
for g in instance_groups['instancegroup']:
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 = {}
args['name'] = self.module.params.get('name')
args['account'] = self.get_account('name')
args['domainid'] = self.get_domain('id')
args['projectid'] = self.get_project('id')
if not self.module.check_mode:
res = self.cs.createInstanceGroup(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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:
res = self.cs.deleteInstanceGroup(id=instance_group['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(default=None),
account = dict(default=None),
project = dict(default=None),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,244 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, Darren Worrall <darren@iweb.co.uk>
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
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 C(state=present)
version_added: '2.0'
author:
- "Darren Worrall (@dazworrall)"
- "René Moser (@resmo)"
options:
ip_address:
description:
- Public IP address.
- Required if C(state=absent)
required: false
default: null
domain:
description:
- Domain the IP address is related to.
required: false
default: null
network:
description:
- Network the IP address is related to.
required: false
default: null
vpc:
description:
- VPC the IP address is related to.
required: false
default: null
version_added: "2.2"
account:
description:
- Account the IP address is related to.
required: false
default: null
project:
description:
- Name of the project the IP address is related to.
required: false
default: null
zone:
description:
- Name of the zone in which the IP address is in.
- If not set, default zone is used.
required: false
default: null
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Associate an IP address conditonally
- local_action:
module: cs_ip_address
network: My Network
register: ip_address
when: instance.public_ip is undefined
# Disassociate an IP address
- local_action:
module: cs_ip_address
ip_address: 1.2.3.4
state: absent
'''
RETURN = '''
---
id:
description: UUID of the Public IP address.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
ip_address:
description: Public IP address.
returned: success
type: string
sample: 1.2.3.4
zone:
description: Name of zone the IP address is related to.
returned: success
type: string
sample: ch-gva-2
project:
description: Name of project the IP address is related to.
returned: success
type: string
sample: Production
account:
description: Account the IP address is related to.
returned: success
type: string
sample: example account
domain:
description: Domain the IP address is related to.
returned: success
type: string
sample: example domain
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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)
ip_address = self.module.params.get('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.cs.listPublicIpAddresses(**args)
if ip_addresses:
self.ip_address = ip_addresses['publicipaddress'][0]
return self._get_by_key(key, self.ip_address)
def associate_ip_address(self):
self.result['changed'] = True
args = {
'account': self.get_account(key='name'),
'domainid': self.get_domain(key='id'),
'projectid': self.get_project(key='id'),
'networkid': self.get_network(key='id'),
'zoneid': self.get_zone(key='id'),
}
ip_address = None
if not self.module.check_mode:
res = self.cs.associateIpAddress(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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:
res = self.cs.disassociateIpAddress(id=ip_address['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(default=None),
network = dict(default=None),
zone = dict(default=None),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
poll_async = dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
required_if=[
('state', 'absent', ['ip_address']),
],
supports_check_mode=True
)
try:
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.associate_ip_address()
result = acs_ip_address.get_result(ip_address)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,339 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_iso
short_description: Manages ISO images on Apache CloudStack based clouds.
description:
- Register and remove ISO images.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the ISO.
required: true
url:
description:
- URL where the ISO can be downloaded from. Required if C(state) is present.
required: false
default: null
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 C(state) is present.
required: false
default: null
is_ready:
description:
- This flag is used for searching existing ISOs. If set to C(true), it will only list ISO ready for deployment e.g. successfully downloaded and installed. Recommended to set it to C(false).
required: false
default: false
aliases: []
is_public:
description:
- Register the ISO to be publicly available to all users. Only used if C(state) is present.
required: false
default: false
is_featured:
description:
- Register the ISO to be featured. Only used if C(state) is present.
required: false
default: false
is_dynamically_scalable:
description:
- Register the ISO having XS/VMWare tools installed inorder to support dynamic scaling of VM cpu/memory. Only used if C(state) is present.
required: false
default: false
aliases: []
checksum:
description:
- The MD5 checksum value of this ISO. If set, we search by checksum instead of name.
required: false
default: false
bootable:
description:
- Register the ISO to be bootable. Only used if C(state) is present.
required: false
default: true
domain:
description:
- Domain the ISO is related to.
required: false
default: null
account:
description:
- Account the ISO is related to.
required: false
default: null
project:
description:
- Name of the project the ISO to be registered in.
required: false
default: null
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.
required: false
default: null
iso_filter:
description:
- Name of the filter used to search for the ISO.
required: false
default: 'self'
choices: [ 'featured', 'self', 'selfexecutable','sharedexecutable','executable', 'community' ]
state:
description:
- State of the ISO.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Register an ISO if ISO name does not already exist.
- local_action:
module: 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)
# Register an ISO with given name if ISO md5 checksum does not already exist.
- local_action:
module: 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
# Remove an ISO by name
- local_action:
module: cs_iso
name: Debian 7 64-bit
state: absent
# Remove an ISO by checksum
- local_action:
module: cs_iso
name: Debian 7 64-bit
checksum: 0b31bccccb048d20b551f70830bb7ad0
state: absent
'''
RETURN = '''
---
id:
description: UUID of the ISO.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: Name of the ISO.
returned: success
type: string
sample: Debian 7 64-bit
display_text:
description: Text to be displayed of the ISO.
returned: success
type: string
sample: Debian 7.7 64-bit minimal 2015-03-19
zone:
description: Name of zone the ISO is registered in.
returned: success
type: string
sample: zuerich
status:
description: Status of the ISO.
returned: success
type: string
sample: Successfully Installed
is_ready:
description: True if the ISO is ready to be deployed from.
returned: success
type: boolean
sample: true
checksum:
description: MD5 checksum of the ISO.
returned: success
type: string
sample: 0b31bccccb048d20b551f70830bb7ad0
created:
description: Date of registering.
returned: success
type: string
sample: 2015-03-29T14:57:06+0200
domain:
description: Domain the ISO is related to.
returned: success
type: string
sample: example domain
account:
description: Account the ISO is related to.
returned: success
type: string
sample: example account
project:
description: Project the ISO is related to.
returned: success
type: string
sample: example project
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
class AnsibleCloudStackIso(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackIso, self).__init__(module)
self.returns = {
'checksum': 'checksum',
'status': 'status',
'isready': 'is_ready',
}
self.iso = None
def register_iso(self):
iso = self.get_iso()
if not iso:
args = {}
args['zoneid'] = self.get_zone('id')
args['domainid'] = self.get_domain('id')
args['account'] = self.get_account('name')
args['projectid'] = self.get_project('id')
args['bootable'] = self.module.params.get('bootable')
args['ostypeid'] = self.get_os_type('id')
args['name'] = self.module.params.get('name')
args['displaytext'] = self.module.params.get('name')
args['checksum'] = self.module.params.get('checksum')
args['isdynamicallyscalable'] = self.module.params.get('is_dynamically_scalable')
args['isfeatured'] = self.module.params.get('is_featured')
args['ispublic'] = self.module.params.get('is_public')
if args['bootable'] and not args['ostypeid']:
self.module.fail_json(msg="OS type 'os_type' is requried if 'bootable=true'.")
args['url'] = self.module.params.get('url')
if not args['url']:
self.module.fail_json(msg="URL is requried.")
self.result['changed'] = True
if not self.module.check_mode:
res = self.cs.registerIso(**args)
iso = res['iso'][0]
return iso
def get_iso(self):
if not self.iso:
args = {}
args['isready'] = self.module.params.get('is_ready')
args['isofilter'] = self.module.params.get('iso_filter')
args['domainid'] = self.get_domain('id')
args['account'] = self.get_account('name')
args['projectid'] = self.get_project('id')
args['zoneid'] = self.get_zone('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.cs.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 remove_iso(self):
iso = self.get_iso()
if iso:
self.result['changed'] = True
args = {}
args['id'] = iso['id']
args['projectid'] = self.get_project('id')
args['zoneid'] = self.get_zone('id')
if not self.module.check_mode:
res = self.cs.deleteIso(**args)
return iso
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name = dict(required=True),
url = dict(default=None),
os_type = dict(default=None),
zone = dict(default=None),
iso_filter = dict(default='self', choices=[ 'featured', 'self', 'selfexecutable','sharedexecutable','executable', 'community' ]),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
checksum = dict(default=None),
is_ready = dict(type='bool', default=False),
bootable = dict(type='bool', default=True),
is_featured = dict(type='bool', default=False),
is_dynamically_scalable = dict(type='bool', default=False),
state = dict(choices=['present', 'absent'], default='present'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
acs_iso = AnsibleCloudStackIso(module)
state = module.params.get('state')
if state in ['absent']:
iso = acs_iso.remove_iso()
else:
iso = acs_iso.register_iso()
result = acs_iso.get_result(iso)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,384 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, Darren Worrall <darren@iweb.co.uk>
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_loadbalancer_rule
short_description: Manages load balancer rules on Apache CloudStack based clouds.
description:
- Add, update and remove load balancer rules.
version_added: '2.0'
author:
- "Darren Worrall (@dazworrall)"
- "René Moser (@resmo)"
options:
name:
description:
- The name of the load balancer rule.
required: true
description:
description:
- The description of the load balancer rule.
required: false
default: null
algorithm:
description:
- Load balancer algorithm
- Required when using C(state=present).
required: false
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 C(state=present).
- Can not be changed once the rule exists due API limitation.
required: false
default: null
public_port:
description:
- The public port from where the network traffic will be load balanced from.
- Required when using C(state=present).
- Can not be changed once the rule exists due API limitation.
required: true
default: null
ip_address:
description:
- Public IP address from where the network traffic will be load balanced from.
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.
required: false
default: false
cidr:
description:
- CIDR (full notation) to be used for firewall rule if required.
required: false
default: null
protocol:
description:
- The protocol to be used on the load balancer
required: false
default: null
project:
description:
- Name of the project the load balancer IP address is related to.
required: false
default: null
state:
description:
- State of the rule.
required: true
default: 'present'
choices: [ 'present', 'absent' ]
domain:
description:
- Domain the rule is related to.
required: false
default: null
account:
description:
- Account the rule is related to.
required: false
default: null
zone:
description:
- Name of the zone in which the rule shoud be created.
- If not set, default zone is used.
required: false
default: null
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Create a load balancer rule
- local_action:
module: cs_loadbalancer_rule
name: balance_http
public_ip: 1.2.3.4
algorithm: leastconn
public_port: 80
private_port: 8080
# update algorithm of an existing load balancer rule
- local_action:
module: cs_loadbalancer_rule
name: balance_http
public_ip: 1.2.3.4
algorithm: roundrobin
public_port: 80
private_port: 8080
# Delete a load balancer rule
- local_action:
module: cs_loadbalancer_rule
name: balance_http
public_ip: 1.2.3.4
state: absent
'''
RETURN = '''
---
id:
description: UUID of the rule.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
zone:
description: Name of zone the rule is related to.
returned: success
type: string
sample: ch-gva-2
project:
description: Name of project the rule is related to.
returned: success
type: string
sample: Production
account:
description: Account the rule is related to.
returned: success
type: string
sample: example account
domain:
description: Domain the rule is related to.
returned: success
type: string
sample: example domain
algorithm:
description: Load balancer algorithm used.
returned: success
type: string
sample: "source"
cidr:
description: CIDR to forward traffic from.
returned: success
type: string
sample: ""
name:
description: Name of the rule.
returned: success
type: string
sample: "http-lb"
description:
description: Description of the rule.
returned: success
type: string
sample: "http load balancer rule"
protocol:
description: Protocol of the rule.
returned: success
type: string
sample: "tcp"
public_port:
description: Public port.
returned: success
type: string
sample: 80
private_port:
description: Private IP address.
returned: success
type: string
sample: 80
public_ip:
description: Public IP address.
returned: success
type: string
sample: "1.2.3.4"
tags:
description: List of resource tags associated with the rule.
returned: success
type: dict
sample: '[ { "key": "foo", "value": "bar" } ]'
state:
description: State of the rule.
returned: success
type: string
sample: "Add"
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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.cs.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'),
'publicipid': self.get_ip_address(key='id'),
'name': self.module.params.get('name'),
}
def present_lb_rule(self):
missing_params = []
for required_params in [
'algorithm',
'private_port',
'public_port',
]:
if not self.module.params.get(required_params):
missing_params.append(required_params)
if missing_params:
self.module.fail_json(msg="missing required arguments: %s" % ','.join(missing_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['algorithm'] = self.module.params.get('algorithm')
args['privateport'] = self.module.params.get('private_port')
args['publicport'] = self.module.params.get('public_port')
args['cidrlist'] = self.module.params.get('cidr')
args['description'] = self.module.params.get('description')
args['protocol'] = self.module.params.get('protocol')
res = self.cs.createLoadBalancerRule(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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 = {}
args['id'] = rule['id']
args['algorithm'] = self.module.params.get('algorithm')
args['description'] = self.module.params.get('description')
if self.has_changed(args, rule):
self.result['changed'] = True
if not self.module.check_mode:
res = self.cs.updateLoadBalancerRule(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.deleteLoadBalancerRule(id=rule['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
poll_async = self.module.params.get('poll_async')
if poll_async:
res = self.poll_job(res, 'loadbalancer')
return rule
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name = dict(required=True),
description = dict(default=None),
algorithm = dict(choices=['source', 'roundrobin', 'leastconn'], default='source'),
private_port = dict(type='int', default=None),
public_port = dict(type='int', default=None),
protocol = dict(default=None),
state = dict(choices=['present', 'absent'], default='present'),
ip_address = dict(required=True, aliases=['public_ip']),
cidr = dict(default=None),
project = dict(default=None),
open_firewall = dict(type='bool', default=False),
tags = dict(type='list', aliases=['tag'], default=None),
zone = dict(default=None),
domain = dict(default=None),
account = dict(default=None),
poll_async = dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,364 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, Darren Worrall <darren@iweb.co.uk>
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
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.
version_added: '2.0'
author:
- "Darren Worrall (@dazworrall)"
- "René Moser (@resmo)"
options:
name:
description:
- The name of the load balancer rule.
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 C(name) is not unique.
required: false
default: null
aliases: [ 'public_ip' ]
vms:
description:
- List of VMs to assign to or remove from the rule.
required: true
type: list
aliases: [ 'vm' ]
state:
description:
- Should the VMs be present or absent from the rule.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
project:
description:
- Name of the project the firewall rule is related to.
required: false
default: null
domain:
description:
- Domain the rule is related to.
required: false
default: null
account:
description:
- Account the rule is related to.
required: false
default: null
zone:
description:
- Name of the zone in which the rule should be located.
- If not set, default zone is used.
required: false
default: null
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Add VMs to an exising load balancer
- local_action:
module: cs_loadbalancer_rule_member
name: balance_http
vms:
- web01
- web02
# Remove a VM from an existing load balancer
- local_action:
module: 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
local_action:
module: cs_loadbalancer_rule_member
name: balance_http
vm: "{{ ansible_hostname }}"
state: absent
tasks:
# Perform update
post_tasks:
- name: Add to load balancer
local_action:
module: cs_loadbalancer_rule_member
name: balance_http
vm: "{{ ansible_hostname }}"
state: present
'''
RETURN = '''
---
id:
description: UUID of the rule.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
zone:
description: Name of zone the rule is related to.
returned: success
type: string
sample: ch-gva-2
project:
description: Name of project the rule is related to.
returned: success
type: string
sample: Production
account:
description: Account the rule is related to.
returned: success
type: string
sample: example account
domain:
description: Domain the rule is related to.
returned: success
type: string
sample: example domain
algorithm:
description: Load balancer algorithm used.
returned: success
type: string
sample: "source"
cidr:
description: CIDR to forward traffic from.
returned: success
type: string
sample: ""
name:
description: Name of the rule.
returned: success
type: string
sample: "http-lb"
description:
description: Description of the rule.
returned: success
type: string
sample: "http load balancer rule"
protocol:
description: Protocol of the rule.
returned: success
type: string
sample: "tcp"
public_port:
description: Public port.
returned: success
type: string
sample: 80
private_port:
description: Private IP address.
returned: success
type: string
sample: 80
public_ip:
description: Public IP address.
returned: success
type: string
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: dict
sample: '[ { "key": "foo", "value": "bar" } ]'
state:
description: State of the rule.
returned: success
type: string
sample: "Add"
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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['name'] = self.module.params.get('name')
args['zoneid'] = self.get_zone(key='id')
if self.module.params.get('ip_address'):
args['publicipid'] = self.get_ip_address(key='id')
rules = self.cs.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.cs.listLoadBalancerRuleInstances(id=rule['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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 = self.cs.assignToLoadBalancerRule
to_change = set(wanted_names) - set(existing.keys())
else:
cs_func = self.cs.removeFromLoadBalancerRule
to_change = set(wanted_names) & set(existing.keys())
if not to_change:
return rule
args = self._get_common_args()
vms = self.cs.listVirtualMachines(**args)
to_change_ids = []
for name in to_change:
for vm in vms.get('virtualmachine', []):
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 = cs_func(
id = rule['id'],
virtualmachineids = to_change_ids,
)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(default=None, aliases=['public_ip']),
vms = dict(required=True, aliases=['vm'], type='list'),
state = dict(choices=['present', 'absent'], default='present'),
zone = dict(default=None),
domain = dict(default=None),
project = dict(default=None),
account = dict(default=None),
poll_async = dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,564 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_network
short_description: Manages networks on Apache CloudStack based clouds.
description:
- Create, update, restart and delete networks.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
name:
description:
- Name (case sensitive) of the network.
required: true
display_text:
description:
- Display text of the network.
- If not specified, C(name) will be used as C(display_text).
required: false
default: null
network_offering:
description:
- Name of the offering for the network.
- Required if C(state=present).
required: false
default: null
start_ip:
description:
- The beginning IPv4 address of the network belongs to.
- Only considered on create.
required: false
default: null
end_ip:
description:
- The ending IPv4 address of the network belongs to.
- If not specified, value of C(start_ip) is used.
- Only considered on create.
required: false
default: null
gateway:
description:
- The gateway of the network.
- Required for shared networks and isolated networks when it belongs to a VPC.
- Only considered on create.
required: false
default: null
netmask:
description:
- The netmask of the network.
- Required for shared networks and isolated networks when it belongs to a VPC.
- Only considered on create.
required: false
default: null
start_ipv6:
description:
- The beginning IPv6 address of the network belongs to.
- Only considered on create.
required: false
default: null
end_ipv6:
description:
- The ending IPv6 address of the network belongs to.
- If not specified, value of C(start_ipv6) is used.
- Only considered on create.
required: false
default: null
cidr_ipv6:
description:
- CIDR of IPv6 network, must be at least /64.
- Only considered on create.
required: false
default: null
gateway_ipv6:
description:
- The gateway of the IPv6 network.
- Required for shared networks.
- Only considered on create.
required: false
default: null
vlan:
description:
- The ID or VID of the network.
required: false
default: null
vpc:
description:
- Name of the VPC of the network.
required: false
default: null
isolated_pvlan:
description:
- The isolated private VLAN for this network.
required: false
default: null
clean_up:
description:
- Cleanup old network elements.
- Only considered on C(state=restarted).
required: false
default: false
acl_type:
description:
- Access control type.
- Only considered on create.
required: false
default: account
choices: [ 'account', 'domain' ]
network_domain:
description:
- The network domain.
required: false
default: null
state:
description:
- State of the network.
required: false
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.
required: false
default: null
project:
description:
- Name of the project the network to be deployed in.
required: false
default: null
domain:
description:
- Domain the network is related to.
required: false
default: null
account:
description:
- Account the network is related to.
required: false
default: null
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# create a network
- local_action:
module: cs_network
name: my network
zone: gva-01
network_offering: DefaultIsolatedNetworkOfferingWithSourceNatService
network_domain: example.com
# update a network
- local_action:
module: cs_network
name: my network
display_text: network of domain example.local
network_domain: example.local
# restart a network with clean up
- local_action:
module: cs_network
name: my network
clean_up: yes
state: restared
# remove a network
- local_action:
module: cs_network
name: my network
state: absent
'''
RETURN = '''
---
id:
description: UUID of the network.
returned: success
type: string
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the network.
returned: success
type: string
sample: web project
display_text:
description: Display text of the network.
returned: success
type: string
sample: web project
dns1:
description: IP address of the 1st nameserver.
returned: success
type: string
sample: 1.2.3.4
dns2:
description: IP address of the 2nd nameserver.
returned: success
type: string
sample: 1.2.3.4
cidr:
description: IPv4 network CIDR.
returned: success
type: string
sample: 10.101.64.0/24
gateway:
description: IPv4 gateway.
returned: success
type: string
sample: 10.101.64.1
netmask:
description: IPv4 netmask.
returned: success
type: string
sample: 255.255.255.0
cidr_ipv6:
description: IPv6 network CIDR.
returned: success
type: string
sample: 2001:db8::/64
gateway_ipv6:
description: IPv6 gateway.
returned: success
type: string
sample: 2001:db8::1
state:
description: State of the network.
returned: success
type: string
sample: Implemented
zone:
description: Name of zone.
returned: success
type: string
sample: ch-gva-2
domain:
description: Domain the network is related to.
returned: success
type: string
sample: ROOT
account:
description: Account the network is related to.
returned: success
type: string
sample: example account
project:
description: Name of project.
returned: success
type: string
sample: Production
tags:
description: List of resource tags associated with the network.
returned: success
type: dict
sample: '[ { "key": "foo", "value": "bar" } ]'
acl_type:
description: Access type of the network (Domain, Account).
returned: success
type: string
sample: Account
broadcast_domain_type:
description: Broadcast domain type of the network.
returned: success
type: string
sample: Vlan
type:
description: Type of the network.
returned: success
type: string
sample: Isolated
traffic_type:
description: Traffic type of the network.
returned: success
type: string
sample: Guest
state:
description: State of the network (Allocated, Implemented, Setup).
returned: success
type: string
sample: Allocated
is_persistent:
description: Whether the network is persistent or not.
returned: success
type: boolean
sample: false
network_domain:
description: The network domain
returned: success
type: string
sample: example.local
network_offering:
description: The network offering name.
returned: success
type: string
sample: DefaultIsolatedNetworkOfferingWithSourceNatService
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
class AnsibleCloudStackNetwork(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackNetwork, self).__init__(module)
self.returns = {
'networkdomain': 'network domain',
'networkofferingname': 'network_offering',
'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_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 = {}
args['zoneid'] = self.get_zone(key='id')
network_offerings = self.cs.listNetworkOfferings(**args)
if network_offerings:
for no in network_offerings['networkoffering']:
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 = {}
args['name'] = self.module.params.get('name')
args['displaytext'] = self.get_or_fallback('display_text', 'name')
args['networkdomain'] = self.module.params.get('network_domain')
args['networkofferingid'] = self.get_network_offering(key='id')
return args
def get_network(self):
if not self.network:
network = self.module.params.get('name')
args = {}
args['zoneid'] = self.get_zone(key='id')
args['projectid'] = self.get_project(key='id')
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
networks = self.cs.listNetworks(**args)
if networks:
for n in networks['network']:
if network in [ n['name'], n['displaytext'], n['id']]:
self.network = n
break
return self.network
def present_network(self):
network = self.get_network()
if not network:
network = self.create_network(network)
else:
network = self.update_network(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.cs.updateNetwork(**args)
if 'errortext' in network:
self.module.fail_json(msg="Failed: '%s'" % network['errortext'])
poll_async = self.module.params.get('poll_async')
if network and poll_async:
network = self.poll_job(network, 'network')
return network
def create_network(self, network):
self.result['changed'] = True
args = self._get_args()
args['acltype'] = self.module.params.get('acl_type')
args['zoneid'] = self.get_zone(key='id')
args['projectid'] = self.get_project(key='id')
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['startip'] = self.module.params.get('start_ip')
args['endip'] = self.get_or_fallback('end_ip', 'start_ip')
args['netmask'] = self.module.params.get('netmask')
args['gateway'] = self.module.params.get('gateway')
args['startipv6'] = self.module.params.get('start_ipv6')
args['endipv6'] = self.get_or_fallback('end_ipv6', 'start_ipv6')
args['ip6cidr'] = self.module.params.get('cidr_ipv6')
args['ip6gateway'] = self.module.params.get('gateway_ipv6')
args['vlan'] = self.module.params.get('vlan')
args['isolatedpvlan'] = self.module.params.get('isolated_pvlan')
args['subdomainaccess'] = self.module.params.get('subdomain_access')
args['vpcid'] = self.get_vpc(key='id')
if not self.module.check_mode:
res = self.cs.createNetwork(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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 = {}
args['id'] = network['id']
args['cleanup'] = self.module.params.get('clean_up')
if not self.module.check_mode:
network = self.cs.restartNetwork(**args)
if 'errortext' in network:
self.module.fail_json(msg="Failed: '%s'" % network['errortext'])
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 = {}
args['id'] = network['id']
if not self.module.check_mode:
res = self.cs.deleteNetwork(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
poll_async = self.module.params.get('poll_async')
if res and poll_async:
res = self.poll_job(res, 'network')
return network
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name = dict(required=True),
display_text = dict(default=None),
network_offering = dict(default=None),
zone = dict(default=None),
start_ip = dict(default=None),
end_ip = dict(default=None),
gateway = dict(default=None),
netmask = dict(default=None),
start_ipv6 = dict(default=None),
end_ipv6 = dict(default=None),
cidr_ipv6 = dict(default=None),
gateway_ipv6 = dict(default=None),
vlan = dict(default=None),
vpc = dict(default=None),
isolated_pvlan = dict(default=None),
clean_up = dict(type='bool', default=False),
network_domain = dict(default=None),
state = dict(choices=['present', 'absent', 'restarted' ], default='present'),
acl_type = dict(choices=['account', 'domain'], default='account'),
project = dict(default=None),
domain = dict(default=None),
account = dict(default=None),
poll_async = dict(type='bool', default=True),
))
required_together = cs_required_together()
required_together.extend([
['start_ip', 'netmask', 'gateway'],
['start_ipv6', 'cidr_ipv6', 'gateway_ipv6'],
])
module = AnsibleModule(
argument_spec=argument_spec,
required_together=required_together,
supports_check_mode=True
)
try:
acs_network = AnsibleCloudStackNetwork(module)
state = module.params.get('state')
if state in ['absent']:
network = acs_network.absent_network()
elif state in ['restarted']:
network = acs_network.restart_network()
else:
network = acs_network.present_network()
result = acs_network.get_result(network)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,297 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_nic
short_description: Manages NICs and secondary IPs of an instance on Apache CloudStack based clouds.
description:
- Add and remove secondary IPs to and from a NIC.
version_added: "2.3"
author: "René Moser (@resmo)"
options:
vm:
description:
- Name of instance.
required: true
aliases: ['name']
network:
description:
- Name of the network.
- Required to find the NIC if instance has multiple networks assigned.
required: false
default: null
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.
required: false
default: null
aliases: ['secondary_ip']
vpc:
description:
- Name of the VPC the C(vm) is related to.
required: false
default: null
domain:
description:
- Domain the instance is related to.
required: false
default: null
account:
description:
- Account the instance is related to.
required: false
default: null
project:
description:
- Name of the project the instance is deployed in.
required: false
default: null
zone:
description:
- Name of the zone in which the instance is deployed in.
- If not set, default zone is used.
required: false
default: null
state:
description:
- State of the ipaddress.
required: false
default: "present"
choices: [ 'present', 'absent' ]
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Assign a specific IP to the default NIC of the VM
- local_action:
module: cs_nic
vm: customer_xy
vm_guest_ip: 10.10.10.10
# Assign an IP to the default NIC of the VM
# Note: If vm_guest_ip is not set, you will get a new IP address on every run.
- local_action:
module: cs_nic
vm: customer_xy
# Remove a specific IP from the default NIC
- local_action:
module: cs_nic
vm: customer_xy
vm_guest_ip: 10.10.10.10
state: absent
'''
RETURN = '''
---
id:
description: UUID of the nic.
returned: success
type: string
sample: 87b1e0ce-4e01-11e4-bb66-0050569e64b8
vm:
description: Name of the VM.
returned: success
type: string
sample: web-01
ip_address:
description: Primary IP of the NIC.
returned: success
type: string
sample: 10.10.10.10
netmask:
description: Netmask of the NIC.
returned: success
type: string
sample: 255.255.255.0
mac_address:
description: MAC address of the NIC.
returned: success
type: string
sample: 02:00:33:31:00:e4
vm_guest_ip:
description: Secondary IP of the NIC.
returned: success
type: string
sample: 10.10.10.10
network:
description: Name of the network if not default.
returned: success
type: string
sample: sync network
domain:
description: Domain the VM is related to.
returned: success
type: string
sample: example domain
account:
description: Account the VM is related to.
returned: success
type: string
sample: example account
project:
description: Name of project the VM is related to.
returned: success
type: string
sample: Production
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.cloudstack import *
class AnsibleCloudStackNic(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackNic, 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'),
'networkdid': self.get_network(key='id'),
}
nics = self.cs.listNics(**args)
if nics:
self.nic = nics['nic'][0]
return self.nic
self.module.fail_json("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(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.cs.addIpToNic(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(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.cs.removeIpFromNic(id=secondary_ip['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % nic['errortext'])
poll_async = self.module.params.get('poll_async')
if poll_async:
self.poll_job(res, 'nicsecondaryip')
return nic
def get_result(self, nic):
super(AnsibleCloudStackNic, 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
self.result['domain'] = self.get_domain(key='path')
self.result['account'] = self.get_account(key='name')
self.result['project'] = self.get_project(key='name')
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
vm=dict(required=True, aliases=['name']),
vm_guest_ip=dict(default=None, aliases=['secondary_ip']),
network=dict(default=None),
vpc=dict(default=None),
state=dict(choices=['present', 'absent'], default='present'),
domain=dict(default=None),
account=dict(default=None),
project=dict(default=None),
zone=dict(default=None),
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'])
])
)
try:
acs_nic = AnsibleCloudStackNic(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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,305 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_pod
short_description: Manages pods on Apache CloudStack based clouds.
description:
- Create, update, delete pods.
version_added: "2.1"
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the pod.
required: true
id:
description:
- uuid of the exising pod.
default: null
required: false
start_ip:
description:
- Starting IP address for the Pod.
- Required on C(state=present)
default: null
required: false
end_ip:
description:
- Ending IP address for the Pod.
default: null
required: false
netmask:
description:
- Netmask for the Pod.
- Required on C(state=present)
default: null
required: false
gateway:
description:
- Gateway for the Pod.
- Required on C(state=present)
default: null
required: false
zone:
description:
- Name of the zone in which the pod belongs to.
- If not set, default zone is used.
required: false
default: null
state:
description:
- State of the pod.
required: false
default: 'present'
choices: [ 'present', 'enabled', 'disabled', 'absent' ]
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Ensure a pod is present
- local_action:
module: 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
# Ensure a pod is disabled
- local_action:
module: cs_pod
name: pod1
zone: ch-zrh-ix-01
state: disabled
# Ensure a pod is enabled
- local_action:
module: cs_pod
name: pod1
zone: ch-zrh-ix-01
state: enabled
# Ensure a pod is absent
- local_action:
module: cs_pod
name: pod1
zone: ch-zrh-ix-01
state: absent
'''
RETURN = '''
---
id:
description: UUID of the pod.
returned: success
type: string
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the pod.
returned: success
type: string
sample: pod01
start_ip:
description: Starting IP of the pod.
returned: success
type: string
sample: 10.100.1.101
end_ip:
description: Ending IP of the pod.
returned: success
type: string
sample: 10.100.1.254
netmask:
description: Netmask of the pod.
returned: success
type: string
sample: 255.255.255.0
gateway:
description: Gateway of the pod.
returned: success
type: string
sample: 10.100.1.1
allocation_state:
description: State of the pod.
returned: success
type: string
sample: Enabled
zone:
description: Name of zone the pod is in.
returned: success
type: string
sample: ch-gva-2
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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 = {}
args['name'] = self.module.params.get('name')
args['zoneid'] = self.get_zone(key='id')
args['startip'] = self.module.params.get('start_ip')
args['endip'] = self.module.params.get('end_ip')
args['netmask'] = self.module.params.get('netmask')
args['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 = {}
uuid = self.module.params.get('id')
if uuid:
args['id'] = uuid
args['zoneid'] = self.get_zone(key='id')
pods = self.cs.listPods(**args)
if pods:
self.pod = pods['pod'][0]
return self.pod
args['name'] = self.module.params.get('name')
args['zoneid'] = self.get_zone(key='id')
pods = self.cs.listPods(**args)
if pods:
self.pod = pods['pod'][0]
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.cs.createPod(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.updatePod(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
pod = res['pod']
return pod
def absent_pod(self):
pod = self.get_pod()
if pod:
self.result['changed'] = True
args = {}
args['id'] = pod['id']
if not self.module.check_mode:
res = self.cs.deletePod(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
return pod
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
id = dict(default=None),
name = dict(required=True),
gateway = dict(default=None),
netmask = dict(default=None),
start_ip = dict(default=None),
end_ip = dict(default=None),
zone = dict(default=None),
state = dict(choices=['present', 'enabled', 'disabled', 'absent'], default='present'),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,387 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_portforward
short_description: Manages port forwarding rules on Apache CloudStack based clouds.
description:
- Create, update and remove port forwarding rules.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
ip_address:
description:
- Public IP address the rule is assigned to.
required: true
vm:
description:
- Name of virtual machine which we make the port forwarding rule for.
- Required if C(state=present).
required: false
default: null
state:
description:
- State of the port forwarding rule.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
protocol:
description:
- Protocol of the port forwarding rule.
required: false
default: 'tcp'
choices: [ 'tcp', 'udp' ]
public_port:
description:
- Start public port for this rule.
required: true
public_end_port:
description:
- End public port for this rule.
- If not specified equal C(public_port).
required: false
default: null
private_port:
description:
- Start private port for this rule.
required: true
private_end_port:
description:
- End private port for this rule.
- If not specified equal C(private_port).
required: false
default: null
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.
required: false
default: false
vm_guest_ip:
description:
- VM guest NIC secondary IP address for the port forwarding rule.
required: false
default: false
domain:
description:
- Domain the C(vm) is related to.
required: false
default: null
account:
description:
- Account the C(vm) is related to.
required: false
default: null
project:
description:
- Name of the project the C(vm) is located in.
required: false
default: null
zone:
description:
- Name of the zone in which the virtual machine is in.
- If not set, default zone is used.
required: false
default: null
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# 1.2.3.4:80 -> web01:8080
- local_action:
module: cs_portforward
ip_address: 1.2.3.4
vm: web01
public_port: 80
private_port: 8080
# forward SSH and open firewall
- local_action:
module: cs_portforward
ip_address: '{{ public_ip }}'
vm: '{{ inventory_hostname }}'
public_port: '{{ ansible_ssh_port }}'
private_port: 22
open_firewall: true
# forward DNS traffic, but do not open firewall
- local_action:
module: cs_portforward
ip_address: 1.2.3.4
vm: '{{ inventory_hostname }}'
public_port: 53
private_port: 53
protocol: udp
# remove ssh port forwarding
- local_action:
module: cs_portforward
ip_address: 1.2.3.4
public_port: 22
private_port: 22
state: absent
'''
RETURN = '''
---
id:
description: UUID of the public IP address.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
ip_address:
description: Public IP address.
returned: success
type: string
sample: 1.2.3.4
protocol:
description: Protocol.
returned: success
type: string
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
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: string
sample: web-01
vm_display_name:
description: Display name of the virtual machine.
returned: success
type: string
sample: web-01
vm_guest_ip:
description: IP of the virtual machine.
returned: success
type: string
sample: 10.101.65.152
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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')
public_end_port = self.get_or_fallback('public_end_port', 'public_port')
private_port = self.module.params.get('private_port')
private_end_port = self.get_or_fallback('private_end_port', 'private_port')
args = {}
args['ipaddressid'] = self.get_ip_address(key='id')
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['projectid'] = self.get_project(key='id')
portforwarding_rules = self.cs.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()
return portforwarding_rule
def create_portforwarding_rule(self):
args = {}
args['protocol'] = self.module.params.get('protocol')
args['publicport'] = self.module.params.get('public_port')
args['publicendport'] = self.get_or_fallback('public_end_port', 'public_port')
args['privateport'] = self.module.params.get('private_port')
args['privateendport'] = self.get_or_fallback('private_end_port', 'private_port')
args['openfirewall'] = self.module.params.get('open_firewall')
args['vmguestip'] = self.get_vm_guest_ip()
args['ipaddressid'] = self.get_ip_address(key='id')
args['virtualmachineid'] = self.get_vm(key='id')
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
portforwarding_rule = None
self.result['changed'] = True
if not self.module.check_mode:
portforwarding_rule = self.cs.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 = {}
args['protocol'] = self.module.params.get('protocol')
args['publicport'] = self.module.params.get('public_port')
args['publicendport'] = self.get_or_fallback('public_end_port', 'public_port')
args['privateport'] = self.module.params.get('private_port')
args['privateendport'] = self.get_or_fallback('private_end_port', 'private_port')
args['vmguestip'] = self.get_vm_guest_ip()
args['ipaddressid'] = self.get_ip_address(key='id')
args['virtualmachineid'] = self.get_vm(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.cs.updatePortForwardingRule(**args)
self.absent_portforwarding_rule()
portforwarding_rule = self.cs.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 = {}
args['id'] = portforwarding_rule['id']
if not self.module.check_mode:
res = self.cs.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:
# Bad bad API does not always return int when it should.
for search_key, return_key in self.returns_to_int.iteritems():
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', default=None),
private_port = dict(type='int', required=True),
private_end_port = dict(type='int', default=None),
state = dict(choices=['present', 'absent'], default='present'),
open_firewall = dict(type='bool', default=False),
vm_guest_ip = dict(default=None),
vm = dict(default=None),
zone = dict(default=None),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
poll_async = dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,311 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_project
short_description: Manages projects on Apache CloudStack based clouds.
description:
- Create, update, suspend, activate and remove projects.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the project.
required: true
display_text:
description:
- Display text of the project.
- If not specified, C(name) will be used as C(display_text).
required: false
default: null
state:
description:
- State of the project.
required: false
default: 'present'
choices: [ 'present', 'absent', 'active', 'suspended' ]
domain:
description:
- Domain the project is related to.
required: false
default: null
account:
description:
- Account the project is related to.
required: false
default: null
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. C(tags: [])."
required: false
default: null
version_added: "2.2"
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Create a project
- local_action:
module: cs_project
name: web
tags:
- { key: admin, value: john }
- { key: foo, value: bar }
# Rename a project
- local_action:
module: cs_project
name: web
display_text: my web project
# Suspend an existing project
- local_action:
module: cs_project
name: web
state: suspended
# Activate an existing project
- local_action:
module: cs_project
name: web
state: active
# Remove a project
- local_action:
module: cs_project
name: web
state: absent
'''
RETURN = '''
---
id:
description: UUID of the project.
returned: success
type: string
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the project.
returned: success
type: string
sample: web project
display_text:
description: Display text of the project.
returned: success
type: string
sample: web project
state:
description: State of the project.
returned: success
type: string
sample: Active
domain:
description: Domain the project is related to.
returned: success
type: string
sample: example domain
account:
description: Account the project is related to.
returned: success
type: string
sample: example account
tags:
description: List of resource tags associated with the project.
returned: success
type: dict
sample: '[ { "key": "foo", "value": "bar" } ]'
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
class AnsibleCloudStackProject(AnsibleCloudStack):
def get_project(self):
if not self.project:
project = self.module.params.get('name')
args = {}
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
projects = self.cs.listProjects(**args)
if projects:
for p in projects['project']:
if project.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 = {}
args['id'] = project['id']
args['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.cs.updateProject(**args)
if 'errortext' in project:
self.module.fail_json(msg="Failed: '%s'" % project['errortext'])
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 = {}
args['name'] = self.module.params.get('name')
args['displaytext'] = self.get_or_fallback('display_text', 'name')
args['account'] = self.get_account('name')
args['domainid'] = self.get_domain('id')
if not self.module.check_mode:
project = self.cs.createProject(**args)
if 'errortext' in project:
self.module.fail_json(msg="Failed: '%s'" % project['errortext'])
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 = {}
args['id'] = project['id']
if not self.module.check_mode:
if state == 'suspended':
project = self.cs.suspendProject(**args)
else:
project = self.cs.activateProject(**args)
if 'errortext' in project:
self.module.fail_json(msg="Failed: '%s'" % project['errortext'])
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 = {}
args['id'] = project['id']
if not self.module.check_mode:
res = self.cs.deleteProject(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(default=None),
state = dict(choices=['present', 'absent', 'active', 'suspended' ], default='present'),
domain = dict(default=None),
account = dict(default=None),
poll_async = dict(type='bool', default=True),
tags=dict(type='list', aliases=['tag'], default=None),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,208 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': 'preview',
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_region
short_description: Manages regions on Apache CloudStack based clouds.
description:
- Add, update and remove regions.
version_added: "2.3"
author: "René Moser (@resmo)"
options:
id:
description:
- ID of the region.
- Must be an number (int).
required: true
name:
description:
- Name of the region.
- Required if C(state=present)
required: false
default: null
endpoint:
description:
- Endpoint URL of the region.
- Required if C(state=present)
required: false
default: null
state:
description:
- State of the region.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# create a region
local_action:
module: cs_region
id: 2
name: geneva
endpoint: https://cloud.gva.example.com
# remove a region with ID 2
local_action:
module: 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: string
sample: local
endpoint:
description: Endpoint of the region.
returned: success
type: string
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.cloudstack import *
from ansible.module_utils.basic import AnsibleModule
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.cs.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.cs.addRegion(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.updateRegion(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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:
res = self.cs.removeRegion(id=region['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
return region
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
id=dict(required=True, type='int'),
name=dict(default=None),
endpoint=dict(default=None),
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
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,220 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_resourcelimit
short_description: Manages resource limits on Apache CloudStack based clouds.
description:
- Manage limits of resources for domains, accounts and projects.
version_added: "2.1"
author: "René Moser (@resmo)"
options:
resource_type:
description:
- Type of the resource.
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).
required: false
default: -1
aliases: [ 'max' ]
domain:
description:
- Domain the resource is related to.
required: false
default: null
account:
description:
- Account the resource is related to.
required: false
default: null
project:
description:
- Name of the project the resource is related to.
required: false
default: null
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Update a resource limit for instances of a domain
local_action:
module: cs_resourcelimit
type: instance
limit: 10
domain: customers
# Update a resource limit for instances of an account
local_action:
module: cs_resourcelimit
type: instance
limit: 12
account: moserre
domain: customers
'''
RETURN = '''
---
recource_type:
description: Type of the resource
returned: success
type: string
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: string
sample: example domain
account:
description: Account the resource is related to.
returned: success
type: string
sample: example account
project:
description: Project the resource is related to.
returned: success
type: string
sample: example project
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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 = {}
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['projectid'] = self.get_project(key='id')
args['resourcetype'] = self.get_resource_type()
resource_limit = self.cs.listResourceLimits(**args)
if resource_limit:
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 = {}
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['projectid'] = self.get_project(key='id')
args['resourcetype'] = self.get_resource_type()
args['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.cs.updateResourceLimit(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
resource_limit = res['resourcelimit']
return resource_limit
def get_result(self, resource_limit):
self.result = super(AnsibleCloudStackResourceLimit, self).get_result(resource_limit)
self.result['resource_type'] = self.module.params.get('resource_type')
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
resource_type = dict(required=True, choices=RESOURCE_TYPES.keys(), aliases=['type']),
limit = dict(default=-1, aliases=['max']),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
acs_resource_limit = AnsibleCloudStackResourceLimit(module)
resource_limit = acs_resource_limit.update_resource_limit()
result = acs_resource_limit.get_result(resource_limit)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,378 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_router
short_description: Manages routers on Apache CloudStack based clouds.
description:
- Start, restart, stop and destroy routers.
- C(state=present) is not able to create routers, use M(cs_network) instead.
version_added: "2.2"
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the router.
required: true
service_offering:
description:
- Name or id of the service offering of the router.
required: false
default: null
domain:
description:
- Domain the router is related to.
required: false
default: null
account:
description:
- Account the router is related to.
required: false
default: null
project:
description:
- Name of the project the router is related to.
required: false
default: null
state:
description:
- State of the router.
required: false
default: 'present'
choices: [ 'present', 'absent', 'started', 'stopped', 'restarted' ]
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Ensure the router has the desired service offering, no matter if
# the router is running or not.
- local_action:
module: cs_router
name: r-40-VM
service_offering: System Offering for Software Router
# Ensure started
- local_action:
module: cs_router
name: r-40-VM
state: started
# Ensure started with desired service offering.
# If the service offerings changes, router will be rebooted.
- local_action:
module: cs_router
name: r-40-VM
service_offering: System Offering for Software Router
state: started
# Ensure stopped
- local_action:
module: cs_router
name: r-40-VM
state: stopped
# Remove a router
- local_action:
module: cs_router
name: r-40-VM
state: absent
'''
RETURN = '''
---
id:
description: UUID of the router.
returned: success
type: string
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the router.
returned: success
type: string
sample: r-40-VM
created:
description: Date of the router was created.
returned: success
type: string
sample: 2014-12-01T14:57:57+0100
template_version:
description: Version of the system VM template.
returned: success
type: string
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: string
sample: UNKNOWN
role:
description: Role of the router.
returned: success
type: string
sample: VIRTUAL_ROUTER
zone:
description: Name of zone the router is in.
returned: success
type: string
sample: ch-gva-2
service_offering:
description: Name of the service offering the router has.
returned: success
type: string
sample: System Offering For Software Router
state:
description: State of the router.
returned: success
type: string
sample: Active
domain:
description: Domain the router is related to.
returned: success
type: string
sample: ROOT
account:
description: Account the router is related to.
returned: success
type: string
sample: admin
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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 = {}
args['issystem'] = True
service_offerings = self.cs.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 = {}
args['projectid'] = self.get_project(key='id')
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
routers = self.cs.listRouters(**args)
if routers:
for r in routers['router']:
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 = {}
args['id'] = router['id']
if not self.module.check_mode:
res = self.cs.startRouter(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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 = {}
args['id'] = router['id']
if not self.module.check_mode:
res = self.cs.stopRouter(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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 = {}
args['id'] = router['id']
if not self.module.check_mode:
res = self.cs.rebootRouter(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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 = {}
args['id'] = router['id']
if not self.module.check_mode:
res = self.cs.destroyRouter(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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 = {}
args['id'] = router['id']
args['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.cs.changeServiceForRouter(**args)
if 'errortext' in router:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(default=None),
state = dict(choices=['present', 'started', 'stopped', 'restarted', 'absent'], default="present"),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
poll_async = dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,223 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_securitygroup
short_description: Manages security groups on Apache CloudStack based clouds.
description:
- Create and remove security groups.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the security group.
required: true
description:
description:
- Description of the security group.
required: false
default: null
state:
description:
- State of the security group.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
domain:
description:
- Domain the security group is related to.
required: false
default: null
account:
description:
- Account the security group is related to.
required: false
default: null
project:
description:
- Name of the project the security group to be created in.
required: false
default: null
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Create a security group
- local_action:
module: cs_securitygroup
name: default
description: default security group
# Remove a security group
- local_action:
module: cs_securitygroup
name: default
state: absent
'''
RETURN = '''
---
id:
description: UUID of the security group.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: Name of security group.
returned: success
type: string
sample: app
description:
description: Description of security group.
returned: success
type: string
sample: application security group
tags:
description: List of resource tags associated with the security group.
returned: success
type: dict
sample: '[ { "key": "foo", "value": "bar" } ]'
project:
description: Name of project the security group is related to.
returned: success
type: string
sample: Production
domain:
description: Domain the security group is related to.
returned: success
type: string
sample: example domain
account:
description: Account the security group is related to.
returned: success
type: string
sample: example account
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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 = {}
args['projectid'] = self.get_project(key='id')
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['securitygroupname'] = self.module.params.get('name')
sgs = self.cs.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 = {}
args['name'] = self.module.params.get('name')
args['projectid'] = self.get_project(key='id')
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['description'] = self.module.params.get('description')
if not self.module.check_mode:
res = self.cs.createSecurityGroup(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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 = {}
args['name'] = self.module.params.get('name')
args['projectid'] = self.get_project(key='id')
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
if not self.module.check_mode:
res = self.cs.deleteSecurityGroup(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
return security_group
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name = dict(required=True),
description = dict(default=None),
state = dict(choices=['present', 'absent'], default='present'),
project = dict(default=None),
account = dict(default=None),
domain = dict(default=None),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,425 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_securitygroup_rule
short_description: Manages security group rules on Apache CloudStack based clouds.
description:
- Add and remove security group rules.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
security_group:
description:
- Name of the security group the rule is related to. The security group must be existing.
required: true
state:
description:
- State of the security group rule.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
protocol:
description:
- Protocol of the security group rule.
required: false
default: 'tcp'
choices: [ 'tcp', 'udp', 'icmp', 'ah', 'esp', 'gre' ]
type:
description:
- Ingress or egress security group rule.
required: false
default: 'ingress'
choices: [ 'ingress', 'egress' ]
cidr:
description:
- CIDR (full notation) to be used for security group rule.
required: false
default: '0.0.0.0/0'
user_security_group:
description:
- Security group this rule is based of.
required: false
default: null
start_port:
description:
- Start port for this rule. Required if C(protocol=tcp) or C(protocol=udp).
required: false
default: null
aliases: [ 'port' ]
end_port:
description:
- End port for this rule. Required if C(protocol=tcp) or C(protocol=udp), but C(start_port) will be used if not set.
required: false
default: null
icmp_type:
description:
- Type of the icmp message being sent. Required if C(protocol=icmp).
required: false
default: null
icmp_code:
description:
- Error code for this icmp message. Required if C(protocol=icmp).
required: false
default: null
project:
description:
- Name of the project the security group to be created in.
required: false
default: null
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
---
# Allow inbound port 80/tcp from 1.2.3.4 added to security group 'default'
- local_action:
module: cs_securitygroup_rule
security_group: default
port: 80
cidr: 1.2.3.4/32
# Allow tcp/udp outbound added to security group 'default'
- local_action:
module: cs_securitygroup_rule
security_group: default
type: egress
start_port: 1
end_port: 65535
protocol: '{{ item }}'
with_items:
- tcp
- udp
# Allow inbound icmp from 0.0.0.0/0 added to security group 'default'
- local_action:
module: cs_securitygroup_rule
security_group: default
protocol: icmp
icmp_code: -1
icmp_type: -1
# Remove rule inbound port 80/tcp from 0.0.0.0/0 from security group 'default'
- local_action:
module: cs_securitygroup_rule
security_group: default
port: 80
state: absent
# Allow inbound port 80/tcp from security group web added to security group 'default'
- local_action:
module: cs_securitygroup_rule
security_group: default
port: 80
user_security_group: web
'''
RETURN = '''
---
id:
description: UUID of the of the rule.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
security_group:
description: security group of the rule.
returned: success
type: string
sample: default
type:
description: type of the rule.
returned: success
type: string
sample: ingress
cidr:
description: CIDR of the rule.
returned: success and cidr is defined
type: string
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: string
sample: default
protocol:
description: protocol of the rule.
returned: success
type: string
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
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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 not (start_port and end_port):
self.module.fail_json(msg="no start_port or end_port set for protocol '%s'" % protocol)
if protocol == 'icmp' and not (icmp_type and icmp_code):
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 = {}
args['securitygroupname'] = security_group_name
args['projectid'] = self.get_project('id')
sgs = self.cs.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.cs.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.cs.authorizeSecurityGroupEgress(**args)
if res and 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.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.cs.revokeSecurityGroupEgress(id=rule['ruleid'])
if res and 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(default=None),
protocol = dict(choices=['tcp', 'udp', 'icmp', 'ah', 'esp', 'gre'], default='tcp'),
icmp_type = dict(type='int', default=None),
icmp_code = dict(type='int', default=None),
start_port = dict(type='int', default=None, aliases=['port']),
end_port = dict(type='int', default=None),
state = dict(choices=['present', 'absent'], default='present'),
project = dict(default=None),
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
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,387 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_snapshot_policy
short_description: Manages volume snapshot policies on Apache CloudStack based clouds.
description:
- Create, update and delete volume snapshot policies.
version_added: '2.2'
author: "René Moser (@resmo)"
options:
volume:
description:
- Name of the volume.
- Either C(volume) or C(vm) is required.
required: false
default: null
volume_type:
description:
- Type of the volume.
required: false
default: null
choices:
- DATADISK
- ROOT
version_added: "2.3"
vm:
description:
- Name of the instance to select the volume from.
- Use C(volume_type) if VM has a DATADISK and ROOT volume.
- In case of C(volume_type=DATADISK), additionally use C(device_id) if VM has more than one DATADISK volume.
- Either C(volume) or C(vm) is required.
required: false
default: null
version_added: "2.3"
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.
required: false
default: null
version_added: "2.3"
vpc:
description:
- Name of the vpc the instance is deployed in.
required: false
default: null
version_added: "2.3"
interval_type:
description:
- Interval of the snapshot.
required: false
default: 'daily'
choices: [ 'hourly', 'daily', 'weekly', 'monthly' ]
aliases: [ 'interval' ]
max_snaps:
description:
- Max number of snapshots.
required: false
default: 8
aliases: [ 'max' ]
schedule:
description:
- Time the snapshot is scheduled. Required if C(state=present).
- 'Format for C(interval_type=HOURLY): C(MM)'
- 'Format for C(interval_type=DAILY): C(MM:HH)'
- 'Format for C(interval_type=WEEKLY): C(MM:HH:DD (1-7))'
- 'Format for C(interval_type=MONTHLY): C(MM:HH:DD (1-28))'
required: false
default: null
time_zone:
description:
- Specifies a timezone for this command.
required: false
default: 'UTC'
aliases: [ 'timezone' ]
state:
description:
- State of the snapshot policy.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
domain:
description:
- Domain the volume is related to.
required: false
default: null
account:
description:
- Account the volume is related to.
required: false
default: null
project:
description:
- Name of the project the volume is related to.
required: false
default: null
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Ensure a snapshot policy daily at 1h00 UTC
- local_action:
module: cs_snapshot_policy
volume: ROOT-478
schedule: '00:1'
max_snaps: 3
# Ensure a snapshot policy daily at 1h00 UTC on the second DATADISK of VM web-01
- local_action:
module: cs_snapshot_policy
vm: web-01
volume_type: DATADISK
device_id: 2
schedule: '00:1'
max_snaps: 3
# Ensure a snapshot policy hourly at minute 5 UTC
- local_action:
module: cs_snapshot_policy
volume: ROOT-478
schedule: '5'
interval_type: hourly
max_snaps: 1
# Ensure a snapshot policy weekly on Sunday at 05h00, TZ Europe/Zurich
- local_action:
module: cs_snapshot_policy
volume: ROOT-478
schedule: '00:5:1'
interval_type: weekly
max_snaps: 1
time_zone: 'Europe/Zurich'
# Ensure a snapshot policy is absent
- local_action:
module: cs_snapshot_policy
volume: ROOT-478
interval_type: hourly
state: absent
'''
RETURN = '''
---
id:
description: UUID of the snapshot policy.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
interval_type:
description: interval type of the snapshot policy.
returned: success
type: string
sample: daily
schedule:
description: schedule of the snapshot policy.
returned: success
type: string
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: string
sample: Etc/UTC
volume:
description: the volume of the snapshot policy.
returned: success
type: string
sample: Etc/UTC
zone:
description: Name of zone the volume is related to.
returned: success
type: string
sample: ch-gva-2
project:
description: Name of project the volume is related to.
returned: success
type: string
sample: Production
account:
description: Account the volume is related to.
returned: success
type: string
sample: example account
domain:
description: Domain the volume is related to.
returned: success
type: string
sample: example domain
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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'),
'type': self.module.params.get('volume_type'),
}
volumes = self.cs.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.cs.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.cs.createSnapshotPolicy(**args)
policy = res['snapshotpolicy']
if 'errortext' in policy:
self.module.fail_json(msg="Failed: '%s'" % policy['errortext'])
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:
res = self.cs.deleteSnapshotPolicies(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % policy['errortext'])
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(default=None),
volume_type=dict(choices=['DATADISK', 'ROOT'], default=None),
vm=dict(default=None),
device_id=dict(type='int', default=None),
vpc=dict(default=None),
interval_type=dict(default='daily', choices=['hourly', 'daily', 'weekly', 'monthly'], aliases=['interval']),
schedule=dict(default=None),
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(default=None),
account=dict(default=None),
project=dict(default=None),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
required_one_of = (
['vm', 'volume'],
),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,255 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
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.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
name:
description:
- Name of public key.
required: true
domain:
description:
- Domain the public key is related to.
required: false
default: null
account:
description:
- Account the public key is related to.
required: false
default: null
project:
description:
- Name of the project the public key to be registered in.
required: false
default: null
state:
description:
- State of the public key.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
public_key:
description:
- String of the public key.
required: false
default: null
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# create a new private / public key pair:
- cs_sshkeypair:
name: linus@example.com
delegate_to: localhost
register: key
- debug:
msg: 'Private key is {{ key.private_key }}'
# remove a public key by its name:
- cs_sshkeypair:
name: linus@example.com
state: absent
delegate_to: localhost
# register your existing local public key:
- cs_sshkeypair:
name: linus@example.com
public_key: '{{ lookup('file', '~/.ssh/id_rsa.pub') }}'
delegate_to: localhost
'''
RETURN = '''
---
id:
description: UUID of the SSH public key.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: Name of the SSH public key.
returned: success
type: string
sample: linus@example.com
fingerprint:
description: Fingerprint of the SSH public key.
returned: success
type: string
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: string
sample: "-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQCkeFYjI+4k8bWfIRMzp4pCzhlopNydbbwRu824P5ilD4ATWMUG\nvEtuCQ2Mp5k5Bma30CdYHgh2/SbxC5RxXSUKTUJtTKpoJUy8PAhb1nn9dnfkC2oU\naRVi9NRUgypTIZxMpgooHOxvAzWxbZCyh1W+91Ld3FNaGxTLqTgeevY84wIDAQAB\nAoGAcwQwgLyUwsNB1vmjWwE0QEmvHS4FlhZyahhi4hGfZvbzAxSWHIK7YUT1c8KU\n9XsThEIN8aJ3GvcoL3OAqNKRnoNb14neejVHkYRadhxqc0GVN6AUIyCqoEMpvhFI\nQrinM572ORzv5ffRjCTbvZcYlW+sqFKNo5e8pYIB8TigpFECQQDu7bg9vkvg8xPs\nkP1K+EH0vsR6vUfy+m3euXjnbJtiP7RoTkZk0JQMOmexgy1qQhISWT0e451wd62v\nJ7M0trl5AkEAsDivJnMIlCCCypwPN4tdNUYpe9dtidR1zLmb3SA7wXk5xMUgLZI9\ncWPjBCMt0KKShdDhQ+hjXAyKQLF7iAPuOwJABjdHCMwvmy2XwhrPjCjDRoPEBtFv\n0sFzJE08+QBZVogDwIbwy+SlRWArnHGmN9J6N+H8dhZD3U4vxZPJ1MBAOQJBAJxO\nCv1dt1Q76gbwmYa49LnWO+F+2cgRTVODpr5iYt5fOmBQQRRqzFkRMkFvOqn+KVzM\nQ6LKM6dn8BEl295vLhUCQQCVDWzoSk3GjL3sOjfAUTyAj8VAXM69llaptxWWySPM\nE9pA+8rYmHfohYFx7FD5/KWCO+sfmxTNB48X0uwyE8tO\n-----END RSA PRIVATE KEY-----\n"
'''
try:
import sshpubkeys
has_lib_sshpubkeys = True
except ImportError:
has_lib_sshpubkeys = False
from ansible.module_utils.cloudstack import *
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 = {}
args['domainid'] = self.get_domain('id')
args['account'] = self.get_account('name')
args['projectid'] = self.get_project('id')
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:
res = self.cs.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:
self.cs.deleteSSHKeyPair(**args)
args['publickey'] = public_key
res = self.cs.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 = {}
args['domainid'] = self.get_domain('id')
args['account'] = self.get_account('name')
args['projectid'] = self.get_project('id')
args['name'] = self.module.params.get('name')
if not self.module.check_mode:
res = self.cs.createSSHKeyPair(**args)
ssh_key = res['keypair']
return ssh_key
def remove_ssh_key(self):
ssh_key = self.get_ssh_key()
if ssh_key:
self.result['changed'] = True
args = {}
args['domainid'] = self.get_domain('id')
args['account'] = self.get_account('name')
args['projectid'] = self.get_project('id')
args['name'] = self.module.params.get('name')
if not self.module.check_mode:
res = self.cs.deleteSSHKeyPair(**args)
return ssh_key
def get_ssh_key(self):
if not self.ssh_key:
args = {}
args['domainid'] = self.get_domain('id')
args['account'] = self.get_account('name')
args['projectid'] = self.get_project('id')
args['name'] = self.module.params.get('name')
ssh_keys = self.cs.listSSHKeyPairs(**args)
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)
return key.hash()
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name = dict(required=True),
public_key = dict(default=None),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
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="python library sshpubkeys required: pip install sshpubkeys")
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,282 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_staticnat
short_description: Manages static NATs on Apache CloudStack based clouds.
description:
- Create, update and remove static NATs.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
ip_address:
description:
- Public IP address the static NAT is assigned to.
required: true
vm:
description:
- Name of virtual machine which we make the static NAT for.
- Required if C(state=present).
required: false
default: null
vm_guest_ip:
description:
- VM guest NIC secondary IP address for the static NAT.
required: false
default: false
network:
description:
- Network the IP address is related to.
required: false
default: null
version_added: "2.2"
vpc:
description:
- Name of the VPC.
required: false
default: null
version_added: "2.3"
state:
description:
- State of the static NAT.
required: false
default: 'present'
choices: [ 'present', 'absent' ]
domain:
description:
- Domain the static NAT is related to.
required: false
default: null
account:
description:
- Account the static NAT is related to.
required: false
default: null
project:
description:
- Name of the project the static NAT is related to.
required: false
default: null
zone:
description:
- Name of the zone in which the virtual machine is in.
- If not set, default zone is used.
required: false
default: null
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# create a static NAT: 1.2.3.4 -> web01
- local_action:
module: cs_staticnat
ip_address: 1.2.3.4
vm: web01
# remove a static NAT
- local_action:
module: cs_staticnat
ip_address: 1.2.3.4
state: absent
'''
RETURN = '''
---
id:
description: UUID of the ip_address.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
ip_address:
description: Public IP address.
returned: success
type: string
sample: 1.2.3.4
vm_name:
description: Name of the virtual machine.
returned: success
type: string
sample: web-01
vm_display_name:
description: Display name of the virtual machine.
returned: success
type: string
sample: web-01
vm_guest_ip:
description: IP of the virtual machine.
returned: success
type: string
sample: 10.101.65.152
zone:
description: Name of zone the static NAT is related to.
returned: success
type: string
sample: ch-gva-2
project:
description: Name of project the static NAT is related to.
returned: success
type: string
sample: Production
account:
description: Account the static NAT is related to.
returned: success
type: string
sample: example account
domain:
description: Domain the static NAT is related to.
returned: success
type: string
sample: example domain
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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 = {}
args['virtualmachineid'] = self.get_vm(key='id')
args['ipaddressid'] = ip_address['id']
args['vmguestip'] = self.get_vm_guest_ip()
args['networkid'] = self.get_network(key='id')
if not self.module.check_mode:
res = self.cs.enableStaticNat(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
# 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 = {}
args['virtualmachineid'] = self.get_vm(key='id')
args['ipaddressid'] = ip_address['id']
args['vmguestip'] = self.get_vm_guest_ip()
# 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.cs.disableStaticNat(ipaddressid=ip_address['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
self.poll_job(res, 'staticnat')
res = self.cs.enableStaticNat(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
# 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.cs.disableStaticNat(ipaddressid=ip_address['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(default=None),
vm_guest_ip = dict(default=None),
network = dict(default=None),
vpc = dict(default=None),
state = dict(choices=['present', 'absent'], default='present'),
zone = dict(default=None),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
poll_async = dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,672 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_template
short_description: Manages templates on Apache CloudStack based clouds.
description:
- Register a template from URL, create a template from a ROOT volume of a stopped VM or its snapshot, extract and delete templates.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the template.
required: true
url:
description:
- URL of where the template is hosted on C(state=present).
- URL to which the template would be extracted on C(state=extracted).
- Mutually exclusive with C(vm).
required: false
default: null
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 C(url).
required: false
default: null
snapshot:
description:
- Name of the snapshot, created from the VM ROOT volume, the template will be created from.
- C(vm) is required together with this argument.
required: false
default: null
os_type:
description:
- OS type that best represents the OS of this template.
required: false
default: null
checksum:
description:
- The MD5 checksum value of this template.
- If set, we search by checksum instead of name.
required: false
default: false
is_ready:
description:
- This flag is used for searching existing templates.
- If set to C(true), it will only list template ready for deployment e.g. successfully downloaded and installed.
- Recommended to set it to C(false).
required: false
default: false
is_public:
description:
- Register the template to be publicly available to all users.
- Only used if C(state) is present.
required: false
default: false
is_featured:
description:
- Register the template to be featured.
- Only used if C(state) is present.
required: false
default: false
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 C(state) is present.
required: false
default: false
cross_zones:
description:
- Whether the template should be synced or removed across zones.
- Only used if C(state) is present or absent.
required: false
default: false
mode:
description:
- Mode for the template extraction.
- Only used if C(state=extracted).
required: false
default: 'http_download'
choices: [ 'http_download', 'ftp_upload' ]
domain:
description:
- Domain the template, snapshot or VM is related to.
required: false
default: null
account:
description:
- Account the template, snapshot or VM is related to.
required: false
default: null
project:
description:
- Name of the project the template to be registered in.
required: false
default: null
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.
required: false
default: null
template_filter:
description:
- Name of the filter used to search for the template.
required: false
default: 'self'
choices: [ 'featured', 'self', 'selfexecutable', 'sharedexecutable', 'executable', 'community' ]
hypervisor:
description:
- Name the hypervisor to be used for creating the new template.
- Relevant when using C(state=present).
required: false
default: null
choices: [ 'KVM', 'VMware', 'BareMetal', 'XenServer', 'LXC', 'HyperV', 'UCS', 'OVM' ]
requires_hvm:
description:
- true if this template requires HVM.
required: false
default: false
password_enabled:
description:
- True if the template supports the password reset feature.
required: false
default: false
template_tag:
description:
- the tag for this template.
required: false
default: null
sshkey_enabled:
description:
- True if the template supports the sshkey upload feature.
required: false
default: false
is_routing:
description:
- True if the template type is routing i.e., if template is used to deploy router.
- Only considered if C(url) is used.
required: false
default: false
format:
description:
- The format for the template.
- Relevant when using C(state=present).
required: false
default: null
choices: [ 'QCOW2', 'RAW', 'VHD', 'OVA' ]
is_extractable:
description:
- True if the template or its derivatives are extractable.
required: false
default: false
details:
description:
- Template details in key/value pairs.
required: false
default: null
bits:
description:
- 32 or 64 bits support.
required: false
default: '64'
display_text:
description:
- Display text of the template.
required: false
default: null
state:
description:
- State of the template.
required: false
default: 'present'
choices: [ 'present', 'absent', 'extacted' ]
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Register a systemvm template
- local_action:
module: 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)
# Create a template from a stopped virtual machine's volume
- local_action:
module: cs_template
name: debian-base-template
vm: debian-base-vm
os_type: Debian GNU/Linux 7(64-bit)
zone: tokio-ix
password_enabled: yes
is_public: yes
# Create a template from a virtual machine's root volume snapshot
- local_action:
module: cs_template
name: debian-base-template
vm: debian-base-vm
snapshot: ROOT-233_2015061509114
os_type: Debian GNU/Linux 7(64-bit)
zone: tokio-ix
password_enabled: yes
is_public: yes
# Remove a template
- local_action:
module: cs_template
name: systemvm-4.2
cross_zones: yes
state: absent
'''
RETURN = '''
---
id:
description: UUID of the template.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: Name of the template.
returned: success
type: string
sample: Debian 7 64-bit
display_text:
description: Display text of the template.
returned: success
type: string
sample: Debian 7.7 64-bit minimal 2015-03-19
checksum:
description: MD5 checksum of the template.
returned: success
type: string
sample: 0b31bccccb048d20b551f70830bb7ad0
status:
description: Status of the template.
returned: success
type: string
sample: Download Complete
is_ready:
description: True if the template is ready to be deployed from.
returned: success
type: boolean
sample: true
is_public:
description: True if the template is public.
returned: success
type: boolean
sample: true
is_featured:
description: True if the template is featured.
returned: success
type: boolean
sample: true
is_extractable:
description: True if the template is extractable.
returned: success
type: boolean
sample: true
format:
description: Format of the template.
returned: success
type: string
sample: OVA
os_type:
description: Typo of the OS.
returned: success
type: string
sample: CentOS 6.5 (64-bit)
password_enabled:
description: True if the reset password feature is enabled, false otherwise.
returned: success
type: boolean
sample: false
sshkey_enabled:
description: true if template is sshkey enabled, false otherwise.
returned: success
type: boolean
sample: false
cross_zones:
description: true if the template is managed across all zones, false otherwise.
returned: success
type: boolean
sample: false
template_type:
description: Type of the template.
returned: success
type: string
sample: USER
created:
description: Date of registering.
returned: success
type: string
sample: 2015-03-29T14:57:06+0200
template_tag:
description: Template tag related to this template.
returned: success
type: string
sample: special
hypervisor:
description: Hypervisor related to this template.
returned: success
type: string
sample: VMware
mode:
description: Mode of extraction
returned: success
type: string
sample: http_download
state:
description: State of the extracted template
returned: success
type: string
sample: DOWNLOAD_URL_CREATED
url:
description: Url to which the template is extracted to
returned: success
type: string
sample: "http://1.2.3.4/userdata/eb307f13-4aca-45e8-b157-a414a14e6b04.ova"
tags:
description: List of resource tags associated with the template.
returned: success
type: dict
sample: '[ { "key": "foo", "value": "bar" } ]'
zone:
description: Name of zone the template is registered in.
returned: success
type: string
sample: zuerich
domain:
description: Domain the template is related to.
returned: success
type: string
sample: example domain
account:
description: Account the template is related to.
returned: success
type: string
sample: example account
project:
description: Name of project the template is related to.
returned: success
type: string
sample: Production
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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',
'tempaltetype': 'template_type',
'ostypename': 'os_type',
'crossZones': 'cross_zones',
'isextractable': 'is_extractable',
'isfeatured': 'is_featured',
'ispublic': 'is_public',
'format': 'format',
'hypervisor': 'hypervisor',
'url': 'url',
'extractMode': 'mode',
'state': 'state',
}
def _get_args(self):
args = {}
args['name'] = self.module.params.get('name')
args['displaytext'] = self.get_or_fallback('display_text', 'name')
args['bits'] = self.module.params.get('bits')
args['isdynamicallyscalable'] = self.module.params.get('is_dynamically_scalable')
args['isextractable'] = self.module.params.get('is_extractable')
args['isfeatured'] = self.module.params.get('is_featured')
args['ispublic'] = self.module.params.get('is_public')
args['passwordenabled'] = self.module.params.get('password_enabled')
args['requireshvm'] = self.module.params.get('requires_hvm')
args['templatetag'] = self.module.params.get('template_tag')
args['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 = {}
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['projectid'] = self.get_project(key='id')
args['virtualmachineid'] = self.get_vm(key='id')
args['type'] = "ROOT"
volumes = self.cs.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 = {}
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['projectid'] = self.get_project(key='id')
args['volumeid'] = self.get_root_volume('id')
snapshots = self.cs.listSnapshots(**args)
if snapshots:
for s in snapshots['snapshot']:
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 create_template(self):
template = self.get_template()
if not template:
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.cs.createTemplate(**args)
if 'errortext' in template:
self.module.fail_json(msg="Failed: '%s'" % template['errortext'])
poll_async = self.module.params.get('poll_async')
if poll_async:
template = self.poll_job(template, 'template')
return template
def register_template(self):
required_params = [
'format',
'url',
'hypervisor',
]
self.module.fail_on_missing_params(required_params=required_params)
template = self.get_template()
if not template:
self.result['changed'] = True
args = self._get_args()
args['url'] = self.module.params.get('url')
args['format'] = self.module.params.get('format')
args['checksum'] = self.module.params.get('checksum')
args['isextractable'] = self.module.params.get('is_extractable')
args['isrouting'] = self.module.params.get('is_routing')
args['sshkeyenabled'] = self.module.params.get('sshkey_enabled')
args['hypervisor'] = self.get_hypervisor()
args['domainid'] = self.get_domain(key='id')
args['account'] = self.get_account(key='name')
args['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:
res = self.cs.registerTemplate(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
template = res['template']
return template
def get_template(self):
args = {}
args['isready'] = self.module.params.get('is_ready')
args['templatefilter'] = self.module.params.get('template_filter')
args['domainid'] = self.get_domain(key='id')
args['account'] = self.get_account(key='name')
args['projectid'] = self.get_project(key='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')
templates = self.cs.listTemplates(**args)
if templates:
# if checksum is set, we only look on that.
if not checksum:
return templates['template'][0]
else:
for i in templates['template']:
if 'checksum' in i and i['checksum'] == checksum:
return i
return None
def extract_template(self):
template = self.get_template()
if not template:
self.module.fail_json(msg="Failed: template not found")
args = {}
args['id'] = template['id']
args['url'] = self.module.params.get('url')
args['mode'] = self.module.params.get('mode')
args['zoneid'] = self.get_zone(key='id')
self.result['changed'] = True
if not self.module.check_mode:
template = self.cs.extractTemplate(**args)
if 'errortext' in template:
self.module.fail_json(msg="Failed: '%s'" % template['errortext'])
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 = {}
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.cs.deleteTemplate(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
poll_async = self.module.params.get('poll_async')
if poll_async:
res = self.poll_job(res, 'template')
return template
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name = dict(required=True),
display_text = dict(default=None),
url = dict(default=None),
vm = dict(default=None),
snapshot = dict(default=None),
os_type = dict(default=None),
is_ready = dict(type='bool', default=False),
is_public = dict(type='bool', default=True),
is_featured = dict(type='bool', default=False),
is_dynamically_scalable = dict(type='bool', default=False),
is_extractable = dict(type='bool', default=False),
is_routing = dict(type='bool', default=False),
checksum = dict(default=None),
template_filter = dict(default='self', choices=['featured', 'self', 'selfexecutable', 'sharedexecutable', 'executable', 'community']),
hypervisor = dict(choices=CS_HYPERVISORS, default=None),
requires_hvm = dict(type='bool', default=False),
password_enabled = dict(type='bool', default=False),
template_tag = dict(default=None),
sshkey_enabled = dict(type='bool', default=False),
format = dict(choices=['QCOW2', 'RAW', 'VHD', 'OVA'], default=None),
details = dict(default=None),
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(default=None),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
poll_async = dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
mutually_exclusive = (
['url', 'vm'],
['zone', 'cross_zones'],
),
supports_check_mode=True
)
try:
acs_tpl = AnsibleCloudStackTemplate(module)
state = module.params.get('state')
if state in ['absent']:
tpl = acs_tpl.remove_template()
elif state in ['extracted']:
tpl = acs_tpl.extract_template()
else:
if module.params.get('url'):
tpl = acs_tpl.register_template()
elif module.params.get('vm'):
tpl = acs_tpl.create_template()
else:
module.fail_json(msg="one of the following is required on state=present: url,vm")
result = acs_tpl.get_result(tpl)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,455 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_user
short_description: Manages users on Apache CloudStack based clouds.
description:
- Create, update, disable, lock, enable and remove users.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
username:
description:
- Username of the user.
required: true
account:
description:
- Account the user will be created under.
- Required on C(state=present).
required: false
default: null
password:
description:
- Password of the user to be created.
- Required on C(state=present).
- Only considered on creation and will not be updated if user exists.
required: false
default: null
first_name:
description:
- First name of the user.
- Required on C(state=present).
required: false
default: null
last_name:
description:
- Last name of the user.
- Required on C(state=present).
required: false
default: null
email:
description:
- Email of the user.
- Required on C(state=present).
required: false
default: null
timezone:
description:
- Timezone of the user.
required: false
default: null
domain:
description:
- Domain the user is related to.
required: false
default: 'ROOT'
state:
description:
- State of the user.
- C(unlocked) is an alias for C(enabled).
required: false
default: 'present'
choices: [ 'present', 'absent', 'enabled', 'disabled', 'locked', 'unlocked' ]
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# create an user in domain 'CUSTOMERS'
local_action:
module: cs_user
account: developers
username: johndoe
password: S3Cur3
last_name: Doe
first_name: John
email: john.doe@example.com
domain: CUSTOMERS
# Lock an existing user in domain 'CUSTOMERS'
local_action:
module: cs_user
username: johndoe
domain: CUSTOMERS
state: locked
# Disable an existing user in domain 'CUSTOMERS'
local_action:
module: cs_user
username: johndoe
domain: CUSTOMERS
state: disabled
# Enable/unlock an existing user in domain 'CUSTOMERS'
local_action:
module: cs_user
username: johndoe
domain: CUSTOMERS
state: enabled
# Remove an user in domain 'CUSTOMERS'
local_action:
module: cs_user
name: customer_xy
domain: CUSTOMERS
state: absent
'''
RETURN = '''
---
id:
description: UUID of the user.
returned: success
type: string
sample: 87b1e0ce-4e01-11e4-bb66-0050569e64b8
username:
description: Username of the user.
returned: success
type: string
sample: johndoe
fist_name:
description: First name of the user.
returned: success
type: string
sample: John
last_name:
description: Last name of the user.
returned: success
type: string
sample: Doe
email:
description: Emailof the user.
returned: success
type: string
sample: john.doe@example.com
api_key:
description: API key of the user.
returned: success
type: string
sample: JLhcg8VWi8DoFqL2sSLZMXmGojcLnFrOBTipvBHJjySODcV4mCOo29W2duzPv5cALaZnXj5QxDx3xQfaQt3DKg
api_secret:
description: API secret of the user.
returned: success
type: string
sample: FUELo3LB9fa1UopjTLPdqLv_6OXQMJZv9g9N4B_Ao3HFz8d6IGFCV9MbPFNM8mwz00wbMevja1DoUNDvI8C9-g
account:
description: Account name of the user.
returned: success
type: string
sample: developers
account_type:
description: Type of the account.
returned: success
type: string
sample: user
timezone:
description: Timezone of the user.
returned: success
type: string
sample: enabled
created:
description: Date the user was created.
returned: success
type: string
sample: Doe
state:
description: State of the user.
returned: success
type: string
sample: enabled
domain:
description: Domain the user is related.
returned: success
type: string
sample: ROOT
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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': 'api_secret',
'apikey': '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 = {}
args['domainid'] = self.get_domain('id')
users = self.cs.listUsers(**args)
if users:
user_name = self.module.params.get('username')
for u in users['user']:
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 = {}
args['id'] = user['id']
if not self.module.check_mode:
res = self.cs.enableUser(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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 = {}
args['id'] = user['id']
if not self.module.check_mode:
res = self.cs.lockUser(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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 = {}
args['id'] = user['id']
if not self.module.check_mode:
user = self.cs.disableUser(**args)
if 'errortext' in user:
self.module.fail_json(msg="Failed: '%s'" % user['errortext'])
poll_async = self.module.params.get('poll_async')
if poll_async:
user = self.poll_job(user, 'user')
return user
def present_user(self):
missing_params = []
for required_params in [
'account',
'email',
'password',
'first_name',
'last_name',
]:
if not self.module.params.get(required_params):
missing_params.append(required_params)
if missing_params:
self.module.fail_json(msg="missing required arguments: %s" % ','.join(missing_params))
user = self.get_user()
if user:
user = self._update_user(user)
else:
user = self._create_user(user)
return user
def _create_user(self, user):
self.result['changed'] = True
args = {}
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain('id')
args['username'] = self.module.params.get('username')
args['password'] = self.module.params.get('password')
args['firstname'] = self.module.params.get('first_name')
args['lastname'] = self.module.params.get('last_name')
args['email'] = self.module.params.get('email')
args['timezone'] = self.module.params.get('timezone')
if not self.module.check_mode:
res = self.cs.createUser(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
user = res['user']
# register user api keys
res = self.cs.registerUserKeys(id=user['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
user.update(res['userkeys'])
return user
def _update_user(self, user):
args = {}
args['id'] = user['id']
args['firstname'] = self.module.params.get('first_name')
args['lastname'] = self.module.params.get('last_name')
args['email'] = self.module.params.get('email')
args['timezone'] = self.module.params.get('timezone')
if self.has_changed(args, user):
self.result['changed'] = True
if not self.module.check_mode:
res = self.cs.updateUser(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
user = res['user']
# register user api keys
if 'apikey' not in user:
self.result['changed'] = True
if not self.module.check_mode:
res = self.cs.registerUserKeys(id=user['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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:
res = self.cs.deleteUser(id=user['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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
return self.result
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
username = dict(required=True),
account = dict(default=None),
state = dict(choices=['present', 'absent', 'enabled', 'disabled', 'locked', 'unlocked'], default='present'),
domain = dict(default='ROOT'),
email = dict(default=None),
first_name = dict(default=None),
last_name = dict(default=None),
password = dict(default=None, no_log=True),
timezone = dict(default=None),
poll_async = dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
acs_acc = AnsibleCloudStackUser(module)
state = module.params.get('state')
if state in ['absent']:
user = acs_acc.absent_user()
elif state in ['enabled', 'unlocked']:
user = acs_acc.enable_user()
elif state in ['disabled']:
user = acs_acc.disable_user()
elif state in ['locked']:
user = acs_acc.lock_user()
else:
user = acs_acc.present_user()
result = acs_acc.get_result(user)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,304 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_vmsnapshot
short_description: Manages VM snapshots on Apache CloudStack based clouds.
description:
- Create, remove and revert VM from snapshots.
version_added: '2.0'
author: "René Moser (@resmo)"
options:
name:
description:
- Unique Name of the snapshot. In CloudStack terms display name.
required: true
aliases: ['display_name']
vm:
description:
- Name of the virtual machine.
required: true
description:
description:
- Description of the snapshot.
required: false
default: null
snapshot_memory:
description:
- Snapshot memory if set to true.
required: false
default: false
zone:
description:
- Name of the zone in which the VM is in. If not set, default zone is used.
required: false
default: null
project:
description:
- Name of the project the VM is assigned to.
required: false
default: null
state:
description:
- State of the snapshot.
required: false
default: 'present'
choices: [ 'present', 'absent', 'revert' ]
domain:
description:
- Domain the VM snapshot is related to.
required: false
default: null
account:
description:
- Account the VM snapshot is related to.
required: false
default: null
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Create a VM snapshot of disk and memory before an upgrade
- local_action:
module: cs_vmsnapshot
name: Snapshot before upgrade
vm: web-01
snapshot_memory: yes
# Revert a VM to a snapshot after a failed upgrade
- local_action:
module: cs_vmsnapshot
name: Snapshot before upgrade
vm: web-01
state: revert
# Remove a VM snapshot after successful upgrade
- local_action:
module: cs_vmsnapshot
name: Snapshot before upgrade
vm: web-01
state: absent
'''
RETURN = '''
---
id:
description: UUID of the snapshot.
returned: success
type: string
sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
name:
description: Name of the snapshot.
returned: success
type: string
sample: snapshot before update
display_name:
description: Display name of the snapshot.
returned: success
type: string
sample: snapshot before update
created:
description: date of the snapshot.
returned: success
type: string
sample: 2015-03-29T14:57:06+0200
current:
description: true if snapshot is current
returned: success
type: boolean
sample: True
state:
description: state of the vm snapshot
returned: success
type: string
sample: Allocated
type:
description: type of vm snapshot
returned: success
type: string
sample: DiskAndMemory
description:
description: description of vm snapshot
returned: success
type: string
sample: snapshot brought to you by Ansible
domain:
description: Domain the the vm snapshot is related to.
returned: success
type: string
sample: example domain
account:
description: Account the vm snapshot is related to.
returned: success
type: string
sample: example account
project:
description: Name of project the vm snapshot is related to.
returned: success
type: string
sample: Production
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
class AnsibleCloudStackVmSnapshot(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackVmSnapshot, self).__init__(module)
self.returns = {
'type': 'type',
'current': 'current',
}
def get_snapshot(self):
args = {}
args['virtualmachineid'] = self.get_vm('id')
args['account'] = self.get_account('name')
args['domainid'] = self.get_domain('id')
args['projectid'] = self.get_project('id')
args['name'] = self.module.params.get('name')
snapshots = self.cs.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 = {}
args['virtualmachineid'] = self.get_vm('id')
args['name'] = self.module.params.get('name')
args['description'] = self.module.params.get('description')
args['snapshotmemory'] = self.module.params.get('snapshot_memory')
if not self.module.check_mode:
res = self.cs.createVMSnapshot(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
poll_async = self.module.params.get('poll_async')
if res and poll_async:
snapshot = self.poll_job(res, 'vmsnapshot')
return snapshot
def remove_snapshot(self):
snapshot = self.get_snapshot()
if snapshot:
self.result['changed'] = True
if not self.module.check_mode:
res = self.cs.deleteVMSnapshot(vmsnapshotid=snapshot['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.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(default=None),
zone = dict(default=None),
snapshot_memory = dict(type='bool', default=False),
state = dict(choices=['present', 'absent', 'revert'], default='present'),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
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,
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,496 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2015, Jefferson Girão <jefferson@girao.net>
# (c) 2015, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_volume
short_description: Manages volumes on Apache CloudStack based clouds.
description:
- Create, destroy, attach, detach volumes.
version_added: "2.1"
author:
- "Jefferson Girão (@jeffersongirao)"
- "René Moser (@resmo)"
options:
name:
description:
- Name of the volume.
- C(name) can only contain ASCII letters.
required: true
account:
description:
- Account the volume is related to.
required: false
default: null
custom_id:
description:
- Custom id to the resource.
- Allowed to Root Admins only.
required: false
default: null
disk_offering:
description:
- Name of the disk offering to be used.
- Required one of C(disk_offering), C(snapshot) if volume is not already C(state=present).
required: false
default: null
display_volume:
description:
- Whether to display the volume to the end user or not.
- Allowed to Root Admins only.
required: false
default: true
domain:
description:
- Name of the domain the volume to be deployed in.
required: false
default: null
max_iops:
description:
- Max iops
required: false
default: null
min_iops:
description:
- Min iops
required: false
default: null
project:
description:
- Name of the project the volume to be deployed in.
required: false
default: null
size:
description:
- Size of disk in GB
required: false
default: null
snapshot:
description:
- The snapshot name for the disk volume.
- Required one of C(disk_offering), C(snapshot) if volume is not already C(state=present).
required: false
default: null
force:
description:
- Force removal of volume even it is attached to a VM.
- Considered on C(state=absnet) only.
required: false
default: false
shrink_ok:
description:
- Whether to allow to shrink the volume.
required: false
default: false
vm:
description:
- Name of the virtual machine to attach the volume to.
required: false
default: null
zone:
description:
- Name of the zone in which the volume should be deployed.
- If not set, default zone is used.
required: false
default: null
state:
description:
- State of the volume.
required: false
default: 'present'
choices: [ 'present', 'absent', 'attached', 'detached' ]
poll_async:
description:
- Poll async jobs until job has finished.
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Create volume within project, zone with specified storage options
- local_action:
module: cs_volume
name: web-vm-1-volume
project: Integration
zone: ch-zrh-ix-01
disk_offering: PerfPlus Storage
size: 20
# Create/attach volume to instance
- local_action:
module: cs_volume
name: web-vm-1-volume
disk_offering: PerfPlus Storage
size: 20
vm: web-vm-1
state: attached
# Detach volume
- local_action:
module: cs_volume
name: web-vm-1-volume
state: detached
# Remove volume
- local_action:
module: cs_volume
name: web-vm-1-volume
state: absent
'''
RETURN = '''
id:
description: ID of the volume.
returned: success
type: string
sample:
name:
description: Name of the volume.
returned: success
type: string
sample: web-volume-01
display_name:
description: Display name of the volume.
returned: success
type: string
sample: web-volume-01
group:
description: Group the volume belongs to
returned: success
type: string
sample: web
domain:
description: Domain the volume belongs to
returned: success
type: string
sample: example domain
project:
description: Project the volume belongs to
returned: success
type: string
sample: Production
zone:
description: Name of zone the volume is in.
returned: success
type: string
sample: ch-gva-2
created:
description: Date of the volume was created.
returned: success
type: string
sample: 2014-12-01T14:57:57+0100
attached:
description: Date of the volume was attached.
returned: success
type: string
sample: 2014-12-01T14:57:57+0100
type:
description: Disk volume type.
returned: success
type: string
sample: DATADISK
size:
description: Size of disk volume.
returned: success
type: string
sample: 20
vm:
description: Name of the vm the volume is attached to (not returned when detached)
returned: success
type: string
sample: web-01
state:
description: State of the volume
returned: success
type: string
sample: Attached
device_id:
description: Id of the device on user vm the volume is attached to (not returned when detached)
returned: success
type: string
sample: 1
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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',
}
self.volume = None
#TODO implement in cloudstack utils
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.cs.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.module.fail_json(msg="Disk offering '%s' not found" % disk_offering)
def get_volume(self):
if not self.volume:
args = {}
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['projectid'] = self.get_project(key='id')
args['zoneid'] = self.get_zone(key='id')
args['displayvolume'] = self.module.params.get('display_volume')
args['type'] = 'DATADISK'
volumes = self.cs.listVolumes(**args)
if volumes:
volume_name = self.module.params.get('name')
for v in volumes['volume']:
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 = {}
args['name'] = snapshot
args['account'] = self.get_account('name')
args['domainid'] = self.get_domain('id')
args['projectid'] = self.get_project('id')
snapshots = self.cs.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 = {}
args['name'] = self.module.params.get('name')
args['account'] = self.get_account(key='name')
args['domainid'] = self.get_domain(key='id')
args['diskofferingid'] = disk_offering_id
args['displayvolume'] = self.module.params.get('display_volume')
args['maxiops'] = self.module.params.get('max_iops')
args['miniops'] = self.module.params.get('min_iops')
args['projectid'] = self.get_project(key='id')
args['size'] = self.module.params.get('size')
args['snapshotid'] = snapshot_id
args['zoneid'] = self.get_zone(key='id')
if not self.module.check_mode:
res = self.cs.createVolume(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
poll_async = self.module.params.get('poll_async')
if poll_async:
volume = self.poll_job(res, '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 = {}
args['id'] = volume['id']
args['virtualmachineid'] = self.get_vm(key='id')
args['deviceid'] = self.module.params.get('device_id')
if not self.module.check_mode:
res = self.cs.attachVolume(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.detachVolume(id=volume['id'])
if 'errortext' in volume:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.deleteVolume(id=volume['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
poll_async = self.module.params.get('poll_async')
if poll_async:
res = self.poll_job(res, 'volume')
return volume
def update_volume(self, volume):
args_resize = {}
args_resize['id'] = volume['id']
args_resize['diskofferingid'] = self.get_disk_offering(key='id')
args_resize['maxiops'] = self.module.params.get('max_iops')
args_resize['miniops'] = self.module.params.get('min_iops')
args_resize['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.cs.resizeVolume(**args_resize)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
poll_async = self.module.params.get('poll_async')
if poll_async:
volume = self.poll_job(res, 'volume')
self.volume = volume
return volume
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name = dict(required=True),
disk_offering = dict(default=None),
display_volume = dict(type='bool', default=None),
max_iops = dict(type='int', default=None),
min_iops = dict(type='int', default=None),
size = dict(type='int', default=None),
snapshot = dict(default=None),
vm = dict(default=None),
device_id = dict(type='int', default=None),
custom_id = dict(default=None),
force = dict(type='bool', default=False),
shrink_ok = dict(type='bool', default=False),
state = dict(choices=['present', 'absent', 'attached', 'detached'], default='present'),
zone = dict(default=None),
domain = dict(default=None),
account = dict(default=None),
project = dict(default=None),
poll_async = dict(type='bool', default=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
mutually_exclusive = (
['snapshot', 'disk_offering'],
),
supports_check_mode=True
)
try:
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()
else:
volume = acs_vol.present_volume()
result = acs_vol.get_result(volume)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,391 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it an/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http//www.gnu.or/license/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_vpc
short_description: "Manages VPCs on Apache CloudStack based clouds."
description:
- "Create, update and delete VPCs."
version_added: "2.3"
author: "René Moser (@resmo)"
options:
name:
description:
- "Name of the VPC."
required: true
display_text:
description:
- "Display text of the VPC."
- "If not set, C(name) will be used for creating."
required: false
default: null
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 C(state=present)."
required: false
default: null
network_domain:
description:
- "Network domain for the VPC."
- "All networks inside the VPC will belong to this domain."
required: false
default: null
vpc_offering:
description:
- "Name of the VPC offering."
- "If not set, default VPC offering is used."
required: false
default: null
state:
description:
- "State of the VPC."
required: false
default: present
choices:
- present
- absent
- restarted
domain:
description:
- "Domain the VPC is related to."
required: false
default: null
account:
description:
- "Account the VPC is related to."
required: false
default: null
project:
description:
- "Name of the project the VPC is related to."
required: false
default: null
zone:
description:
- "Name of the zone."
- "If not set, default zone is used."
required: false
default: null
tags:
description:
- "List of tags. Tags are a list of dictionaries having keys C(key) and C(value)."
- "For deleting all tags, set an empty list e.g. C(tags: [])."
required: false
default: null
aliases:
- tag
poll_async:
description:
- "Poll async jobs until job has finished."
required: false
default: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Ensure a VPC is present
- local_action:
module: cs_vpc
name: my_vpc
display_text: My example VPC
cidr: 10.10.0.0/16
# Ensure a VPC is absent
- local_action:
module: cs_vpc
name: my_vpc
state: absent
# Ensure a VPC is restarted
- local_action:
module: cs_vpc
name: my_vpc
state: restarted
'''
RETURN = '''
---
id:
description: "UUID of the VPC."
returned: success
type: string
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: "Name of the VPC."
returned: success
type: string
sample: my_vpc
display_text:
description: "Display text of the VPC."
returned: success
type: string
sample: My example VPC
cidr:
description: "CIDR of the VPC."
returned: success
type: string
sample: 10.10.0.0/16
network_domain:
description: "Network domain of the VPC."
returned: success
type: string
sample: example.com
region_level_vpc:
description: "Whether the VPC is region level or not."
returned: success
type: boolean
sample: true
restart_required:
description: "Wheter the VPC router needs a restart or not."
returned: success
type: boolean
sample: true
distributed_vpc_router:
description: "Whether the VPC uses distributed router or not."
returned: success
type: boolean
sample: true
redundant_vpc_router:
description: "Whether the VPC has redundant routers or not."
returned: success
type: boolean
sample: true
domain:
description: "Domain the VPC is related to."
returned: success
type: string
sample: example domain
account:
description: "Account the VPC is related to."
returned: success
type: string
sample: example account
project:
description: "Name of project the VPC is related to."
returned: success
type: string
sample: Production
zone:
description: "Name of zone the VPC is in."
returned: success
type: string
sample: ch-gva-2
state:
description: "State of the VPC."
returned: success
type: string
sample: Enabled
tags:
description: "List of resource tags associated with the VPC."
returned: success
type: dict
sample: '[ { "key": "foo", "value": "bar" } ]'
'''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.cloudstack import *
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
self.vpc_offering = None
def get_vpc_offering(self, key=None):
if self.vpc_offering:
return self._get_by_key(key, self.vpc_offering)
vpc_offering = self.module.params.get('vpc_offering')
args = {}
if vpc_offering:
args['name'] = vpc_offering
else:
args['isdefault'] = True
vpc_offerings = self.cs.listVPCOfferings(**args)
if vpc_offerings:
self.vpc_offering = vpc_offerings['vpcoffering'][0]
return self._get_by_key(key, self.vpc_offering)
self.module.fail_json(msg="VPC offering '%s' not found" % vpc_offering)
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'),
}
vpcs = self.cs.listVPCs()
if vpcs:
vpc_name = self.module.params.get('name')
for v in vpcs['vpc']:
if vpc_name.lower() in [ v['name'].lower(), v['id']]:
self.vpc = v
break
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'],
}
res = self.cs.restartVPC(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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'),
'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'),
}
self.result['diff']['after'] = args
if not self.module.check_mode:
res = self.cs.createVPC(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.updateVPC(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.deleteVPC(id=vpc['id'])
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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(default=None),
display_text=dict(default=None),
vpc_offering=dict(default=None),
network_domain=dict(default=None),
state=dict(choices=['present', 'absent', 'restarted'], default='present'),
domain=dict(default=None),
account=dict(default=None),
project=dict(default=None),
zone=dict(default=None),
tags=dict(type='list', aliases=['tag'], default=None),
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,
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,406 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_zone
short_description: Manages zones on Apache CloudStack based clouds.
description:
- Create, update and remove zones.
version_added: "2.1"
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the zone.
required: true
id:
description:
- uuid of the exising zone.
default: null
required: false
state:
description:
- State of the zone.
required: false
default: 'present'
choices: [ 'present', 'enabled', 'disabled', 'absent' ]
domain:
description:
- Domain the zone is related to.
- Zone is a public zone if not set.
required: false
default: null
network_domain:
description:
- Network domain for the zone.
required: false
default: null
network_type:
description:
- Network type of the zone.
required: false
default: basic
choices: [ 'basic', 'advanced' ]
dns1:
description:
- First DNS for the zone.
- Required if C(state=present)
required: false
default: null
dns2:
description:
- Second DNS for the zone.
required: false
default: null
internal_dns1:
description:
- First internal DNS for the zone.
- If not set C(dns1) will be used on C(state=present).
required: false
default: null
internal_dns2:
description:
- Second internal DNS for the zone.
required: false
default: null
dns1_ipv6:
description:
- First DNS for IPv6 for the zone.
required: false
default: null
dns2_ipv6:
description:
- Second DNS for IPv6 for the zone.
required: false
default: null
guest_cidr_address:
description:
- Guest CIDR address for the zone.
required: false
default: null
dhcp_provider:
description:
- DHCP provider for the Zone.
required: false
default: null
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
# Ensure a zone is present
- local_action:
module: cs_zone
name: ch-zrh-ix-01
dns1: 8.8.8.8
dns2: 8.8.4.4
network_type: basic
# Ensure a zone is disabled
- local_action:
module: cs_zone
name: ch-zrh-ix-01
state: disabled
# Ensure a zone is enabled
- local_action:
module: cs_zone
name: ch-zrh-ix-01
state: enabled
# Ensure a zone is absent
- local_action:
module: cs_zone
name: ch-zrh-ix-01
state: absent
'''
RETURN = '''
---
id:
description: UUID of the zone.
returned: success
type: string
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
name:
description: Name of the zone.
returned: success
type: string
sample: zone01
dns1:
description: First DNS for the zone.
returned: success
type: string
sample: 8.8.8.8
dns2:
description: Second DNS for the zone.
returned: success
type: string
sample: 8.8.4.4
internal_dns1:
description: First internal DNS for the zone.
returned: success
type: string
sample: 8.8.8.8
internal_dns2:
description: Second internal DNS for the zone.
returned: success
type: string
sample: 8.8.4.4
dns1_ipv6:
description: First IPv6 DNS for the zone.
returned: success
type: string
sample: "2001:4860:4860::8888"
dns2_ipv6:
description: Second IPv6 DNS for the zone.
returned: success
type: string
sample: "2001:4860:4860::8844"
allocation_state:
description: State of the zone.
returned: success
type: string
sample: Enabled
domain:
description: Domain the zone is related to.
returned: success
type: string
sample: ROOT
network_domain:
description: Network domain for the zone.
returned: success
type: string
sample: example.com
network_type:
description: Network type for the zone.
returned: success
type: string
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: string
sample: 10.1.1.0/24
dhcp_provider:
description: DHCP provider for the zone
returned: success
type: string
sample: VirtualRouter
zone_token:
description: Zone token
returned: success
type: string
sample: ccb0a60c-79c8-3230-ab8b-8bdbe8c45bb7
tags:
description: List of resource tags associated with the zone.
returned: success
type: dict
sample: [ { "key": "foo", "value": "bar" } ]
'''
# import cloudstack common
from ansible.module_utils.cloudstack import *
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 = {}
args['name'] = self.module.params.get('name')
args['dns1'] = self.module.params.get('dns1')
args['dns2'] = self.module.params.get('dns2')
args['internaldns1'] = self.get_or_fallback('internal_dns1', 'dns1')
args['internaldns2'] = self.get_or_fallback('internal_dns2', 'dns2')
args['ipv6dns1'] = self.module.params.get('dns1_ipv6')
args['ipv6dns2'] = self.module.params.get('dns2_ipv6')
args['networktype'] = self.module.params.get('network_type')
args['domain'] = self.module.params.get('network_domain')
args['localstorageenabled'] = self.module.params.get('local_storage_enabled')
args['guestcidraddress'] = self.module.params.get('guest_cidr_address')
args['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.cs.listZones(**args)
if zones:
self.zone = zones['zone'][0]
return self.zone
args['name'] = self.module.params.get('name')
zones = self.cs.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.cs.createZone(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
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.cs.updateZone(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
zone = res['zone']
return zone
def absent_zone(self):
zone = self.get_zone()
if zone:
self.result['changed'] = True
args = {}
args['id'] = zone['id']
if not self.module.check_mode:
res = self.cs.deleteZone(**args)
if 'errortext' in res:
self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
return zone
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
id = dict(default=None),
name = dict(required=True),
dns1 = dict(default=None),
dns2 = dict(default=None),
internal_dns1 = dict(default=None),
internal_dns2 = dict(default=None),
dns1_ipv6 = dict(default=None),
dns2_ipv6 = dict(default=None),
network_type = dict(default='basic', choices=['Basic', 'basic', 'Advanced', 'advanced']),
network_domain = dict(default=None),
guest_cidr_address = dict(default=None),
dhcp_provider = dict(default=None),
local_storage_enabled = dict(default=None),
securitygroups_enabled = dict(default=None),
state = dict(choices=['present', 'enabled', 'disabled', 'absent'], default='present'),
domain = dict(default=None),
))
module = AnsibleModule(
argument_spec=argument_spec,
required_together=cs_required_together(),
supports_check_mode=True
)
try:
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)
except CloudStackException as e:
module.fail_json(msg='CloudStackException: %s' % str(e))
module.exit_json(**result)
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,205 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# (c) 2016, René Moser <mail@renemoser.net>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {'status': ['stableinterface'],
'supported_by': 'community',
'version': '1.0'}
DOCUMENTATION = '''
---
module: cs_zone_facts
short_description: Gathering facts of zones from Apache CloudStack based clouds.
description:
- Gathering facts from the API of a zone.
version_added: "2.1"
author: "René Moser (@resmo)"
options:
name:
description:
- Name of the zone.
required: true
extends_documentation_fragment: cloudstack
'''
EXAMPLES = '''
- cs_zone_facts:
name: ch-gva-1
delegate_to: localhost
- debug:
var: cloudstack_zone
'''
RETURN = '''
---
cloudstack_zone.id:
description: UUID of the zone.
returned: success
type: string
sample: 04589590-ac63-4ffc-93f5-b698b8ac38b6
cloudstack_zone.name:
description: Name of the zone.
returned: success
type: string
sample: zone01
cloudstack_zone.dns1:
description: First DNS for the zone.
returned: success
type: string
sample: 8.8.8.8
cloudstack_zone.dns2:
description: Second DNS for the zone.
returned: success
type: string
sample: 8.8.4.4
cloudstack_zone.internal_dns1:
description: First internal DNS for the zone.
returned: success
type: string
sample: 8.8.8.8
cloudstack_zone.internal_dns2:
description: Second internal DNS for the zone.
returned: success
type: string
sample: 8.8.4.4
cloudstack_zone.dns1_ipv6:
description: First IPv6 DNS for the zone.
returned: success
type: string
sample: "2001:4860:4860::8888"
cloudstack_zone.dns2_ipv6:
description: Second IPv6 DNS for the zone.
returned: success
type: string
sample: "2001:4860:4860::8844"
cloudstack_zone.allocation_state:
description: State of the zone.
returned: success
type: string
sample: Enabled
cloudstack_zone.domain:
description: Domain the zone is related to.
returned: success
type: string
sample: ROOT
cloudstack_zone.network_domain:
description: Network domain for the zone.
returned: success
type: string
sample: example.com
cloudstack_zone.network_type:
description: Network type for the zone.
returned: success
type: string
sample: basic
cloudstack_zone.local_storage_enabled:
description: Local storage offering enabled.
returned: success
type: bool
sample: false
cloudstack_zone.securitygroups_enabled:
description: Security groups support is enabled.
returned: success
type: bool
sample: false
cloudstack_zone.guest_cidr_address:
description: Guest CIDR address for the zone
returned: success
type: string
sample: 10.1.1.0/24
cloudstack_zone.dhcp_provider:
description: DHCP provider for the zone
returned: success
type: string
sample: VirtualRouter
cloudstack_zone.zone_token:
description: Zone token
returned: success
type: string
sample: ccb0a60c-79c8-3230-ab8b-8bdbe8c45bb7
cloudstack_zone.tags:
description: List of resource tags associated with the zone.
returned: success
type: dict
sample: [ { "key": "foo", "value": "bar" } ]
'''
import base64
# import cloudstack common
from ansible.module_utils.cloudstack import *
class AnsibleCloudStackZoneFacts(AnsibleCloudStack):
def __init__(self, module):
super(AnsibleCloudStackZoneFacts, self).__init__(module)
self.returns = {
'dns1': 'dns1',
'dns2': 'dns2',
'internaldns1': 'internal_dns1',
'internaldns2': 'internal_dns2',
'ipv6dns1': 'dns1_ipv6',
'ipv6dns2': 'dns2_ipv6',
'domain': 'network_domain',
'networktype': 'network_type',
'securitygroupsenabled': 'securitygroups_enabled',
'localstorageenabled': 'local_storage_enabled',
'guestcidraddress': 'guest_cidr_address',
'dhcpprovider': 'dhcp_provider',
'allocationstate': 'allocation_state',
'zonetoken': 'zone_token',
}
self.facts = {
'cloudstack_zone': None,
}
def get_zone(self):
if not self.zone:
# TODO: add param key signature in get_zone()
self.module.params['zone'] = self.module.params.get('name')
super(AnsibleCloudStackZoneFacts, self).get_zone()
return self.zone
def run(self):
zone = self.get_zone()
self.facts['cloudstack_zone'] = self.get_result(zone)
return self.facts
def main():
argument_spec = cs_argument_spec()
argument_spec.update(dict(
name = dict(required=True),
))
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=False,
)
cs_zone_facts = AnsibleCloudStackZoneFacts(module=module).run()
cs_facts_result = dict(changed=False, ansible_facts=cs_zone_facts)
module.exit_json(**cs_facts_result)
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()