mirror of https://github.com/ansible/ansible.git
Merge branch 'devel' of https://github.com/ansible/ansible-modules-extras into fix-lvol-lvcreate-prompt
commit
f6faf53702
@ -1,10 +1,15 @@
|
||||
sudo: false
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- deadsnakes
|
||||
packages:
|
||||
- python2.4
|
||||
- python2.6
|
||||
script:
|
||||
- python2.4 -m compileall -fq -x 'cloud/' .
|
||||
- python2.4 -m compileall -fq -x 'cloud/|monitoring/zabbix.*\.py|/layman\.py|/maven_artifact\.py|clustering/consul.*\.py|notification/pushbullet\.py' .
|
||||
- python2.6 -m compileall -fq .
|
||||
- python2.7 -m compileall -fq .
|
||||
|
@ -0,0 +1,91 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: ec2_win_password
|
||||
short_description: gets the default administrator password for ec2 windows instances
|
||||
description:
|
||||
- Gets the default administrator password from any EC2 Windows instance. The instance is referenced by its id (e.g. i-XXXXXXX). This module has a dependency on python-boto.
|
||||
version_added: "2.0"
|
||||
author: Rick Mendes
|
||||
options:
|
||||
instance_id:
|
||||
description:
|
||||
- The instance id to get the password data from.
|
||||
required: true
|
||||
key_file:
|
||||
description:
|
||||
- path to the file containing the key pair used on the instance
|
||||
required: true
|
||||
region:
|
||||
description:
|
||||
- The AWS region to use. Must be specified if ec2_url is not used. If not specified then the value of the EC2_REGION environment variable, if any, is used.
|
||||
required: false
|
||||
default: null
|
||||
aliases: [ 'aws_region', 'ec2_region' ]
|
||||
|
||||
extends_documentation_fragment: aws
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Example of getting a password
|
||||
tasks:
|
||||
- name: get the Administrator password
|
||||
ec2_win_password:
|
||||
profile: my-boto-profile
|
||||
instance_id: i-XXXXXX
|
||||
region: us-east-1
|
||||
key_file: "~/aws-creds/my_test_key.pem"
|
||||
'''
|
||||
|
||||
from base64 import b64decode
|
||||
from os.path import expanduser
|
||||
from Crypto.Cipher import PKCS1_v1_5
|
||||
from Crypto.PublicKey import RSA
|
||||
|
||||
try:
|
||||
import boto.ec2
|
||||
HAS_BOTO = True
|
||||
except ImportError:
|
||||
HAS_BOTO = False
|
||||
|
||||
def main():
|
||||
argument_spec = ec2_argument_spec()
|
||||
argument_spec.update(dict(
|
||||
instance_id = dict(required=True),
|
||||
key_file = dict(required=True),
|
||||
)
|
||||
)
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
|
||||
if not HAS_BOTO:
|
||||
module.fail_json(msg='Boto required for this module.')
|
||||
|
||||
instance_id = module.params.get('instance_id')
|
||||
key_file = expanduser(module.params.get('key_file'))
|
||||
|
||||
ec2 = ec2_connect(module)
|
||||
|
||||
data = ec2.get_password_data(instance_id)
|
||||
decoded = b64decode(data)
|
||||
|
||||
f = open(key_file, 'r')
|
||||
key = RSA.importKey(f.read())
|
||||
cipher = PKCS1_v1_5.new(key)
|
||||
sentinel = 'password decryption failed!!!'
|
||||
|
||||
try:
|
||||
decrypted = cipher.decrypt(decoded, sentinel)
|
||||
except ValueError as e:
|
||||
decrypted = None
|
||||
|
||||
if decrypted == None:
|
||||
module.exit_json(win_password='', changed=False)
|
||||
else:
|
||||
module.exit_json(win_password=decrypted, changed=True)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
from ansible.module_utils.ec2 import *
|
||||
|
||||
main()
|
@ -0,0 +1,410 @@
|
||||
#!/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/>.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: cs_account
|
||||
short_description: Manages account on Apache CloudStack based clouds.
|
||||
description:
|
||||
- Create, disable, lock, enable and remove accounts.
|
||||
version_added: '2.0'
|
||||
author: '"René Moser (@resmo)" <mail@renemoser.net>'
|
||||
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.
|
||||
required: false
|
||||
default: 'present'
|
||||
choices: [ 'present', 'absent', 'enabled', 'disabled', 'locked' ]
|
||||
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 = '''
|
||||
---
|
||||
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
|
||||
account_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
|
||||
'''
|
||||
|
||||
try:
|
||||
from cs import CloudStack, CloudStackException, read_config
|
||||
has_lib_cs = True
|
||||
except ImportError:
|
||||
has_lib_cs = False
|
||||
|
||||
# import cloudstack common
|
||||
from ansible.module_utils.cloudstack import *
|
||||
|
||||
|
||||
class AnsibleCloudStackAccount(AnsibleCloudStack):
|
||||
|
||||
def __init__(self, module):
|
||||
AnsibleCloudStack.__init__(self, module)
|
||||
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 = {}
|
||||
args['listall'] = True
|
||||
args['domainid'] = self.get_domain('id')
|
||||
accounts = self.cs.listAccounts(**args)
|
||||
if accounts:
|
||||
account_name = self.module.params.get('name')
|
||||
for a in accounts['account']:
|
||||
if account_name in [ a['name'] ]:
|
||||
self.account = a
|
||||
break
|
||||
|
||||
return self.account
|
||||
|
||||
|
||||
def enable_account(self):
|
||||
account = self.get_account()
|
||||
if not account:
|
||||
self.module.fail_json(msg="Failed: account not present")
|
||||
|
||||
if account['state'].lower() != 'enabled':
|
||||
self.result['changed'] = True
|
||||
args = {}
|
||||
args['id'] = account['id']
|
||||
args['account'] = self.module.params.get('name')
|
||||
args['domainid'] = self.get_domain('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:
|
||||
self.module.fail_json(msg="Failed: account not present")
|
||||
|
||||
# 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 = {}
|
||||
args['id'] = account['id']
|
||||
args['account'] = self.module.params.get('name')
|
||||
args['domainid'] = self.get_domain('id')
|
||||
args['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):
|
||||
missing_params = []
|
||||
|
||||
if not self.module.params.get('email'):
|
||||
missing_params.append('email')
|
||||
|
||||
if not self.module.params.get('username'):
|
||||
missing_params.append('username')
|
||||
|
||||
if not self.module.params.get('password'):
|
||||
missing_params.append('password')
|
||||
|
||||
if not self.module.params.get('first_name'):
|
||||
missing_params.append('first_name')
|
||||
|
||||
if not self.module.params.get('last_name'):
|
||||
missing_params.append('last_name')
|
||||
|
||||
if missing_params:
|
||||
self.module.fail_json(msg="missing required arguments: %s" % ','.join(missing_params))
|
||||
|
||||
account = self.get_account()
|
||||
|
||||
if not account:
|
||||
self.result['changed'] = True
|
||||
|
||||
args = {}
|
||||
args['account'] = self.module.params.get('name')
|
||||
args['domainid'] = self.get_domain('id')
|
||||
args['accounttype'] = self.get_account_type()
|
||||
args['networkdomain'] = self.module.params.get('network_domain')
|
||||
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.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 account:
|
||||
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, 'account')
|
||||
return account
|
||||
|
||||
|
||||
def get_result(self, account):
|
||||
if account:
|
||||
if 'name' in account:
|
||||
self.result['name'] = account['name']
|
||||
if 'accounttype' in account:
|
||||
for key,value in self.account_types.items():
|
||||
if value == account['accounttype']:
|
||||
self.result['account_type'] = key
|
||||
break
|
||||
if 'state' in account:
|
||||
self.result['account_state'] = account['state']
|
||||
if 'domain' in account:
|
||||
self.result['domain'] = account['domain']
|
||||
if 'networkdomain' in account:
|
||||
self.result['network_domain'] = account['networkdomain']
|
||||
return self.result
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
name = dict(required=True),
|
||||
state = dict(choices=['present', 'absent', 'enabled', 'disabled', 'locked' ], 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),
|
||||
timezone = dict(default=None),
|
||||
poll_async = dict(choices=BOOLEANS, default=True),
|
||||
api_key = dict(default=None),
|
||||
api_secret = dict(default=None, no_log=True),
|
||||
api_url = dict(default=None),
|
||||
api_http_method = dict(default='get'),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
if not has_lib_cs:
|
||||
module.fail_json(msg="python library cs required: pip install cs")
|
||||
|
||||
try:
|
||||
acs_acc = AnsibleCloudStackAccount(module)
|
||||
|
||||
state = module.params.get('state')
|
||||
|
||||
if state in ['absent']:
|
||||
account = acs_acc.absent_account()
|
||||
|
||||
elif state in ['enabled']:
|
||||
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, e:
|
||||
module.fail_json(msg='CloudStackException: %s' % str(e))
|
||||
|
||||
except Exception, e:
|
||||
module.fail_json(msg='Exception: %s' % str(e))
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -0,0 +1,436 @@
|
||||
#!/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/>.
|
||||
|
||||
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)" <mail@renemoser.net>'
|
||||
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
|
||||
open_firewall: true
|
||||
|
||||
|
||||
# remove ssh port forwarding
|
||||
- local_action:
|
||||
module: cs_portforward
|
||||
ip_address: 1.2.3.4
|
||||
public_port: 22
|
||||
private_port: 22
|
||||
state: absent
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
---
|
||||
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: Private start port.
|
||||
returned: success
|
||||
type: int
|
||||
sample: 80
|
||||
private_end_port:
|
||||
description: Private end port.
|
||||
returned: success
|
||||
type: int
|
||||
public_port:
|
||||
description: Public start port.
|
||||
returned: success
|
||||
type: int
|
||||
sample: 80
|
||||
public_end_port:
|
||||
description: Public end port.
|
||||
returned: success
|
||||
type: int
|
||||
sample: 80
|
||||
tags:
|
||||
description: Tag srelated 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
|
||||
'''
|
||||
|
||||
|
||||
try:
|
||||
from cs import CloudStack, CloudStackException, read_config
|
||||
has_lib_cs = True
|
||||
except ImportError:
|
||||
has_lib_cs = False
|
||||
|
||||
# import cloudstack common
|
||||
from ansible.module_utils.cloudstack import *
|
||||
|
||||
|
||||
class AnsibleCloudStackPortforwarding(AnsibleCloudStack):
|
||||
|
||||
def __init__(self, module):
|
||||
AnsibleCloudStack.__init__(self, module)
|
||||
self.portforwarding_rule = None
|
||||
self.vm_default_nic = None
|
||||
|
||||
|
||||
def get_public_end_port(self):
|
||||
if not self.module.params.get('public_end_port'):
|
||||
return self.module.params.get('public_port')
|
||||
return self.module.params.get('public_end_port')
|
||||
|
||||
|
||||
def get_private_end_port(self):
|
||||
if not self.module.params.get('private_end_port'):
|
||||
return self.module.params.get('private_port')
|
||||
return self.module.params.get('private_end_port')
|
||||
|
||||
|
||||
def get_vm_guest_ip(self):
|
||||
vm_guest_ip = self.module.params.get('vm_guest_ip')
|
||||
default_nic = self.get_vm_default_nic()
|
||||
|
||||
if not vm_guest_ip:
|
||||
return default_nic['ipaddress']
|
||||
|
||||
for secondary_ip in default_nic['secondaryip']:
|
||||
if vm_guest_ip == secondary_ip['ipaddress']:
|
||||
return vm_guest_ip
|
||||
self.module.fail_json(msg="Secondary IP '%s' not assigned to VM" % vm_guest_ip)
|
||||
|
||||
|
||||
def get_vm_default_nic(self):
|
||||
if self.vm_default_nic:
|
||||
return self.vm_default_nic
|
||||
|
||||
nics = self.cs.listNics(virtualmachineid=self.get_vm(key='id'))
|
||||
if nics:
|
||||
for n in nics['nic']:
|
||||
if n['isdefault']:
|
||||
self.vm_default_nic = n
|
||||
return self.vm_default_nic
|
||||
self.module.fail_json(msg="No default IP address of VM '%s' found" % self.module.params.get('vm'))
|
||||
|
||||
|
||||
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_public_end_port()
|
||||
private_port = self.module.params.get('private_port')
|
||||
private_end_port = self.get_public_end_port()
|
||||
|
||||
args = {}
|
||||
args['ipaddressid'] = self.get_ip_address(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_public_end_port()
|
||||
args['privateport'] = self.module.params.get('private_port')
|
||||
args['privateendport'] = self.get_private_end_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')
|
||||
|
||||
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_public_end_port()
|
||||
args['privateport'] = self.module.params.get('private_port')
|
||||
args['privateendport'] = self.get_private_end_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')
|
||||
|
||||
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):
|
||||
if portforwarding_rule:
|
||||
if 'id' in portforwarding_rule:
|
||||
self.result['id'] = portforwarding_rule['id']
|
||||
if 'virtualmachinedisplayname' in portforwarding_rule:
|
||||
self.result['vm_display_name'] = portforwarding_rule['virtualmachinedisplayname']
|
||||
if 'virtualmachinename' in portforwarding_rule:
|
||||
self.result['vm_name'] = portforwarding_rule['virtualmachinename']
|
||||
if 'ipaddress' in portforwarding_rule:
|
||||
self.result['ip_address'] = portforwarding_rule['ipaddress']
|
||||
if 'vmguestip' in portforwarding_rule:
|
||||
self.result['vm_guest_ip'] = portforwarding_rule['vmguestip']
|
||||
if 'publicport' in portforwarding_rule:
|
||||
self.result['public_port'] = int(portforwarding_rule['publicport'])
|
||||
if 'publicendport' in portforwarding_rule:
|
||||
self.result['public_end_port'] = int(portforwarding_rule['publicendport'])
|
||||
if 'privateport' in portforwarding_rule:
|
||||
self.result['private_port'] = int(portforwarding_rule['privateport'])
|
||||
if 'privateendport' in portforwarding_rule:
|
||||
self.result['private_end_port'] = int(portforwarding_rule['privateendport'])
|
||||
if 'protocol' in portforwarding_rule:
|
||||
self.result['protocol'] = portforwarding_rule['protocol']
|
||||
if 'tags' in portforwarding_rule:
|
||||
self.result['tags'] = []
|
||||
for tag in portforwarding_rule['tags']:
|
||||
result_tag = {}
|
||||
result_tag['key'] = tag['key']
|
||||
result_tag['value'] = tag['value']
|
||||
self.result['tags'].append(result_tag)
|
||||
return self.result
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = 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(choices=BOOLEANS, 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(choices=BOOLEANS, default=True),
|
||||
api_key = dict(default=None),
|
||||
api_secret = dict(default=None, no_log=True),
|
||||
api_url = dict(default=None),
|
||||
api_http_method = dict(default='get'),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
if not has_lib_cs:
|
||||
module.fail_json(msg="python library cs required: pip install cs")
|
||||
|
||||
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, e:
|
||||
module.fail_json(msg='CloudStackException: %s' % str(e))
|
||||
|
||||
except Exception, e:
|
||||
module.fail_json(msg='Exception: %s' % str(e))
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -0,0 +1,432 @@
|
||||
#!/usr/bin/python
|
||||
# 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/>.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: proxmox
|
||||
short_description: management of instances in Proxmox VE cluster
|
||||
description:
|
||||
- allows you to create/delete/stop instances in Proxmox VE cluster
|
||||
version_added: "2.0"
|
||||
options:
|
||||
api_host:
|
||||
description:
|
||||
- the host of the Proxmox VE cluster
|
||||
required: true
|
||||
api_user:
|
||||
description:
|
||||
- the user to authenticate with
|
||||
required: true
|
||||
api_password:
|
||||
description:
|
||||
- the password to authenticate with
|
||||
- you can use PROXMOX_PASSWORD environment variable
|
||||
default: null
|
||||
required: false
|
||||
vmid:
|
||||
description:
|
||||
- the instance id
|
||||
default: null
|
||||
required: true
|
||||
https_verify_ssl:
|
||||
description:
|
||||
- enable / disable https certificate verification
|
||||
default: false
|
||||
required: false
|
||||
type: boolean
|
||||
node:
|
||||
description:
|
||||
- Proxmox VE node, when new VM will be created
|
||||
- required only for C(state=present)
|
||||
- for another states will be autodiscovered
|
||||
default: null
|
||||
required: false
|
||||
password:
|
||||
description:
|
||||
- the instance root password
|
||||
- required only for C(state=present)
|
||||
default: null
|
||||
required: false
|
||||
hostname:
|
||||
description:
|
||||
- the instance hostname
|
||||
- required only for C(state=present)
|
||||
default: null
|
||||
required: false
|
||||
ostemplate:
|
||||
description:
|
||||
- the template for VM creating
|
||||
- required only for C(state=present)
|
||||
default: null
|
||||
required: false
|
||||
disk:
|
||||
description:
|
||||
- hard disk size in GB for instance
|
||||
default: 3
|
||||
required: false
|
||||
cpus:
|
||||
description:
|
||||
- numbers of allocated cpus for instance
|
||||
default: 1
|
||||
required: false
|
||||
memory:
|
||||
description:
|
||||
- memory size in MB for instance
|
||||
default: 512
|
||||
required: false
|
||||
swap:
|
||||
description:
|
||||
- swap memory size in MB for instance
|
||||
default: 0
|
||||
required: false
|
||||
netif:
|
||||
description:
|
||||
- specifies network interfaces for the container
|
||||
default: null
|
||||
required: false
|
||||
type: string
|
||||
ip_address:
|
||||
description:
|
||||
- specifies the address the container will be assigned
|
||||
default: null
|
||||
required: false
|
||||
type: string
|
||||
onboot:
|
||||
description:
|
||||
- specifies whether a VM will be started during system bootup
|
||||
default: false
|
||||
required: false
|
||||
type: boolean
|
||||
storage:
|
||||
description:
|
||||
- target storage
|
||||
default: 'local'
|
||||
required: false
|
||||
type: string
|
||||
cpuunits:
|
||||
description:
|
||||
- CPU weight for a VM
|
||||
default: 1000
|
||||
required: false
|
||||
type: integer
|
||||
nameserver:
|
||||
description:
|
||||
- sets DNS server IP address for a container
|
||||
default: null
|
||||
required: false
|
||||
type: string
|
||||
searchdomain:
|
||||
description:
|
||||
- sets DNS search domain for a container
|
||||
default: null
|
||||
required: false
|
||||
type: string
|
||||
timeout:
|
||||
description:
|
||||
- timeout for operations
|
||||
default: 30
|
||||
required: false
|
||||
type: integer
|
||||
force:
|
||||
description:
|
||||
- forcing operations
|
||||
- can be used only with states C(present), C(stopped), C(restarted)
|
||||
- with C(state=present) force option allow to overwrite existing container
|
||||
- with states C(stopped) , C(restarted) allow to force stop instance
|
||||
default: false
|
||||
required: false
|
||||
type: boolean
|
||||
state:
|
||||
description:
|
||||
- Indicate desired state of the instance
|
||||
choices: ['present', 'started', 'absent', 'stopped', 'restarted']
|
||||
default: present
|
||||
notes:
|
||||
- Requires proxmoxer and requests modules on host. This modules can be installed with pip.
|
||||
requirements: [ "proxmoxer", "requests" ]
|
||||
author: "Sergei Antipov @UnderGreen"
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Create new container with minimal options
|
||||
- proxmox: vmid=100 node='uk-mc02' api_user='root@pam' api_password='1q2w3e' api_host='node1' password='123456' hostname='example.org' ostemplate='local:vztmpl/ubuntu-14.04-x86_64.tar.gz'
|
||||
|
||||
# Create new container with minimal options with force(it will rewrite existing container)
|
||||
- proxmox: vmid=100 node='uk-mc02' api_user='root@pam' api_password='1q2w3e' api_host='node1' password='123456' hostname='example.org' ostemplate='local:vztmpl/ubuntu-14.04-x86_64.tar.gz' force=yes
|
||||
|
||||
# Create new container with minimal options use environment PROXMOX_PASSWORD variable(you should export it before)
|
||||
- proxmox: vmid=100 node='uk-mc02' api_user='root@pam' api_host='node1' password='123456' hostname='example.org' ostemplate='local:vztmpl/ubuntu-14.04-x86_64.tar.gz'
|
||||
|
||||
# Start container
|
||||
- proxmox: vmid=100 api_user='root@pam' api_password='1q2w3e' api_host='node1' state=started
|
||||
|
||||
# Stop container
|
||||
- proxmox: vmid=100 api_user='root@pam' api_password='1q2w3e' api_host='node1' state=stopped
|
||||
|
||||
# Stop container with force
|
||||
- proxmox: vmid=100 api_user='root@pam' api_password='1q2w3e' api_host='node1' force=yes state=stopped
|
||||
|
||||
# Restart container(stopped or mounted container you can't restart)
|
||||
- proxmox: vmid=100 api_user='root@pam' api_password='1q2w3e' api_host='node1' state=stopped
|
||||
|
||||
# Remove container
|
||||
- proxmox: vmid=100 api_user='root@pam' api_password='1q2w3e' api_host='node1' state=absent
|
||||
'''
|
||||
|
||||
import os
|
||||
import time
|
||||
|
||||
try:
|
||||
from proxmoxer import ProxmoxAPI
|
||||
HAS_PROXMOXER = True
|
||||
except ImportError:
|
||||
HAS_PROXMOXER = False
|
||||
|
||||
def get_instance(proxmox, vmid):
|
||||
return [ vm for vm in proxmox.cluster.resources.get(type='vm') if vm['vmid'] == int(vmid) ]
|
||||
|
||||
def content_check(proxmox, node, ostemplate, storage):
|
||||
return [ True for cnt in proxmox.nodes(node).storage(storage).content.get() if cnt['volid'] == ostemplate ]
|
||||
|
||||
def node_check(proxmox, node):
|
||||
return [ True for nd in proxmox.nodes.get() if nd['node'] == node ]
|
||||
|
||||
def create_instance(module, proxmox, vmid, node, disk, storage, cpus, memory, swap, timeout, **kwargs):
|
||||
proxmox_node = proxmox.nodes(node)
|
||||
taskid = proxmox_node.openvz.create(vmid=vmid, storage=storage, memory=memory, swap=swap,
|
||||
cpus=cpus, disk=disk, **kwargs)
|
||||
|
||||
while timeout:
|
||||
if ( proxmox_node.tasks(taskid).status.get()['status'] == 'stopped'
|
||||
and proxmox_node.tasks(taskid).status.get()['exitstatus'] == 'OK' ):
|
||||
return True
|
||||
timeout = timeout - 1
|
||||
if timeout == 0:
|
||||
module.fail_json(msg='Reached timeout while waiting for creating VM. Last line in task before timeout: %s'
|
||||
% proxmox_node.tasks(taskid).log.get()[:1])
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
def start_instance(module, proxmox, vm, vmid, timeout):
|
||||
taskid = proxmox.nodes(vm[0]['node']).openvz(vmid).status.start.post()
|
||||
while timeout:
|
||||
if ( proxmox.nodes(vm[0]['node']).tasks(taskid).status.get()['status'] == 'stopped'
|
||||
and proxmox.nodes(vm[0]['node']).tasks(taskid).status.get()['exitstatus'] == 'OK' ):
|
||||
return True
|
||||
timeout = timeout - 1
|
||||
if timeout == 0:
|
||||
module.fail_json(msg='Reached timeout while waiting for starting VM. Last line in task before timeout: %s'
|
||||
% proxmox.nodes(vm[0]['node']).tasks(taskid).log.get()[:1])
|
||||
|
||||
time.sleep(1)
|
||||
return False
|
||||
|
||||
def stop_instance(module, proxmox, vm, vmid, timeout, force):
|
||||
if force:
|
||||
taskid = proxmox.nodes(vm[0]['node']).openvz(vmid).status.shutdown.post(forceStop=1)
|
||||
else:
|
||||
taskid = proxmox.nodes(vm[0]['node']).openvz(vmid).status.shutdown.post()
|
||||
while timeout:
|
||||
if ( proxmox.nodes(vm[0]['node']).tasks(taskid).status.get()['status'] == 'stopped'
|
||||
and proxmox.nodes(vm[0]['node']).tasks(taskid).status.get()['exitstatus'] == 'OK' ):
|
||||
return True
|
||||
timeout = timeout - 1
|
||||
if timeout == 0:
|
||||
module.fail_json(msg='Reached timeout while waiting for stopping VM. Last line in task before timeout: %s'
|
||||
% proxmox_node.tasks(taskid).log.get()[:1])
|
||||
|
||||
time.sleep(1)
|
||||
return False
|
||||
|
||||
def umount_instance(module, proxmox, vm, vmid, timeout):
|
||||
taskid = proxmox.nodes(vm[0]['node']).openvz(vmid).status.umount.post()
|
||||
while timeout:
|
||||
if ( proxmox.nodes(vm[0]['node']).tasks(taskid).status.get()['status'] == 'stopped'
|
||||
and proxmox.nodes(vm[0]['node']).tasks(taskid).status.get()['exitstatus'] == 'OK' ):
|
||||
return True
|
||||
timeout = timeout - 1
|
||||
if timeout == 0:
|
||||
module.fail_json(msg='Reached timeout while waiting for unmounting VM. Last line in task before timeout: %s'
|
||||
% proxmox_node.tasks(taskid).log.get()[:1])
|
||||
|
||||
time.sleep(1)
|
||||
return False
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
api_host = dict(required=True),
|
||||
api_user = dict(required=True),
|
||||
api_password = dict(no_log=True),
|
||||
vmid = dict(required=True),
|
||||
https_verify_ssl = dict(type='bool', choices=BOOLEANS, default='no'),
|
||||
node = dict(),
|
||||
password = dict(no_log=True),
|
||||
hostname = dict(),
|
||||
ostemplate = dict(),
|
||||
disk = dict(type='int', default=3),
|
||||
cpus = dict(type='int', default=1),
|
||||
memory = dict(type='int', default=512),
|
||||
swap = dict(type='int', default=0),
|
||||
netif = dict(),
|
||||
ip_address = dict(),
|
||||
onboot = dict(type='bool', choices=BOOLEANS, default='no'),
|
||||
storage = dict(default='local'),
|
||||
cpuunits = dict(type='int', default=1000),
|
||||
nameserver = dict(),
|
||||
searchdomain = dict(),
|
||||
timeout = dict(type='int', default=30),
|
||||
force = dict(type='bool', choices=BOOLEANS, default='no'),
|
||||
state = dict(default='present', choices=['present', 'absent', 'stopped', 'started', 'restarted']),
|
||||
)
|
||||
)
|
||||
|
||||
if not HAS_PROXMOXER:
|
||||
module.fail_json(msg='proxmoxer required for this module')
|
||||
|
||||
state = module.params['state']
|
||||
api_user = module.params['api_user']
|
||||
api_host = module.params['api_host']
|
||||
api_password = module.params['api_password']
|
||||
vmid = module.params['vmid']
|
||||
https_verify_ssl = module.params['https_verify_ssl']
|
||||
node = module.params['node']
|
||||
disk = module.params['disk']
|
||||
cpus = module.params['cpus']
|
||||
memory = module.params['memory']
|
||||
swap = module.params['swap']
|
||||
storage = module.params['storage']
|
||||
timeout = module.params['timeout']
|
||||
|
||||
# If password not set get it from PROXMOX_PASSWORD env
|
||||
if not api_password:
|
||||
try:
|
||||
api_password = os.environ['PROXMOX_PASSWORD']
|
||||
except KeyError, e:
|
||||
module.fail_json(msg='You should set api_password param or use PROXMOX_PASSWORD environment variable')
|
||||
|
||||
try:
|
||||
proxmox = ProxmoxAPI(api_host, user=api_user, password=api_password, verify_ssl=https_verify_ssl)
|
||||
except Exception, e:
|
||||
module.fail_json(msg='authorization on proxmox cluster failed with exception: %s' % e)
|
||||
|
||||
if state == 'present':
|
||||
try:
|
||||
if get_instance(proxmox, vmid) and not module.params['force']:
|
||||
module.exit_json(changed=False, msg="VM with vmid = %s is already exists" % vmid)
|
||||
elif not (node, module.params['hostname'] and module.params['password'] and module.params['ostemplate']):
|
||||
module.fail_json(msg='node, hostname, password and ostemplate are mandatory for creating vm')
|
||||
elif not node_check(proxmox, node):
|
||||
module.fail_json(msg="node '%s' not exists in cluster" % node)
|
||||
elif not content_check(proxmox, node, module.params['ostemplate'], storage):
|
||||
module.fail_json(msg="ostemplate '%s' not exists on node %s and storage %s"
|
||||
% (module.params['ostemplate'], node, storage))
|
||||
|
||||
create_instance(module, proxmox, vmid, node, disk, storage, cpus, memory, swap, timeout,
|
||||
password = module.params['password'],
|
||||
hostname = module.params['hostname'],
|
||||
ostemplate = module.params['ostemplate'],
|
||||
netif = module.params['netif'],
|
||||
ip_address = module.params['ip_address'],
|
||||
onboot = int(module.params['onboot']),
|
||||
cpuunits = module.params['cpuunits'],
|
||||
nameserver = module.params['nameserver'],
|
||||
searchdomain = module.params['searchdomain'],
|
||||
force = int(module.params['force']))
|
||||
|
||||
module.exit_json(changed=True, msg="deployed VM %s from template %s" % (vmid, module.params['ostemplate']))
|
||||
except Exception, e:
|
||||
module.fail_json(msg="creation of VM %s failed with exception: %s" % ( vmid, e ))
|
||||
|
||||
elif state == 'started':
|
||||
try:
|
||||
vm = get_instance(proxmox, vmid)
|
||||
if not vm:
|
||||
module.fail_json(msg='VM with vmid = %s not exists in cluster' % vmid)
|
||||
if proxmox.nodes(vm[0]['node']).openvz(vmid).status.current.get()['status'] == 'running':
|
||||
module.exit_json(changed=False, msg="VM %s is already running" % vmid)
|
||||
|
||||
if start_instance(module, proxmox, vm, vmid, timeout):
|
||||
module.exit_json(changed=True, msg="VM %s started" % vmid)
|
||||
except Exception, e:
|
||||
module.fail_json(msg="starting of VM %s failed with exception: %s" % ( vmid, e ))
|
||||
|
||||
elif state == 'stopped':
|
||||
try:
|
||||
vm = get_instance(proxmox, vmid)
|
||||
if not vm:
|
||||
module.fail_json(msg='VM with vmid = %s not exists in cluster' % vmid)
|
||||
|
||||
if proxmox.nodes(vm[0]['node']).openvz(vmid).status.current.get()['status'] == 'mounted':
|
||||
if module.params['force']:
|
||||
if umount_instance(module, proxmox, vm, vmid, timeout):
|
||||
module.exit_json(changed=True, msg="VM %s is shutting down" % vmid)
|
||||
else:
|
||||
module.exit_json(changed=False, msg=("VM %s is already shutdown, but mounted. "
|
||||
"You can use force option to umount it.") % vmid)
|
||||
|
||||
if proxmox.nodes(vm[0]['node']).openvz(vmid).status.current.get()['status'] == 'stopped':
|
||||
module.exit_json(changed=False, msg="VM %s is already shutdown" % vmid)
|
||||
|
||||
if stop_instance(module, proxmox, vm, vmid, timeout, force = module.params['force']):
|
||||
module.exit_json(changed=True, msg="VM %s is shutting down" % vmid)
|
||||
except Exception, e:
|
||||
module.fail_json(msg="stopping of VM %s failed with exception: %s" % ( vmid, e ))
|
||||
|
||||
elif state == 'restarted':
|
||||
try:
|
||||
vm = get_instance(proxmox, vmid)
|
||||
if not vm:
|
||||
module.fail_json(msg='VM with vmid = %s not exists in cluster' % vmid)
|
||||
if ( proxmox.nodes(vm[0]['node']).openvz(vmid).status.current.get()['status'] == 'stopped'
|
||||
or proxmox.nodes(vm[0]['node']).openvz(vmid).status.current.get()['status'] == 'mounted' ):
|
||||
module.exit_json(changed=False, msg="VM %s is not running" % vmid)
|
||||
|
||||
if ( stop_instance(module, proxmox, vm, vmid, timeout, force = module.params['force']) and
|
||||
start_instance(module, proxmox, vm, vmid, timeout) ):
|
||||
module.exit_json(changed=True, msg="VM %s is restarted" % vmid)
|
||||
except Exception, e:
|
||||
module.fail_json(msg="restarting of VM %s failed with exception: %s" % ( vmid, e ))
|
||||
|
||||
elif state == 'absent':
|
||||
try:
|
||||
vm = get_instance(proxmox, vmid)
|
||||
if not vm:
|
||||
module.exit_json(changed=False, msg="VM %s does not exist" % vmid)
|
||||
|
||||
if proxmox.nodes(vm[0]['node']).openvz(vmid).status.current.get()['status'] == 'running':
|
||||
module.exit_json(changed=False, msg="VM %s is running. Stop it before deletion." % vmid)
|
||||
|
||||
if proxmox.nodes(vm[0]['node']).openvz(vmid).status.current.get()['status'] == 'mounted':
|
||||
module.exit_json(changed=False, msg="VM %s is mounted. Stop it with force option before deletion." % vmid)
|
||||
|
||||
taskid = proxmox.nodes(vm[0]['node']).openvz.delete(vmid)
|
||||
while timeout:
|
||||
if ( proxmox.nodes(vm[0]['node']).tasks(taskid).status.get()['status'] == 'stopped'
|
||||
and proxmox.nodes(vm[0]['node']).tasks(taskid).status.get()['exitstatus'] == 'OK' ):
|
||||
module.exit_json(changed=True, msg="VM %s removed" % vmid)
|
||||
timeout = timeout - 1
|
||||
if timeout == 0:
|
||||
module.fail_json(msg='Reached timeout while waiting for removing VM. Last line in task before timeout: %s'
|
||||
% proxmox_node.tasks(taskid).log.get()[:1])
|
||||
|
||||
time.sleep(1)
|
||||
except Exception, e:
|
||||
module.fail_json(msg="deletion of VM %s failed with exception: %s" % ( vmid, e ))
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -0,0 +1,214 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2015, Manuel Sousa <manuel.sousa@gmail.com>
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: rabbitmq_binding
|
||||
author: '"Manuel Sousa (@manuel-sousa)" <manuel.sousa@gmail.com>'
|
||||
version_added: "2.0"
|
||||
|
||||
short_description: This module manages rabbitMQ bindings
|
||||
description:
|
||||
- This module uses rabbitMQ Rest API to create/delete bindings
|
||||
requirements: [ python requests ]
|
||||
options:
|
||||
state:
|
||||
description:
|
||||
- Whether the exchange should be present or absent
|
||||
- Only present implemented atm
|
||||
choices: [ "present", "absent" ]
|
||||
required: false
|
||||
default: present
|
||||
name:
|
||||
description:
|
||||
- source exchange to create binding on
|
||||
required: true
|
||||
aliases: [ "src", "source" ]
|
||||
login_user:
|
||||
description:
|
||||
- rabbitMQ user for connection
|
||||
required: false
|
||||
default: guest
|
||||
login_password:
|
||||
description:
|
||||
- rabbitMQ password for connection
|
||||
required: false
|
||||
default: false
|
||||
login_host:
|
||||
description:
|
||||
- rabbitMQ host for connection
|
||||
required: false
|
||||
default: localhost
|
||||
login_port:
|
||||
description:
|
||||
- rabbitMQ management api port
|
||||
required: false
|
||||
default: 15672
|
||||
vhost:
|
||||
description:
|
||||
- rabbitMQ virtual host
|
||||
- default vhost is /
|
||||
required: false
|
||||
default: "/"
|
||||
destination:
|
||||
description:
|
||||
- destination exchange or queue for the binding
|
||||
required: true
|
||||
aliases: [ "dst", "dest" ]
|
||||
destination_type:
|
||||
description:
|
||||
- Either queue or exchange
|
||||
required: true
|
||||
choices: [ "queue", "exchange" ]
|
||||
aliases: [ "type", "dest_type" ]
|
||||
routing_key:
|
||||
description:
|
||||
- routing key for the binding
|
||||
- default is #
|
||||
required: false
|
||||
default: "#"
|
||||
arguments:
|
||||
description:
|
||||
- extra arguments for exchange. If defined this argument is a key/value dictionary
|
||||
required: false
|
||||
default: {}
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Bind myQueue to directExchange with routing key info
|
||||
- rabbitmq_binding: name=directExchange destination=myQueue type=queue routing_key=info
|
||||
|
||||
# Bind directExchange to topicExchange with routing key *.info
|
||||
- rabbitmq_binding: name=topicExchange destination=topicExchange type=exchange routing_key="*.info"
|
||||
'''
|
||||
|
||||
import requests
|
||||
import urllib
|
||||
import json
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
state = dict(default='present', choices=['present', 'absent'], type='str'),
|
||||
name = dict(required=True, aliases=[ "src", "source" ], type='str'),
|
||||
login_user = dict(default='guest', type='str'),
|
||||
login_password = dict(default='guest', type='str', no_log=True),
|
||||
login_host = dict(default='localhost', type='str'),
|
||||
login_port = dict(default='15672', type='str'),
|
||||
vhost = dict(default='/', type='str'),
|
||||
destination = dict(required=True, aliases=[ "dst", "dest"], type='str'),
|
||||
destination_type = dict(required=True, aliases=[ "type", "dest_type"], choices=[ "queue", "exchange" ],type='str'),
|
||||
routing_key = dict(default='#', type='str'),
|
||||
arguments = dict(default=dict(), type='dict')
|
||||
),
|
||||
supports_check_mode = True
|
||||
)
|
||||
|
||||
if module.params['destination_type'] == "queue":
|
||||
dest_type="q"
|
||||
else:
|
||||
dest_type="e"
|
||||
|
||||
url = "http://%s:%s/api/bindings/%s/e/%s/%s/%s/%s" % (
|
||||
module.params['login_host'],
|
||||
module.params['login_port'],
|
||||
urllib.quote(module.params['vhost'],''),
|
||||
module.params['name'],
|
||||
dest_type,
|
||||
module.params['destination'],
|
||||
urllib.quote(module.params['routing_key'],'')
|
||||
)
|
||||
|
||||
# Check if exchange already exists
|
||||
r = requests.get( url, auth=(module.params['login_user'],module.params['login_password']))
|
||||
|
||||
if r.status_code==200:
|
||||
binding_exists = True
|
||||
response = r.json()
|
||||
elif r.status_code==404:
|
||||
binding_exists = False
|
||||
response = r.text
|
||||
else:
|
||||
module.fail_json(
|
||||
msg = "Invalid response from RESTAPI when trying to check if exchange exists",
|
||||
details = r.text
|
||||
)
|
||||
|
||||
if module.params['state']=='present':
|
||||
change_required = not binding_exists
|
||||
else:
|
||||
change_required = binding_exists
|
||||
|
||||
# Exit if check_mode
|
||||
if module.check_mode:
|
||||
module.exit_json(
|
||||
changed= change_required,
|
||||
name = module.params['name'],
|
||||
details = response,
|
||||
arguments = module.params['arguments']
|
||||
)
|
||||
|
||||
# Do changes
|
||||
if change_required:
|
||||
if module.params['state'] == 'present':
|
||||
url = "http://%s:%s/api/bindings/%s/e/%s/%s/%s" % (
|
||||
module.params['login_host'],
|
||||
module.params['login_port'],
|
||||
urllib.quote(module.params['vhost'],''),
|
||||
module.params['name'],
|
||||
dest_type,
|
||||
module.params['destination']
|
||||
)
|
||||
|
||||
r = requests.post(
|
||||
url,
|
||||
auth = (module.params['login_user'],module.params['login_password']),
|
||||
headers = { "content-type": "application/json"},
|
||||
data = json.dumps({
|
||||
"routing_key": module.params['routing_key'],
|
||||
"arguments": module.params['arguments']
|
||||
})
|
||||
)
|
||||
elif module.params['state'] == 'absent':
|
||||
r = requests.delete( url, auth = (module.params['login_user'],module.params['login_password']))
|
||||
|
||||
if r.status_code == 204 or r.status_code == 201:
|
||||
module.exit_json(
|
||||
changed = True,
|
||||
name = module.params['name'],
|
||||
destination = module.params['destination']
|
||||
)
|
||||
else:
|
||||
module.fail_json(
|
||||
msg = "Error creating exchange",
|
||||
status = r.status_code,
|
||||
details = r.text
|
||||
)
|
||||
|
||||
else:
|
||||
module.exit_json(
|
||||
changed = False,
|
||||
name = module.params['name']
|
||||
)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -0,0 +1,218 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2015, Manuel Sousa <manuel.sousa@gmail.com>
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: rabbitmq_exchange
|
||||
author: '"Manuel Sousa (@manuel-sousa)" <manuel.sousa@gmail.com>'
|
||||
version_added: "2.0"
|
||||
|
||||
short_description: This module manages rabbitMQ exchanges
|
||||
description:
|
||||
- This module uses rabbitMQ Rest API to create/delete exchanges
|
||||
requirements: [ python requests ]
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the exchange to create
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- Whether the exchange should be present or absent
|
||||
- Only present implemented atm
|
||||
choices: [ "present", "absent" ]
|
||||
required: false
|
||||
default: present
|
||||
login_user:
|
||||
description:
|
||||
- rabbitMQ user for connection
|
||||
required: false
|
||||
default: guest
|
||||
login_password:
|
||||
description:
|
||||
- rabbitMQ password for connection
|
||||
required: false
|
||||
default: false
|
||||
login_host:
|
||||
description:
|
||||
- rabbitMQ host for connection
|
||||
required: false
|
||||
default: localhost
|
||||
login_port:
|
||||
description:
|
||||
- rabbitMQ management api port
|
||||
required: false
|
||||
default: 15672
|
||||
vhost:
|
||||
description:
|
||||
- rabbitMQ virtual host
|
||||
required: false
|
||||
default: "/"
|
||||
durable:
|
||||
description:
|
||||
- whether exchange is durable or not
|
||||
required: false
|
||||
choices: [ "yes", "no" ]
|
||||
default: yes
|
||||
exchange_type:
|
||||
description:
|
||||
- type for the exchange
|
||||
required: false
|
||||
choices: [ "fanout", "direct", "headers", "topic" ]
|
||||
aliases: [ "type" ]
|
||||
default: direct
|
||||
auto_delete:
|
||||
description:
|
||||
- if the exchange should delete itself after all queues/exchanges unbound from it
|
||||
required: false
|
||||
choices: [ "yes", "no" ]
|
||||
default: no
|
||||
internal:
|
||||
description:
|
||||
- exchange is available only for other exchanges
|
||||
required: false
|
||||
choices: [ "yes", "no" ]
|
||||
default: no
|
||||
arguments:
|
||||
description:
|
||||
- extra arguments for exchange. If defined this argument is a key/value dictionary
|
||||
required: false
|
||||
default: {}
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Create direct exchange
|
||||
- rabbitmq_exchange: name=directExchange
|
||||
|
||||
# Create topic exchange on vhost
|
||||
- rabbitmq_exchange: name=topicExchange type=topic vhost=myVhost
|
||||
'''
|
||||
|
||||
import requests
|
||||
import urllib
|
||||
import json
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
state = dict(default='present', choices=['present', 'absent'], type='str'),
|
||||
name = dict(required=True, type='str'),
|
||||
login_user = dict(default='guest', type='str'),
|
||||
login_password = dict(default='guest', type='str', no_log=True),
|
||||
login_host = dict(default='localhost', type='str'),
|
||||
login_port = dict(default='15672', type='str'),
|
||||
vhost = dict(default='/', type='str'),
|
||||
durable = dict(default=True, choices=BOOLEANS, type='bool'),
|
||||
auto_delete = dict(default=False, choices=BOOLEANS, type='bool'),
|
||||
internal = dict(default=False, choices=BOOLEANS, type='bool'),
|
||||
exchange_type = dict(default='direct', aliases=['type'], type='str'),
|
||||
arguments = dict(default=dict(), type='dict')
|
||||
),
|
||||
supports_check_mode = True
|
||||
)
|
||||
|
||||
url = "http://%s:%s/api/exchanges/%s/%s" % (
|
||||
module.params['login_host'],
|
||||
module.params['login_port'],
|
||||
urllib.quote(module.params['vhost'],''),
|
||||
module.params['name']
|
||||
)
|
||||
|
||||
# Check if exchange already exists
|
||||
r = requests.get( url, auth=(module.params['login_user'],module.params['login_password']))
|
||||
|
||||
if r.status_code==200:
|
||||
exchange_exists = True
|
||||
response = r.json()
|
||||
elif r.status_code==404:
|
||||
exchange_exists = False
|
||||
response = r.text
|
||||
else:
|
||||
module.fail_json(
|
||||
msg = "Invalid response from RESTAPI when trying to check if exchange exists",
|
||||
details = r.text
|
||||
)
|
||||
|
||||
if module.params['state']=='present':
|
||||
change_required = not exchange_exists
|
||||
else:
|
||||
change_required = exchange_exists
|
||||
|
||||
# Check if attributes change on existing exchange
|
||||
if not change_required and r.status_code==200 and module.params['state'] == 'present':
|
||||
if not (
|
||||
response['durable'] == module.params['durable'] and
|
||||
response['auto_delete'] == module.params['auto_delete'] and
|
||||
response['internal'] == module.params['internal'] and
|
||||
response['type'] == module.params['exchange_type']
|
||||
):
|
||||
module.fail_json(
|
||||
msg = "RabbitMQ RESTAPI doesn't support attribute changes for existing exchanges"
|
||||
)
|
||||
|
||||
# Exit if check_mode
|
||||
if module.check_mode:
|
||||
module.exit_json(
|
||||
changed= change_required,
|
||||
name = module.params['name'],
|
||||
details = response,
|
||||
arguments = module.params['arguments']
|
||||
)
|
||||
|
||||
# Do changes
|
||||
if change_required:
|
||||
if module.params['state'] == 'present':
|
||||
r = requests.put(
|
||||
url,
|
||||
auth = (module.params['login_user'],module.params['login_password']),
|
||||
headers = { "content-type": "application/json"},
|
||||
data = json.dumps({
|
||||
"durable": module.params['durable'],
|
||||
"auto_delete": module.params['auto_delete'],
|
||||
"internal": module.params['internal'],
|
||||
"type": module.params['exchange_type'],
|
||||
"arguments": module.params['arguments']
|
||||
})
|
||||
)
|
||||
elif module.params['state'] == 'absent':
|
||||
r = requests.delete( url, auth = (module.params['login_user'],module.params['login_password']))
|
||||
|
||||
if r.status_code == 204:
|
||||
module.exit_json(
|
||||
changed = True,
|
||||
name = module.params['name']
|
||||
)
|
||||
else:
|
||||
module.fail_json(
|
||||
msg = "Error creating exchange",
|
||||
status = r.status_code,
|
||||
details = r.text
|
||||
)
|
||||
|
||||
else:
|
||||
module.exit_json(
|
||||
changed = False,
|
||||
name = module.params['name']
|
||||
)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -0,0 +1,263 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# (c) 2015, Manuel Sousa <manuel.sousa@gmail.com>
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: rabbitmq_queue
|
||||
author: '"Manuel Sousa (@manuel-sousa)" <manuel.sousa@gmail.com>'
|
||||
version_added: "2.0"
|
||||
|
||||
short_description: This module manages rabbitMQ queues
|
||||
description:
|
||||
- This module uses rabbitMQ Rest API to create/delete queues
|
||||
requirements: [ python requests ]
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the queue to create
|
||||
required: true
|
||||
state:
|
||||
description:
|
||||
- Whether the queue should be present or absent
|
||||
- Only present implemented atm
|
||||
choices: [ "present", "absent" ]
|
||||
required: false
|
||||
default: present
|
||||
login_user:
|
||||
description:
|
||||
- rabbitMQ user for connection
|
||||
required: false
|
||||
default: guest
|
||||
login_password:
|
||||
description:
|
||||
- rabbitMQ password for connection
|
||||
required: false
|
||||
default: false
|
||||
login_host:
|
||||
description:
|
||||
- rabbitMQ host for connection
|
||||
required: false
|
||||
default: localhost
|
||||
login_port:
|
||||
description:
|
||||
- rabbitMQ management api port
|
||||
required: false
|
||||
default: 15672
|
||||
vhost:
|
||||
description:
|
||||
- rabbitMQ virtual host
|
||||
required: false
|
||||
default: "/"
|
||||
durable:
|
||||
description:
|
||||
- whether queue is durable or not
|
||||
required: false
|
||||
choices: [ "yes", "no" ]
|
||||
default: yes
|
||||
auto_delete:
|
||||
description:
|
||||
- if the queue should delete itself after all queues/queues unbound from it
|
||||
required: false
|
||||
choices: [ "yes", "no" ]
|
||||
default: no
|
||||
message_ttl:
|
||||
description:
|
||||
- How long a message can live in queue before it is discarded (milliseconds)
|
||||
required: False
|
||||
default: forever
|
||||
auto_expires:
|
||||
description:
|
||||
- How long a queue can be unused before it is automatically deleted (milliseconds)
|
||||
required: false
|
||||
default: forever
|
||||
max_length:
|
||||
description:
|
||||
- How many messages can the queue contain before it starts rejecting
|
||||
required: false
|
||||
default: no limit
|
||||
dead_letter_exchange:
|
||||
description:
|
||||
- Optional name of an exchange to which messages will be republished if they
|
||||
- are rejected or expire
|
||||
required: false
|
||||
default: None
|
||||
dead_letter_routing_key:
|
||||
description:
|
||||
- Optional replacement routing key to use when a message is dead-lettered.
|
||||
- Original routing key will be used if unset
|
||||
required: false
|
||||
default: None
|
||||
arguments:
|
||||
description:
|
||||
- extra arguments for queue. If defined this argument is a key/value dictionary
|
||||
required: false
|
||||
default: {}
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Create a queue
|
||||
- rabbitmq_queue: name=myQueue
|
||||
|
||||
# Create a queue on remote host
|
||||
- rabbitmq_queue: name=myRemoteQueue login_user=user login_password=secret login_host=remote.example.org
|
||||
'''
|
||||
|
||||
import requests
|
||||
import urllib
|
||||
import json
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
state = dict(default='present', choices=['present', 'absent'], type='str'),
|
||||
name = dict(required=True, type='str'),
|
||||
login_user = dict(default='guest', type='str'),
|
||||
login_password = dict(default='guest', type='str', no_log=True),
|
||||
login_host = dict(default='localhost', type='str'),
|
||||
login_port = dict(default='15672', type='str'),
|
||||
vhost = dict(default='/', type='str'),
|
||||
durable = dict(default=True, choices=BOOLEANS, type='bool'),
|
||||
auto_delete = dict(default=False, choices=BOOLEANS, type='bool'),
|
||||
message_ttl = dict(default=None, type='int'),
|
||||
auto_expires = dict(default=None, type='int'),
|
||||
max_length = dict(default=None, type='int'),
|
||||
dead_letter_exchange = dict(default=None, type='str'),
|
||||
dead_letter_routing_key = dict(default=None, type='str'),
|
||||
arguments = dict(default=dict(), type='dict')
|
||||
),
|
||||
supports_check_mode = True
|
||||
)
|
||||
|
||||
url = "http://%s:%s/api/queues/%s/%s" % (
|
||||
module.params['login_host'],
|
||||
module.params['login_port'],
|
||||
urllib.quote(module.params['vhost'],''),
|
||||
module.params['name']
|
||||
)
|
||||
|
||||
# Check if queue already exists
|
||||
r = requests.get( url, auth=(module.params['login_user'],module.params['login_password']))
|
||||
|
||||
if r.status_code==200:
|
||||
queue_exists = True
|
||||
response = r.json()
|
||||
elif r.status_code==404:
|
||||
queue_exists = False
|
||||
response = r.text
|
||||
else:
|
||||
module.fail_json(
|
||||
msg = "Invalid response from RESTAPI when trying to check if queue exists",
|
||||
details = r.text
|
||||
)
|
||||
|
||||
if module.params['state']=='present':
|
||||
change_required = not queue_exists
|
||||
else:
|
||||
change_required = queue_exists
|
||||
|
||||
# Check if attributes change on existing queue
|
||||
if not change_required and r.status_code==200 and module.params['state'] == 'present':
|
||||
if not (
|
||||
response['durable'] == module.params['durable'] and
|
||||
response['auto_delete'] == module.params['auto_delete'] and
|
||||
(
|
||||
( 'x-message-ttl' in response['arguments'] and response['arguments']['x-message-ttl'] == module.params['message_ttl'] ) or
|
||||
( 'x-message-ttl' not in response['arguments'] and module.params['message_ttl'] is None )
|
||||
) and
|
||||
(
|
||||
( 'x-expires' in response['arguments'] and response['arguments']['x-expires'] == module.params['auto_expires'] ) or
|
||||
( 'x-expires' not in response['arguments'] and module.params['auto_expires'] is None )
|
||||
) and
|
||||
(
|
||||
( 'x-max-length' in response['arguments'] and response['arguments']['x-max-length'] == module.params['max_length'] ) or
|
||||
( 'x-max-length' not in response['arguments'] and module.params['max_length'] is None )
|
||||
) and
|
||||
(
|
||||
( 'x-dead-letter-exchange' in response['arguments'] and response['arguments']['x-dead-letter-exchange'] == module.params['dead_letter_exchange'] ) or
|
||||
( 'x-dead-letter-exchange' not in response['arguments'] and module.params['dead_letter_exchange'] is None )
|
||||
) and
|
||||
(
|
||||
( 'x-dead-letter-routing-key' in response['arguments'] and response['arguments']['x-dead-letter-routing-key'] == module.params['dead_letter_routing_key'] ) or
|
||||
( 'x-dead-letter-routing-key' not in response['arguments'] and module.params['dead_letter_routing_key'] is None )
|
||||
)
|
||||
):
|
||||
module.fail_json(
|
||||
msg = "RabbitMQ RESTAPI doesn't support attribute changes for existing queues",
|
||||
)
|
||||
|
||||
|
||||
# Copy parameters to arguments as used by RabbitMQ
|
||||
for k,v in {
|
||||
'message_ttl': 'x-message-ttl',
|
||||
'auto_expires': 'x-expires',
|
||||
'max_length': 'x-max-length',
|
||||
'dead_letter_exchange': 'x-dead-letter-exchange',
|
||||
'dead_letter_routing_key': 'x-dead-letter-routing-key'
|
||||
}.items():
|
||||
if module.params[k]:
|
||||
module.params['arguments'][v] = module.params[k]
|
||||
|
||||
# Exit if check_mode
|
||||
if module.check_mode:
|
||||
module.exit_json(
|
||||
changed= change_required,
|
||||
name = module.params['name'],
|
||||
details = response,
|
||||
arguments = module.params['arguments']
|
||||
)
|
||||
|
||||
# Do changes
|
||||
if change_required:
|
||||
if module.params['state'] == 'present':
|
||||
r = requests.put(
|
||||
url,
|
||||
auth = (module.params['login_user'],module.params['login_password']),
|
||||
headers = { "content-type": "application/json"},
|
||||
data = json.dumps({
|
||||
"durable": module.params['durable'],
|
||||
"auto_delete": module.params['auto_delete'],
|
||||
"arguments": module.params['arguments']
|
||||
})
|
||||
)
|
||||
elif module.params['state'] == 'absent':
|
||||
r = requests.delete( url, auth = (module.params['login_user'],module.params['login_password']))
|
||||
|
||||
if r.status_code == 204:
|
||||
module.exit_json(
|
||||
changed = True,
|
||||
name = module.params['name']
|
||||
)
|
||||
else:
|
||||
module.fail_json(
|
||||
msg = "Error creating queue",
|
||||
status = r.status_code,
|
||||
details = r.text
|
||||
)
|
||||
|
||||
else:
|
||||
module.exit_json(
|
||||
changed = False,
|
||||
name = module.params['name']
|
||||
)
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -0,0 +1,132 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# (c) 2014-2015, Epic Games, Inc.
|
||||
|
||||
import requests
|
||||
import time
|
||||
import json
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: circonus_annotation
|
||||
short_description: create an annotation in circonus
|
||||
description:
|
||||
- Create an annotation event with a given category, title and description. Optionally start, end or durations can be provided
|
||||
author: "Nick Harring (@NickatEpic)"
|
||||
version_added: 2.0
|
||||
requirements:
|
||||
- urllib3
|
||||
- requests
|
||||
- time
|
||||
options:
|
||||
api_key:
|
||||
description:
|
||||
- Circonus API key
|
||||
required: true
|
||||
category:
|
||||
description:
|
||||
- Annotation Category
|
||||
required: true
|
||||
description:
|
||||
description:
|
||||
- Description of annotation
|
||||
required: true
|
||||
title:
|
||||
description:
|
||||
- Title of annotation
|
||||
required: true
|
||||
start:
|
||||
description:
|
||||
- Unix timestamp of event start, defaults to now
|
||||
required: false
|
||||
stop:
|
||||
description:
|
||||
- Unix timestamp of event end, defaults to now + duration
|
||||
required: false
|
||||
duration:
|
||||
description:
|
||||
- Duration in seconds of annotation, defaults to 0
|
||||
required: false
|
||||
'''
|
||||
EXAMPLES = '''
|
||||
# Create a simple annotation event with a source, defaults to start and end time of now
|
||||
- circonus_annotation:
|
||||
api_key: XXXXXXXXXXXXXXXXX
|
||||
title: 'App Config Change'
|
||||
description: 'This is a detailed description of the config change'
|
||||
category: 'This category groups like annotations'
|
||||
# Create an annotation with a duration of 5 minutes and a default start time of now
|
||||
- circonus_annotation:
|
||||
api_key: XXXXXXXXXXXXXXXXX
|
||||
title: 'App Config Change'
|
||||
description: 'This is a detailed description of the config change'
|
||||
category: 'This category groups like annotations'
|
||||
duration: 300
|
||||
# Create an annotation with a start_time and end_time
|
||||
- circonus_annotation:
|
||||
api_key: XXXXXXXXXXXXXXXXX
|
||||
title: 'App Config Change'
|
||||
description: 'This is a detailed description of the config change'
|
||||
category: 'This category groups like annotations'
|
||||
start_time: 1395940006
|
||||
end_time: 1395954407
|
||||
'''
|
||||
def post_annotation(annotation, api_key):
|
||||
''' Takes annotation dict and api_key string'''
|
||||
base_url = 'https://api.circonus.com/v2'
|
||||
anootate_post_endpoint = '/annotation'
|
||||
resp = requests.post(base_url + anootate_post_endpoint,
|
||||
headers=build_headers(api_key), data=json.dumps(annotation))
|
||||
resp.raise_for_status()
|
||||
return resp
|
||||
|
||||
def create_annotation(module):
|
||||
''' Takes ansible module object '''
|
||||
annotation = {}
|
||||
if module.params['duration'] != None:
|
||||
duration = module.params['duration']
|
||||
else:
|
||||
duration = 0
|
||||
if module.params['start'] != None:
|
||||
start = module.params['start']
|
||||
else:
|
||||
start = int(time.time())
|
||||
if module.params['stop'] != None:
|
||||
stop = module.params['stop']
|
||||
else:
|
||||
stop = int(time.time())+ duration
|
||||
annotation['start'] = int(start)
|
||||
annotation['stop'] = int(stop)
|
||||
annotation['category'] = module.params['category']
|
||||
annotation['description'] = module.params['description']
|
||||
annotation['title'] = module.params['title']
|
||||
return annotation
|
||||
def build_headers(api_token):
|
||||
'''Takes api token, returns headers with it included.'''
|
||||
headers = {'X-Circonus-App-Name': 'ansible',
|
||||
'Host': 'api.circonus.com', 'X-Circonus-Auth-Token': api_token,
|
||||
'Accept': 'application/json'}
|
||||
return headers
|
||||
|
||||
def main():
|
||||
'''Main function, dispatches logic'''
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
start=dict(required=False, type='int'),
|
||||
stop=dict(required=False, type='int'),
|
||||
category=dict(required=True),
|
||||
title=dict(required=True),
|
||||
description=dict(required=True),
|
||||
duration=dict(required=False, type='int'),
|
||||
api_key=dict(required=True)
|
||||
)
|
||||
)
|
||||
annotation = create_annotation(module)
|
||||
try:
|
||||
resp = post_annotation(annotation, module.params['api_key'])
|
||||
except requests.exceptions.RequestException, err_str:
|
||||
module.fail_json(msg='Request Failed', reason=err_str)
|
||||
module.exit_json(changed=True, annotation=resp.json())
|
||||
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
@ -0,0 +1,182 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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/>.
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
author: "Willy Barro (@willybarro)"
|
||||
requirements: [ pushbullet.py ]
|
||||
module: pushbullet
|
||||
short_description: Sends notifications to Pushbullet
|
||||
description:
|
||||
- This module sends push notifications via Pushbullet to channels or devices.
|
||||
version_added: "2.0"
|
||||
options:
|
||||
api_key:
|
||||
description:
|
||||
- Push bullet API token
|
||||
required: true
|
||||
channel:
|
||||
description:
|
||||
- The channel TAG you wish to broadcast a push notification,
|
||||
as seen on the "My Channels" > "Edit your channel" at
|
||||
Pushbullet page.
|
||||
required: false
|
||||
default: null
|
||||
device:
|
||||
description:
|
||||
- The device NAME you wish to send a push notification,
|
||||
as seen on the Pushbullet main page.
|
||||
required: false
|
||||
default: null
|
||||
push_type:
|
||||
description:
|
||||
- Thing you wish to push.
|
||||
required: false
|
||||
default: note
|
||||
choices: [ "note", "link" ]
|
||||
title:
|
||||
description:
|
||||
- Title of the notification.
|
||||
required: true
|
||||
body:
|
||||
description:
|
||||
- Body of the notification, e.g. Details of the fault you're alerting.
|
||||
required: false
|
||||
|
||||
notes:
|
||||
- Requires pushbullet.py Python package on the remote host.
|
||||
You can install it via pip with ($ pip install pushbullet.py).
|
||||
See U(https://github.com/randomchars/pushbullet.py)
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Sends a push notification to a device
|
||||
- pushbullet:
|
||||
api_key: "ABC123abc123ABC123abc123ABC123ab"
|
||||
device: "Chrome"
|
||||
title: "You may see this on Google Chrome"
|
||||
|
||||
# Sends a link to a device
|
||||
- pushbullet:
|
||||
api_key: "ABC123abc123ABC123abc123ABC123ab"
|
||||
device: "Chrome"
|
||||
push_type: "link"
|
||||
title: "Ansible Documentation"
|
||||
body: "http://docs.ansible.com/"
|
||||
|
||||
# Sends a push notification to a channel
|
||||
- pushbullet:
|
||||
api_key: "ABC123abc123ABC123abc123ABC123ab"
|
||||
channel: "my-awesome-channel"
|
||||
title: "Broadcasting a message to the #my-awesome-channel folks"
|
||||
|
||||
# Sends a push notification with title and body to a channel
|
||||
- pushbullet:
|
||||
api_key: "ABC123abc123ABC123abc123ABC123ab"
|
||||
channel: "my-awesome-channel"
|
||||
title: "ALERT! Signup service is down"
|
||||
body: "Error rate on signup service is over 90% for more than 2 minutes"
|
||||
'''
|
||||
|
||||
try:
|
||||
from pushbullet import PushBullet
|
||||
from pushbullet.errors import InvalidKeyError, PushError
|
||||
except ImportError:
|
||||
pushbullet_found = False
|
||||
else:
|
||||
pushbullet_found = True
|
||||
|
||||
# ===========================================
|
||||
# Main
|
||||
#
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(
|
||||
api_key = dict(type='str', required=True),
|
||||
channel = dict(type='str', default=None),
|
||||
device = dict(type='str', default=None),
|
||||
push_type = dict(type='str', default="note", choices=['note', 'link']),
|
||||
title = dict(type='str', required=True),
|
||||
body = dict(type='str', default=None)
|
||||
),
|
||||
mutually_exclusive = (
|
||||
['channel', 'device'],
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
api_key = module.params['api_key']
|
||||
channel = module.params['channel']
|
||||
device = module.params['device']
|
||||
push_type = module.params['push_type']
|
||||
title = module.params['title']
|
||||
body = module.params['body']
|
||||
|
||||
if not pushbullet_found:
|
||||
module.fail_json(msg="Python 'pushbullet.py' module is required. Install via: $ pip install pushbullet.py")
|
||||
|
||||
# Init pushbullet
|
||||
try:
|
||||
pb = PushBullet(api_key)
|
||||
target = None
|
||||
except InvalidKeyError:
|
||||
module.fail_json(msg="Invalid api_key")
|
||||
|
||||
# Checks for channel/device
|
||||
if device is None and channel is None:
|
||||
module.fail_json(msg="You need to provide a channel or a device.")
|
||||
|
||||
# Search for given device
|
||||
if device is not None:
|
||||
devices_by_nickname = {}
|
||||
for d in pb.devices:
|
||||
devices_by_nickname[d.nickname] = d
|
||||
|
||||
if device in devices_by_nickname:
|
||||
target = devices_by_nickname[device]
|
||||
else:
|
||||
module.fail_json(msg="Device '%s' not found. Available devices: '%s'" % (device, "', '".join(devices_by_nickname.keys())))
|
||||
|
||||
# Search for given channel
|
||||
if channel is not None:
|
||||
channels_by_tag = {}
|
||||
for c in pb.channels:
|
||||
channels_by_tag[c.channel_tag] = c
|
||||
|
||||
if channel in channels_by_tag:
|
||||
target = channels_by_tag[channel]
|
||||
else:
|
||||
module.fail_json(msg="Channel '%s' not found. Available channels: '%s'" % (channel, "', '".join(channels_by_tag.keys())))
|
||||
|
||||
# If in check mode, exit saying that we succeeded
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=False, msg="OK")
|
||||
|
||||
# Send push notification
|
||||
try:
|
||||
target.push_note(title, body)
|
||||
module.exit_json(changed=False, msg="OK")
|
||||
except PushError as e:
|
||||
module.fail_json(msg="An error occurred, Pushbullet's response: %s" % str(e))
|
||||
|
||||
module.fail_json(msg="An unknown error has occurred")
|
||||
|
||||
# import module snippets
|
||||
from ansible.module_utils.basic import *
|
||||
main()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue