Merge branch 'devel' into unevaluated-vars

This commit is contained in:
Lorin Hochstein 2013-05-30 16:27:48 -04:00
commit 8d919d6c97
23 changed files with 664 additions and 51 deletions

View file

@ -26,22 +26,20 @@ Core Features:
Modules added: Modules added:
* rax: module for creating instances in the rackspace cloud (uses pyrax) * cloud: rax: module for creating instances in the rackspace cloud (uses pyrax)
* npm: node.js package management * packages: npm: node.js package management
* postgresql_priv: manages postgresql priveledges * packages: pkgng: next-gen package manager for FreeBSD
* set_fact: sets a variable, which can be the result of a template evaluation * database: postgresql_priv: manages postgresql priveledges
* hipchat: send notification events to hipchat * networking: bigip_pool: load balancing with F5s
* ec2_elb: add and remove machines from ec2 elastic load balancers * networking: ec2_elb: add and remove machines from ec2 elastic load balancers
* flowdock: send messages to flowdock during playbook runs * notification: hipchat: send notification events to hipchat
* pkgng: next-gen package manager for FreeBSD * notification: flowdock: send messages to flowdock during playbook runs
* bigip_pool: load balancing with F5s * notification: campfire: send messages to campfire during playbook runs
* newrelic_deployment: notifies newrelic of new deployments * notification: mqtt: send messages to the Mosquitto message bus
* campfire: send messages to campfire during playbook runs * notification: irc: send messages to IRC channels
* mqtt: send messages to the Mosquitto message bus * notification: filesystem - a wrapper around mkfs
* irc: send messages to IRC channels * notification: jabber: send jabber chat messages
* filesystem - a wrapper around mkfs * notification: osx_say: make OS X say things out loud
* jabber: send jabber chat messages
* osx_say: make OS X say things out loud
* openstack: keystone_user * openstack: keystone_user
* openstack: glance_image * openstack: glance_image
* openstack: nova_compute * openstack: nova_compute
@ -53,7 +51,12 @@ Modules added:
* openstack: quantum_router_gateway * openstack: quantum_router_gateway
* openstack: quantum_router_interface * openstack: quantum_router_interface
* openstack: quantum_subnet * openstack: quantum_subnet
* airbrake_deployment - notify airbrake of new deployments * monitoring: newrelic_deployment: notifies newrelic of new deployments
* monitoring: airbrake_deployment - notify airbrake of new deployments
* monitoring: pingdom
* monitoring: pagerduty
* monitoring: monit
* utility: set_fact: sets a variable, which can be the result of a template evaluation
Modules removed Modules removed
@ -119,7 +122,8 @@ the variable is still registered for the host, with the attribute skipped: True.
* pip works better when sudoing from unpriveledged users * pip works better when sudoing from unpriveledged users
* fix for user creation with groups specification reporting 'changed' incorrectly in some cases * fix for user creation with groups specification reporting 'changed' incorrectly in some cases
* fix for some unicode encoding errors in outputing some data in verbose mode * fix for some unicode encoding errors in outputing some data in verbose mode
* * improved FreeBSD, NetBSD and Solaris facts
* debug module always outputs data without having to specify -v
1.1 "Mean Street" -- 4/2/2013 1.1 "Mean Street" -- 4/2/2013

View file

@ -306,7 +306,8 @@ To see the complete list of variables available for an instance, run the script
Example: OpenStack Inventory Script Example: OpenStack Inventory Script
``````````````````````````````````` ```````````````````````````````````
Though not detailed here in as much depth as the EC2 module, there's also a OpenStack Nova external inventory source in the plugins directory. See the inline comments in the module source for how to use it. Though not detailed here in as much depth as the EC2 module, there's also a OpenStack Compute external inventory source in the plugins directory. It requires the Grizzly release of OpenStack or
later. See the inline comments in the module source for how to use it.
Callback Plugins Callback Plugins
---------------- ----------------

View file

@ -52,6 +52,7 @@ The top level of the directory would contain files and directories like so::
ntp.conf.j2 # <------- templates end in .j2 ntp.conf.j2 # <------- templates end in .j2
files/ # files/ #
bar.txt # <-- files for use with the copy resource bar.txt # <-- files for use with the copy resource
foo.sh # <-- script files for use with the script resource
webtier/ # same kind of structure as "common" was above, done for the webtier role webtier/ # same kind of structure as "common" was above, done for the webtier role
monitoring/ # "" monitoring/ # ""

View file

@ -213,6 +213,17 @@ the remote nodes will be terminated.
Typically you'll be only be backgrounding long-running Typically you'll be only be backgrounding long-running
shell commands or software upgrades only. Backgrounding the copy module does not do a background file transfer. :doc:`playbooks` also support polling, and have a simplified syntax for this. shell commands or software upgrades only. Backgrounding the copy module does not do a background file transfer. :doc:`playbooks` also support polling, and have a simplified syntax for this.
Gathering Facts
```````````````
Facts are described in the playbooks section and represent discovered variables about a
system. These can be used to implement conditional execution of tasks but also just to get ad-hoc information about your system. You can see all facts via::
$ ansible all -m setup
Its also possible to filter this output to just export certain facts, see the "setup" module documentation for details.
Limiting Selected Hosts Limiting Selected Hosts
``````````````````````` ```````````````````````

View file

@ -204,7 +204,19 @@ further information on using Portfiles with MacPorts.
Ubuntu and Debian Ubuntu and Debian
+++++++++++++++++ +++++++++++++++++
Ubuntu builds are available `in a PPA here <https://launchpad.net/~rquillo/+archive/ansible>`_ Ubuntu builds are available `in a PPA here <https://launchpad.net/~rquillo/+archive/ansible>`_.
In Ubuntu 13.04 (raring) its part of the backports repository:
.. code-block:: bash
$ sudo apt-get install ansible/raring-backports
In Debian testing/unstable and Ubntu 13.10+ it is available via
.. code-block:: bash
$ sudo apt-get install ansible
Debian/Ubuntu package recipes can also be built from the source checkout, run: Debian/Ubuntu package recipes can also be built from the source checkout, run:

View file

@ -449,7 +449,7 @@ Roles
.. versionadded: 1.2 .. versionadded: 1.2
Now that you have learned about vars_files, tasks, and handlers, what is the best way to organize your playbooks? Now that you have learned about vars_files, tasks, and handlers, what is the best way to organize your playbooks?
The short answer is to use roles! Roles are automatic ways of automatically loading certain vars_files, tasks, and The short answer is to use roles! Roles are ways of automatically loading certain vars_files, tasks, and
handlers based on a known file structure. Grouping content by roles also allows easy sharing of roles with other users. handlers based on a known file structure. Grouping content by roles also allows easy sharing of roles with other users.
Roles are just automation around 'include' directives as redescribed above, and really don't contain much Roles are just automation around 'include' directives as redescribed above, and really don't contain much
@ -488,6 +488,7 @@ This designates the following behaviors, for each role 'x':
- If roles/x/handlers/main.yml exists, handlers listed therein will be added to the play - If roles/x/handlers/main.yml exists, handlers listed therein will be added to the play
- If roles/x/vars/main.yml exists, variables listed therein will be added to the play - If roles/x/vars/main.yml exists, variables listed therein will be added to the play
- Any copy tasks can reference files in roles/x/files/ without having to path them relatively or absolutely - Any copy tasks can reference files in roles/x/files/ without having to path them relatively or absolutely
- Any script tasks can reference scripts in roles/x/files/ without having to path them relatively or absolutely
- Any template tasks can reference files in roles/x/templates/ without having to path them relatively or absolutely - Any template tasks can reference files in roles/x/templates/ without having to path them relatively or absolutely
If any files are not present, they are just ignored. So it's ok to not have a 'vars/' subdirectory for the role, If any files are not present, they are just ignored. So it's ok to not have a 'vars/' subdirectory for the role,
@ -526,6 +527,8 @@ If you want to define certain tasks to happen before AND after roles are applied
- shell: echo 'hello' - shell: echo 'hello'
roles: roles:
- { role: some_role } - { role: some_role }
tasks:
- shell: echo 'still busy'
post_tasks: post_tasks:
- shell: echo 'goodbye' - shell: echo 'goodbye'

View file

@ -262,8 +262,8 @@ Conditional Execution
````````````````````` `````````````````````
(Note: this section covers 1.2 conditionals, if you are using a previous version, select (Note: this section covers 1.2 conditionals, if you are using a previous version, select
the previous version of the documentation. Those conditional forms continue to be operational the previous version of the documentation, `Ansible 1.1 Docs <http://ansible.cc/docs/released/1.1>`_ .
in 1.2, although the new mechanisms are cleaner.) Those conditional forms continue to be operational in 1.2, although the new mechanisms are cleaner.)
Sometimes you will want to skip a particular step on a particular host. This could be something Sometimes you will want to skip a particular step on a particular host. This could be something
as simple as not installing a certain package if the operating system is a particular version, as simple as not installing a certain package if the operating system is a particular version,
@ -414,7 +414,11 @@ The yum and apt modules use with_items to execute fewer package manager transact
Note that the types of items you iterate over with 'with_items' do not have to be simple lists of strings. Note that the types of items you iterate over with 'with_items' do not have to be simple lists of strings.
If you have a list of hashes, you can reference subkeys using things like:: If you have a list of hashes, you can reference subkeys using things like::
{{ item.subKeyName }} - name: add several users
action: user name={{ item.name }} state=present groups={{ item.groups }}
with_items:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
Lookup Plugins - Accessing Outside Data Lookup Plugins - Accessing Outside Data
``````````````````````````````````````` ```````````````````````````````````````

View file

@ -85,10 +85,10 @@ def boilerplate_module(modfile, args):
if included_boilerplate: if included_boilerplate:
module_data = module_data.replace(module_common.REPLACER, module_common.MODULE_COMMON) module_data = module_data.replace(module_common.REPLACER, module_common.MODULE_COMMON)
encoded_args = "\"\"\"%s\"\"\"" % args.replace("\"","\\\"") encoded_args = repr(str(args))
module_data = module_data.replace(module_common.REPLACER_ARGS, encoded_args) module_data = module_data.replace(module_common.REPLACER_ARGS, encoded_args)
encoded_lang = "\"\"\"%s\"\"\"" % C.DEFAULT_MODULE_LANG encoded_lang = repr(C.DEFAULT_MODULE_LANG)
empty_complex = "\"\"\"%s\"\"\"" % "{}" empty_complex = repr("{}")
module_data = module_data.replace(module_common.REPLACER_LANG, encoded_lang) module_data = module_data.replace(module_common.REPLACER_LANG, encoded_lang)
module_data = module_data.replace('syslog.LOG_USER', "syslog.%s" % C.DEFAULT_SYSLOG_FACILITY) module_data = module_data.replace('syslog.LOG_USER', "syslog.%s" % C.DEFAULT_SYSLOG_FACILITY)
module_data = module_data.replace(module_common.REPLACER_COMPLEX, empty_complex) module_data = module_data.replace(module_common.REPLACER_COMPLEX, empty_complex)

View file

@ -65,7 +65,7 @@ class InventoryParser(object):
for line in self.lines: for line in self.lines:
if line.startswith("["): if line.startswith("["):
active_group_name = line.replace("[","").replace("]","").strip() active_group_name = line.split("#")[0].replace("[","").replace("]","").strip()
if line.find(":vars") != -1 or line.find(":children") != -1: if line.find(":vars") != -1 or line.find(":children") != -1:
active_group_name = active_group_name.rsplit(":", 1)[0] active_group_name = active_group_name.rsplit(":", 1)[0]
if active_group_name not in self.groups: if active_group_name not in self.groups:
@ -78,7 +78,7 @@ class InventoryParser(object):
elif line.startswith("#") or line == '': elif line.startswith("#") or line == '':
pass pass
elif active_group_name: elif active_group_name:
tokens = shlex.split(line) tokens = shlex.split(line.split("#")[0])
if len(tokens) == 0: if len(tokens) == 0:
continue continue
hostname = tokens[0] hostname = tokens[0]

View file

@ -710,9 +710,11 @@ class Runner(object):
module_style = 'non_native_want_json' module_style = 'non_native_want_json'
complex_args_json = utils.jsonify(complex_args) complex_args_json = utils.jsonify(complex_args)
encoded_args = "\"\"\"%s\"\"\"" % module_args.replace("\"","\\\"") # We force conversion of module_args to str because module_common calls shlex.split,
encoded_lang = "\"\"\"%s\"\"\"" % C.DEFAULT_MODULE_LANG # a standard library function that incorrectly handles Unicode input before Python 2.7.3.
encoded_complex = "\"\"\"%s\"\"\"" % complex_args_json.replace("\\", "\\\\") encoded_args = repr(str(module_args))
encoded_lang = repr(C.DEFAULT_MODULE_LANG)
encoded_complex = repr(complex_args_json)
module_data = module_data.replace(module_common.REPLACER, module_common.MODULE_COMMON) module_data = module_data.replace(module_common.REPLACER, module_common.MODULE_COMMON)
module_data = module_data.replace(module_common.REPLACER_ARGS, encoded_args) module_data = module_data.replace(module_common.REPLACER_ARGS, encoded_args)

View file

@ -41,7 +41,10 @@ class ActionModule(object):
# FIXME: error handling # FIXME: error handling
args = " ".join(tokens[1:]) args = " ".join(tokens[1:])
source = template.template(self.runner.basedir, source, inject) source = template.template(self.runner.basedir, source, inject)
source = utils.path_dwim(self.runner.basedir, source) if '_original_file' in inject:
source = utils.path_dwim_relative(inject['_original_file'], 'files', source, self.runner.basedir)
else:
source = utils.path_dwim(self.runner.basedir, source)
# transfer the file to a remote tmp location # transfer the file to a remote tmp location
source = source.replace('\x00','') # why does this happen here? source = source.replace('\x00','') # why does this happen here?

View file

@ -49,6 +49,7 @@ def get_docstring(filename, verbose=False):
if 'EXAMPLES' in (t.id for t in child.targets): if 'EXAMPLES' in (t.id for t in child.targets):
plainexamples = child.value.s[1:] # Skip first empty line plainexamples = child.value.s[1:] # Skip first empty line
except: except:
traceback.print_exc() # temp
if verbose == True: if verbose == True:
traceback.print_exc() traceback.print_exc()
print "unable to parse %s" % filename print "unable to parse %s" % filename

View file

@ -22,7 +22,7 @@ try:
from keystoneclient.v2_0 import client as ksclient from keystoneclient.v2_0 import client as ksclient
import time import time
except ImportError: except ImportError:
print("failed=True msg='glanceclient,novaclient and keystone client are required'") print "failed=True msg='glanceclient,novaclient and keystone client are required'"
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
@ -130,9 +130,7 @@ def _get_server_state(module, nova):
return server_info, server return server_info, server
def _get_port_id(quantum, module, instance_id): def _get_port_id(quantum, module, instance_id):
kwargs = { kwargs = dict(device_id = instance_id)
device_id': instance_id,
}
try: try:
ports = quantum.list_ports(**kwargs) ports = quantum.list_ports(**kwargs)
except Exception as e: except Exception as e:

View file

@ -95,7 +95,7 @@ def main():
module.fail_json(rc=256, msg="no command given") module.fail_json(rc=256, msg="no command given")
if chdir: if chdir:
os.chdir(os.path.expanduser(chdir)) os.chdir(chdir)
if creates: if creates:
# do not run the command if the line contains creates=filename # do not run the command if the line contains creates=filename

View file

@ -126,7 +126,7 @@ EXAMPLES = r"""
lineinfile: dest=/etc/sudoers state=present regexp='^%wheel' line='%wheel ALL=(ALL) NOPASSWD: ALL' lineinfile: dest=/etc/sudoers state=present regexp='^%wheel' line='%wheel ALL=(ALL) NOPASSWD: ALL'
lineinfile: dest=/opt/jboss-as/bin/standalone.conf regexp='^(.*)Xms(\d+)m(.*)$' line='\\1Xms${xms}m\\3' backrefs=yes lineinfile: dest=/opt/jboss-as/bin/standalone.conf regexp='^(.*)Xms(\d+)m(.*)$' line='\1Xms${xms}m\3' backrefs=yes
""" """
def write_changes(module,lines,dest): def write_changes(module,lines,dest):

146
library/monitoring/monit Normal file
View file

@ -0,0 +1,146 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, Darryl Stoflet <stoflet@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: monit
short_description: Manage the state of a program monitored via Monit
description:
- Manage the state of a program monitored via I(Monit)
version_added: "1.2"
options:
name:
description:
- The name of the I(monit) program/process to manage
required: true
default: null
state:
description:
- The state of service
required: true
default: null
choices: [ "present", "started", "stopped", "restarted", "monitored", "unmonitored", "reloaded" ]
examples:
- code: "monit: name=httpd state=started"
description: Manage the state of program I(httpd) to be in I(started) state.
requirements: [ ]
author: Darryl Stoflet
'''
def main():
arg_spec = dict(
name=dict(required=True),
state=dict(required=True, choices=['present', 'started', 'restarted', 'stopped', 'monitored', 'unmonitored', 'reloaded'])
)
module = AnsibleModule(argument_spec=arg_spec, supports_check_mode=True)
name = module.params['name']
state = module.params['state']
MONIT = module.get_bin_path('monit', True)
if state == 'reloaded':
if module.check_mode:
module.exit_json(changed=True)
rc, out, err = module.run_command('%s reload' % MONIT)
module.exit_json(changed=True, name=name, state=state)
rc, out, err = module.run_command('%s summary | grep "Process \'%s\'"' % (MONIT, name))
present = name in out
if not present and not state == 'present':
module.fail_json(msg='%s process not presently configured with monit' % name, name=name, state=state)
if state == 'present':
if not present:
if module.check_mode:
module.exit_json(changed=True)
module.run_command('%s reload' % MONIT, check_rc=True)
rc, out, err = module.run_command('%s summary | grep %s' % (MONIT, name))
if name in out:
module.exit_json(changed=True, name=name, state=state)
else:
module.fail_json(msg=out, name=name, state=state)
module.exit_json(changed=False, name=name, state=state)
rc, out, err = module.run_command('%s summary | grep %s' % (MONIT, name))
running = 'Running' in out
if running and (state == 'started' or state == 'monitored'):
module.exit_json(changed=False, name=name, state=state)
if running and state == 'monitored':
module.exit_json(changed=False, name=name, state=state)
if running and state == 'stopped':
if module.check_mode:
module.exit_json(changed=True)
module.run_command('%s stop %s' % (MONIT, name))
rc, out, err = module.run_command('%s summary | grep %s' % (MONIT, name))
if 'Not monitored' in out or 'stop pending' in out:
module.exit_json(changed=True, name=name, state=state)
module.fail_json(msg=out)
if running and state == 'unmonitored':
if module.check_mode:
module.exit_json(changed=True)
module.run_command('%s unmonitor %s' % (MONIT, name))
rc, out, err = module.run_command('%s summary | grep %s' % (MONIT, name))
if 'Not monitored' in out:
module.exit_json(changed=True, name=name, state=state)
module.fail_json(msg=out)
elif state == 'restarted':
if module.check_mode:
module.exit_json(changed=True)
module.run_command('%s stop %s' % (MONIT, name))
rc, out, err = module.run_command('%s start %s' % (MONIT, name))
if 'Initializing' in out:
module.exit_json(changed=True, name=name, state=state)
module.fail_json(msg=out)
elif not running and state == 'started':
if module.check_mode:
module.exit_json(changed=True)
module.run_command('%s start %s' % (MONIT, name))
rc, out, err = module.run_command('%s summary | grep %s' % (MONIT, name))
if 'Initializing' in out or 'start pending' in out:
module.exit_json(changed=True, name=name, state=state)
module.fail_json(msg=out)
elif not running and state == 'monitored':
if module.check_mode:
module.exit_json(changed=True)
module.run_command('%s monitor %s' % (MONIT, name))
rc, out, err = module.run_command('%s summary | grep %s' % (MONIT, name))
if 'Initializing' in out or 'start pending' in out:
module.exit_json(changed=True, name=name, state=state)
module.fail_json(msg=out)
module.exit_json(changed=False, name=name, state=state)
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

View file

@ -0,0 +1,154 @@
#!/usr/bin/python
DOCUMENTATION = '''
module: pagerduty
short_description: Create PagerDuty maintenance windows
description:
- This module will let you create PagerDuty maintenance windows
version_added: "1.2"
author: Justin Johns
requirements:
- PagerDuty API access
options:
state:
description:
- Create a maintenance window or get a list of ongoing windows.
required: true
default: null
choices: [ "running", "started", "ongoing" ]
aliases: []
name:
description:
- PagerDuty unique subdomain.
required: true
default: null
choices: []
aliases: []
user:
description:
- PagerDuty user ID.
required: true
default: null
choices: []
aliases: []
passwd:
description:
- PagerDuty user password.
required: true
default: null
choices: []
aliases: []
service:
description:
- PagerDuty service ID.
required: false
default: null
choices: []
aliases: []
hours:
description:
- Length of maintenance window in hours.
required: false
default: 1
choices: []
aliases: []
desc:
description:
- Short description of maintenance window.
required: false
default: Created by Ansible
choices: []
aliases: []
notes:
- This module does not yet have support to end maintenance windows.
'''
EXAMPLES='''
# List ongoing maintenance windows.
pagerduty: name=companyabc user=example@example.com passwd=password123 state=ongoing
# Create a 1 hour maintenance window for service FOO123.
pagerduty: name=companyabc user=example@example.com passwd=password123 state=running service=FOO123"
# Create a 4 hour maintenance window for service FOO123 with the description "deployment".
pagerduty: name=companyabc user=example@example.com passwd=password123 state=running service=FOO123 hours=4 desc=deployment"
'''
import json
import datetime
import urllib2
import base64
def ongoing(name, user, passwd):
url = "https://" + name + ".pagerduty.com/api/v1/maintenance_windows/ongoing"
auth = base64.encodestring('%s:%s' % (user, passwd)).replace('\n', '')
req = urllib2.Request(url)
req.add_header("Authorization", "Basic %s" % auth)
res = urllib2.urlopen(req)
out = res.read()
return False, out
def create(name, user, passwd, service, hours, desc):
now = datetime.datetime.utcnow()
later = now + datetime.timedelta(hours=int(hours))
start = now.strftime("%Y-%m-%dT%H:%M:%SZ")
end = later.strftime("%Y-%m-%dT%H:%M:%SZ")
url = "https://" + name + ".pagerduty.com/api/v1/maintenance_windows"
auth = base64.encodestring('%s:%s' % (user, passwd)).replace('\n', '')
data = json.dumps({'maintenance_window': {'start_time': start, 'end_time': end, 'description': desc, 'service_ids': [service]}})
req = urllib2.Request(url, data)
req.add_header("Authorization", "Basic %s" % auth)
req.add_header('Content-Type', 'application/json')
res = urllib2.urlopen(req)
out = res.read()
return False, out
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(required=True, choices=['running', 'started', 'ongoing']),
name=dict(required=True),
user=dict(required=True),
passwd=dict(required=True),
service=dict(required=False),
hours=dict(default='1', required=False),
desc=dict(default='Created by Ansible', required=False)
)
)
state = module.params['state']
name = module.params['name']
user = module.params['user']
passwd = module.params['passwd']
service = module.params['service']
hours = module.params['hours']
desc = module.params['desc']
if state == "running" or state == "started":
if not service:
module.fail_json(msg="service not specified")
(rc, out) = create(name, user, passwd, service, hours, desc)
if state == "ongoing":
(rc, out) = ongoing(name, user, passwd)
if rc != 0:
module.fail_json(msg="failed", result=out)
module.exit_json(msg="success", result=out)
# include magic from lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

119
library/monitoring/pingdom Normal file
View file

@ -0,0 +1,119 @@
#!/usr/bin/python
DOCUMENTATION = '''
module: pingdom
short_description: Pause/unpause Pingdom alerts
description:
- This module will let you pause/unpause Pingdom alerts
version_added: "1.2"
author: Justin Johns
requirements:
- "pingdom python library"
options:
state:
description:
- Define whether or not the check should be running or paused.
required: true
default: null
choices: [ "running", "paused" ]
aliases: []
checkid:
description:
- Pingdom ID of the check.
required: true
default: null
choices: []
aliases: []
uid:
description:
- Pingdom user ID.
required: true
default: null
choices: []
aliases: []
passwd:
description:
- Pingdom user password.
required: true
default: null
choices: []
aliases: []
key:
description:
- Pingdom API key.
required: true
default: null
choices: []
aliases: []
notes:
- This module does not yet have support to add/remove checks.
'''
EXAMPLES = '''
# Pause the check with the ID of 12345.
pingdom: uid=example@example.com passwd=password123 key=apipassword123 checkid=12345 state=paused
# Unpause the check with the ID of 12345.
pingdom: uid=example@example.com passwd=password123 key=apipassword123 checkid=12345 state=running
'''
import pingdom
def pause(checkid, uid, passwd, key):
c = pingdom.PingdomConnection(uid, passwd, key)
c.modify_check(checkid, paused=True)
check = c.get_check(checkid)
name = check.name
result = check.status
#if result != "paused": # api output buggy - accept raw exception for now
# return (True, name, result)
return (False, name, result)
def unpause(checkid, uid, passwd, key):
c = pingdom.PingdomConnection(uid, passwd, key)
c.modify_check(checkid, paused=False)
check = c.get_check(checkid)
name = check.name
result = check.status
#if result != "up": # api output buggy - accept raw exception for now
# return (True, name, result)
return (False, name, result)
def main():
module = AnsibleModule(
argument_spec=dict(
state=dict(required=True, choices=['running', 'paused', 'started', 'stopped']),
checkid=dict(required=True),
uid=dict(required=True),
passwd=dict(required=True),
key=dict(required=True)
)
)
checkid = module.params['checkid']
state = module.params['state']
uid = module.params['uid']
passwd = module.params['passwd']
key = module.params['key']
if (state == "paused" or state == "stopped"):
(rc, name, result) = pause(checkid, uid, passwd, key)
if (state == "running" or state == "started"):
(rc, name, result) = unpause(checkid, uid, passwd, key)
if rc != 0:
module.fail_json(checkid=checkid, name=name, status=result)
module.exit_json(checkid=checkid, name=name, status=result)
# include magic from lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

View file

@ -88,11 +88,12 @@ def main():
if module.check_mode: if module.check_mode:
changed = True changed = True
else: else:
mkfs = module.get_bin_path('mkfs', required=True)
cmd = None cmd = None
if opts is None: if opts is None:
cmd = "mkfs -t %s '%s'"%(fstype, dev) cmd = "%s -t %s '%s'" % (mkfs, fstype, dev)
else: else:
cmd = "mkfs -t %s %s '%s'"%(fstype, opts, dev) cmd = "%s -t %s %s '%s'" % (mkfs, fstype, opts, dev)
rc,_,err = module.run_command(cmd) rc,_,err = module.run_command(cmd)
if rc == 0: if rc == 0:
changed = True changed = True

View file

@ -121,6 +121,7 @@ class Facts(object):
{ 'path' : '/opt/local/bin/pkgin', 'name' : 'pkgin' }, { 'path' : '/opt/local/bin/pkgin', 'name' : 'pkgin' },
{ 'path' : '/opt/local/bin/port', 'name' : 'macports' }, { 'path' : '/opt/local/bin/port', 'name' : 'macports' },
{ 'path' : '/sbin/apk', 'name' : 'apk' }, { 'path' : '/sbin/apk', 'name' : 'apk' },
{ 'path' : '/usr/sbin/pkg', 'name' : 'pkgng' },
] ]
def __init__(self): def __init__(self):
@ -175,7 +176,8 @@ class Facts(object):
SLED = 'Suse', OpenSuSE = 'Suse', SuSE = 'Suse', Gentoo = 'Gentoo', SLED = 'Suse', OpenSuSE = 'Suse', SuSE = 'Suse', Gentoo = 'Gentoo',
Archlinux = 'Archlinux', Mandriva = 'Mandrake', Mandrake = 'Mandrake', Archlinux = 'Archlinux', Mandriva = 'Mandrake', Mandrake = 'Mandrake',
Solaris = 'Solaris', Nexenta = 'Solaris', OmniOS = 'Solaris', OpenIndiana = 'Solaris', Solaris = 'Solaris', Nexenta = 'Solaris', OmniOS = 'Solaris', OpenIndiana = 'Solaris',
SmartOS = 'Solaris', AIX = 'AIX', Alpine = 'Alpine', MacOSX = 'Darwin' SmartOS = 'Solaris', AIX = 'AIX', Alpine = 'Alpine', MacOSX = 'Darwin',
FreeBSD = 'FreeBSD'
) )
if self.facts['system'] == 'AIX': if self.facts['system'] == 'AIX':
@ -189,6 +191,10 @@ class Facts(object):
rc, out, err = module.run_command("/usr/bin/sw_vers -productVersion") rc, out, err = module.run_command("/usr/bin/sw_vers -productVersion")
data = out.split()[-1] data = out.split()[-1]
self.facts['distribution_version'] = data self.facts['distribution_version'] = data
elif self.facts['system'] == 'FreeBSD':
self.facts['distribution'] = 'FreeBSD'
self.facts['distribution_release'] = platform.release()
self.facts['distribution_version'] = platform.version()
else: else:
dist = platform.dist() dist = platform.dist()
self.facts['distribution'] = dist[0].capitalize() or 'NA' self.facts['distribution'] = dist[0].capitalize() or 'NA'
@ -245,12 +251,15 @@ class Facts(object):
def get_public_ssh_host_keys(self): def get_public_ssh_host_keys(self):
dsa_filename = '/etc/ssh/ssh_host_dsa_key.pub' dsa_filename = '/etc/ssh/ssh_host_dsa_key.pub'
rsa_filename = '/etc/ssh/ssh_host_rsa_key.pub' rsa_filename = '/etc/ssh/ssh_host_rsa_key.pub'
ecdsa_filename = '/etc/ssh/ssh_host_ecdsa_key.pub'
if self.facts['system'] == 'Darwin': if self.facts['system'] == 'Darwin':
dsa_filename = '/etc/ssh_host_dsa_key.pub' dsa_filename = '/etc/ssh_host_dsa_key.pub'
rsa_filename = '/etc/ssh_host_rsa_key.pub' rsa_filename = '/etc/ssh_host_rsa_key.pub'
ecdsa_filename = '/etc/ssh_host_ecdsa_key.pub'
dsa = get_file_content(dsa_filename) dsa = get_file_content(dsa_filename)
rsa = get_file_content(rsa_filename) rsa = get_file_content(rsa_filename)
ecdsa = get_file_content(ecdsa_filename)
if dsa is None: if dsa is None:
dsa = 'NA' dsa = 'NA'
else: else:
@ -259,6 +268,10 @@ class Facts(object):
rsa = 'NA' rsa = 'NA'
else: else:
self.facts['ssh_host_key_rsa_public'] = rsa.split()[1] self.facts['ssh_host_key_rsa_public'] = rsa.split()[1]
if ecdsa is None:
ecdsa = 'NA'
else:
self.facts['ssh_host_key_ecdsa_public'] = ecdsa.split()[1]
def get_pkg_mgr_facts(self): def get_pkg_mgr_facts(self):
self.facts['pkg_mgr'] = 'unknown' self.facts['pkg_mgr'] = 'unknown'
@ -1620,7 +1633,7 @@ class LinuxVirtual(Virtual):
return return
# Beware that we can have both kvm and virtualbox running on a single system # Beware that we can have both kvm and virtualbox running on a single system
if os.path.exists("/proc/modules"): if os.path.exists("/proc/modules") and os.access('/proc/modules', os.R_OK):
modules = [] modules = []
for line in open("/proc/modules").readlines(): for line in open("/proc/modules").readlines():
data = line.split(" ", 1) data = line.split(" ", 1)

132
plugins/inventory/vagrant.py Executable file
View file

@ -0,0 +1,132 @@
#!/usr/bin/env python
"""
Vagrant external inventory script. Automatically finds the IP of the booted vagrant vm(s), and
returns it under the host group 'vagrant'
Example Vagrant configuration using this script:
config.vm.provision :ansible do |ansible|
ansible.playbook = "./provision/your_playbook.yml"
ansible.inventory_file = "./provision/inventory/vagrant.py"
ansible.verbose = true
end
"""
# Copyright (C) 2013 Mark Mandel <mark@compoundtheory.com>
#
# This program 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.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#
# Thanks to the spacewalk.py inventory script for giving me the basic structure
# of this.
#
import sys
import subprocess
import re
import string
from optparse import OptionParser
try:
import json
except:
import simplejson as json
# Options
#------------------------------
parser = OptionParser(usage="%prog [options] --list | --host <machine>")
parser.add_option('--list', default=False, dest="list", action="store_true",
help="Produce a JSON consumable grouping of Vagrant servers for Ansible")
parser.add_option('--host', default=None, dest="host",
help="Generate additional host specific details for given host for Ansible")
(options, args) = parser.parse_args()
#
# helper functions
#
# get all the ssh configs for all boxes in an array of dictionaries.
def get_ssh_config():
configs = []
boxes = list_running_boxes()
for box in boxes:
config = get_a_ssh_config(box)
configs.append(config)
return configs
#list all the running boxes
def list_running_boxes():
output = subprocess.check_output(["vagrant", "status"]).split('\n')
boxes = []
for line in output:
matcher = re.search("([^\s]+)[\s]+running \(.+", line)
if matcher:
boxes.append(matcher.group(1))
return boxes
#get the ssh config for a single box
def get_a_ssh_config(box_name):
"""Gives back a map of all the machine's ssh configurations"""
output = subprocess.check_output(["vagrant", "ssh-config", box_name]).split('\n')
config = {}
for line in output:
if line.strip() != '':
matcher = re.search("( )?([a-zA-Z]+) (.*)", line)
config[matcher.group(2)] = matcher.group(3)
return config
# List out servers that vagrant has running
#------------------------------
if options.list:
ssh_config = get_ssh_config()
hosts = { 'vagrant': []}
for data in ssh_config:
hosts['vagrant'].append(data['HostName'])
print json.dumps(hosts)
sys.exit(1)
# Get out the host details
#------------------------------
elif options.host:
result = {}
ssh_config = get_ssh_config()
details = filter(lambda x: (x['HostName'] == options.host), ssh_config)
if len(details) > 0:
#pass through the port, in case it's non standard.
result = details[0]
result['ansible_ssh_port'] = result['Port']
print json.dumps(result)
sys.exit(1)
# Print out help
#------------------------------
else:
parser.print_help()
sys.exit(1)

View file

@ -477,7 +477,7 @@ class TestRunner(unittest.TestCase):
# The order of the test cases is important # The order of the test cases is important
# The regexp doesn't match, so the line will not be added anywhere. # The regexp doesn't match, so the line will not be added anywhere.
testline = r'\\1: Line added by default at the end of the file.' testline = r'\1: Line added by default at the end of the file.'
testcase = ('lineinfile', [ testcase = ('lineinfile', [
"dest=%s" % sample, "dest=%s" % sample,
"regexp='^(First): '", "regexp='^(First): '",
@ -492,7 +492,7 @@ class TestRunner(unittest.TestCase):
# insertafter with EOF # insertafter with EOF
# The regexp doesn't match, so the line will not be added anywhere. # The regexp doesn't match, so the line will not be added anywhere.
testline = r'\\1: Line added with insertafter=EOF' testline = r'\1: Line added with insertafter=EOF'
testcase = ('lineinfile', [ testcase = ('lineinfile', [
"dest=%s" % sample, "dest=%s" % sample,
"insertafter=EOF", "insertafter=EOF",
@ -508,7 +508,7 @@ class TestRunner(unittest.TestCase):
# with invalid insertafter regex # with invalid insertafter regex
# The regexp doesn't match, so do nothing. # The regexp doesn't match, so do nothing.
testline = r'\\1: Line added with an invalid insertafter regex' testline = r'\1: Line added with an invalid insertafter regex'
testcase = ('lineinfile', [ testcase = ('lineinfile', [
"dest=%s" % sample, "dest=%s" % sample,
"insertafter='^abcdefgh'", "insertafter='^abcdefgh'",
@ -522,7 +522,7 @@ class TestRunner(unittest.TestCase):
# with an insertafter regex # with an insertafter regex
# The regexp doesn't match, so do nothing. # The regexp doesn't match, so do nothing.
testline = r'\\1: Line added with a valid insertafter regex' testline = r'\1: Line added with a valid insertafter regex'
testcase = ('lineinfile', [ testcase = ('lineinfile', [
"dest=%s" % sample, "dest=%s" % sample,
"insertafter='^receive messages to '", "insertafter='^receive messages to '",
@ -541,7 +541,7 @@ class TestRunner(unittest.TestCase):
target_line = 'combination of microphone, speaker, keyboard and display. It can send and' target_line = 'combination of microphone, speaker, keyboard and display. It can send and'
idx = artifact.index(target_line) idx = artifact.index(target_line)
testline = r'\\1 of megaphone' testline = r'\1 of megaphone'
testline_after = 'combination of megaphone' testline_after = 'combination of megaphone'
testcase = ('lineinfile', [ testcase = ('lineinfile', [
"dest=%s" % sample, "dest=%s" % sample,
@ -558,7 +558,7 @@ class TestRunner(unittest.TestCase):
assert target_line not in artifact assert target_line not in artifact
# Go again, should be unchanged now. # Go again, should be unchanged now.
testline = r'\\1 of megaphone' testline = r'\1 of megaphone'
testline_after = 'combination of megaphone' testline_after = 'combination of megaphone'
testcase = ('lineinfile', [ testcase = ('lineinfile', [
"dest=%s" % sample, "dest=%s" % sample,
@ -574,11 +574,11 @@ class TestRunner(unittest.TestCase):
f = open(sample, 'a+') f = open(sample, 'a+')
f.write("1 + 1 = 3" + os.linesep) f.write("1 + 1 = 3" + os.linesep)
f.close() f.close()
testline = r"2 + \\g<num> = 3" testline = r"2 + \g<num> = 3"
testline_after = "2 + 1 = 3" testline_after = "2 + 1 = 3"
testcase = ('lineinfile', [ testcase = ('lineinfile', [
"dest=%s" % sample, "dest=%s" % sample,
r"regexp='1 \\+ (?P<num>\\d) = 3'", r"regexp='1 \+ (?P<num>\d) = 3'",
"line='%s'" % testline, "line='%s'" % testline,
"backrefs=yes", "backrefs=yes",
]) ])

View file

@ -0,0 +1,8 @@
[major-god] # group with inline comments
zeus var_a=1 # host with inline comments
# A comment
thor
[minor-god] # group with inline comment and unbalanced quotes: ' "
morpheus # host with inline comments and unbalanced quotes: ' "
# A comment with unbalanced quotes: ' "