Merge branch 'devel' into unevaluated-vars
This commit is contained in:
commit
8d919d6c97
23 changed files with 664 additions and 51 deletions
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -26,22 +26,20 @@ Core Features:
|
|||
|
||||
Modules added:
|
||||
|
||||
* rax: module for creating instances in the rackspace cloud (uses pyrax)
|
||||
* npm: node.js package management
|
||||
* postgresql_priv: manages postgresql priveledges
|
||||
* set_fact: sets a variable, which can be the result of a template evaluation
|
||||
* hipchat: send notification events to hipchat
|
||||
* ec2_elb: add and remove machines from ec2 elastic load balancers
|
||||
* flowdock: send messages to flowdock during playbook runs
|
||||
* pkgng: next-gen package manager for FreeBSD
|
||||
* bigip_pool: load balancing with F5s
|
||||
* newrelic_deployment: notifies newrelic of new deployments
|
||||
* campfire: send messages to campfire during playbook runs
|
||||
* mqtt: send messages to the Mosquitto message bus
|
||||
* irc: send messages to IRC channels
|
||||
* filesystem - a wrapper around mkfs
|
||||
* jabber: send jabber chat messages
|
||||
* osx_say: make OS X say things out loud
|
||||
* cloud: rax: module for creating instances in the rackspace cloud (uses pyrax)
|
||||
* packages: npm: node.js package management
|
||||
* packages: pkgng: next-gen package manager for FreeBSD
|
||||
* database: postgresql_priv: manages postgresql priveledges
|
||||
* networking: bigip_pool: load balancing with F5s
|
||||
* networking: ec2_elb: add and remove machines from ec2 elastic load balancers
|
||||
* notification: hipchat: send notification events to hipchat
|
||||
* notification: flowdock: send messages to flowdock during playbook runs
|
||||
* notification: campfire: send messages to campfire during playbook runs
|
||||
* notification: mqtt: send messages to the Mosquitto message bus
|
||||
* notification: irc: send messages to IRC channels
|
||||
* notification: filesystem - a wrapper around mkfs
|
||||
* notification: jabber: send jabber chat messages
|
||||
* notification: osx_say: make OS X say things out loud
|
||||
* openstack: keystone_user
|
||||
* openstack: glance_image
|
||||
* openstack: nova_compute
|
||||
|
@ -53,7 +51,12 @@ Modules added:
|
|||
* openstack: quantum_router_gateway
|
||||
* openstack: quantum_router_interface
|
||||
* 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
|
||||
|
||||
|
@ -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
|
||||
* 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
|
||||
*
|
||||
* improved FreeBSD, NetBSD and Solaris facts
|
||||
* debug module always outputs data without having to specify -v
|
||||
|
||||
1.1 "Mean Street" -- 4/2/2013
|
||||
|
||||
|
|
|
@ -306,7 +306,8 @@ To see the complete list of variables available for an instance, run the 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
|
||||
----------------
|
||||
|
|
|
@ -52,6 +52,7 @@ The top level of the directory would contain files and directories like so::
|
|||
ntp.conf.j2 # <------- templates end in .j2
|
||||
files/ #
|
||||
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
|
||||
monitoring/ # ""
|
||||
|
|
|
@ -213,6 +213,17 @@ the remote nodes will be terminated.
|
|||
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.
|
||||
|
||||
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
|
||||
```````````````````````
|
||||
|
||||
|
|
|
@ -204,7 +204,19 @@ further information on using Portfiles with MacPorts.
|
|||
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:
|
||||
|
||||
|
|
|
@ -449,7 +449,7 @@ Roles
|
|||
.. versionadded: 1.2
|
||||
|
||||
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.
|
||||
|
||||
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/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 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
|
||||
|
||||
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'
|
||||
roles:
|
||||
- { role: some_role }
|
||||
tasks:
|
||||
- shell: echo 'still busy'
|
||||
post_tasks:
|
||||
- shell: echo 'goodbye'
|
||||
|
||||
|
|
|
@ -262,8 +262,8 @@ Conditional Execution
|
|||
`````````````````````
|
||||
|
||||
(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
|
||||
in 1.2, although the new mechanisms are cleaner.)
|
||||
the previous version of the documentation, `Ansible 1.1 Docs <http://ansible.cc/docs/released/1.1>`_ .
|
||||
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
|
||||
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.
|
||||
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
|
||||
```````````````````````````````````````
|
||||
|
|
|
@ -85,10 +85,10 @@ def boilerplate_module(modfile, args):
|
|||
if included_boilerplate:
|
||||
|
||||
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)
|
||||
encoded_lang = "\"\"\"%s\"\"\"" % C.DEFAULT_MODULE_LANG
|
||||
empty_complex = "\"\"\"%s\"\"\"" % "{}"
|
||||
encoded_lang = repr(C.DEFAULT_MODULE_LANG)
|
||||
empty_complex = repr("{}")
|
||||
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(module_common.REPLACER_COMPLEX, empty_complex)
|
||||
|
|
|
@ -65,7 +65,7 @@ class InventoryParser(object):
|
|||
|
||||
for line in self.lines:
|
||||
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:
|
||||
active_group_name = active_group_name.rsplit(":", 1)[0]
|
||||
if active_group_name not in self.groups:
|
||||
|
@ -78,7 +78,7 @@ class InventoryParser(object):
|
|||
elif line.startswith("#") or line == '':
|
||||
pass
|
||||
elif active_group_name:
|
||||
tokens = shlex.split(line)
|
||||
tokens = shlex.split(line.split("#")[0])
|
||||
if len(tokens) == 0:
|
||||
continue
|
||||
hostname = tokens[0]
|
||||
|
|
|
@ -710,9 +710,11 @@ class Runner(object):
|
|||
module_style = 'non_native_want_json'
|
||||
|
||||
complex_args_json = utils.jsonify(complex_args)
|
||||
encoded_args = "\"\"\"%s\"\"\"" % module_args.replace("\"","\\\"")
|
||||
encoded_lang = "\"\"\"%s\"\"\"" % C.DEFAULT_MODULE_LANG
|
||||
encoded_complex = "\"\"\"%s\"\"\"" % complex_args_json.replace("\\", "\\\\")
|
||||
# We force conversion of module_args to str because module_common calls shlex.split,
|
||||
# a standard library function that incorrectly handles Unicode input before Python 2.7.3.
|
||||
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_ARGS, encoded_args)
|
||||
|
|
|
@ -41,7 +41,10 @@ class ActionModule(object):
|
|||
# FIXME: error handling
|
||||
args = " ".join(tokens[1:])
|
||||
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
|
||||
source = source.replace('\x00','') # why does this happen here?
|
||||
|
|
|
@ -49,6 +49,7 @@ def get_docstring(filename, verbose=False):
|
|||
if 'EXAMPLES' in (t.id for t in child.targets):
|
||||
plainexamples = child.value.s[1:] # Skip first empty line
|
||||
except:
|
||||
traceback.print_exc() # temp
|
||||
if verbose == True:
|
||||
traceback.print_exc()
|
||||
print "unable to parse %s" % filename
|
||||
|
|
|
@ -22,7 +22,7 @@ try:
|
|||
from keystoneclient.v2_0 import client as ksclient
|
||||
import time
|
||||
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 = '''
|
||||
---
|
||||
|
@ -130,9 +130,7 @@ def _get_server_state(module, nova):
|
|||
return server_info, server
|
||||
|
||||
def _get_port_id(quantum, module, instance_id):
|
||||
kwargs = {
|
||||
device_id': instance_id,
|
||||
}
|
||||
kwargs = dict(device_id = instance_id)
|
||||
try:
|
||||
ports = quantum.list_ports(**kwargs)
|
||||
except Exception as e:
|
||||
|
|
|
@ -95,7 +95,7 @@ def main():
|
|||
module.fail_json(rc=256, msg="no command given")
|
||||
|
||||
if chdir:
|
||||
os.chdir(os.path.expanduser(chdir))
|
||||
os.chdir(chdir)
|
||||
|
||||
if creates:
|
||||
# do not run the command if the line contains creates=filename
|
||||
|
|
|
@ -126,7 +126,7 @@ EXAMPLES = r"""
|
|||
|
||||
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):
|
||||
|
|
146
library/monitoring/monit
Normal file
146
library/monitoring/monit
Normal 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()
|
154
library/monitoring/pagerduty
Normal file
154
library/monitoring/pagerduty
Normal 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
119
library/monitoring/pingdom
Normal 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()
|
|
@ -88,11 +88,12 @@ def main():
|
|||
if module.check_mode:
|
||||
changed = True
|
||||
else:
|
||||
mkfs = module.get_bin_path('mkfs', required=True)
|
||||
cmd = None
|
||||
if opts is None:
|
||||
cmd = "mkfs -t %s '%s'"%(fstype, dev)
|
||||
cmd = "%s -t %s '%s'" % (mkfs, fstype, dev)
|
||||
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)
|
||||
if rc == 0:
|
||||
changed = True
|
||||
|
|
|
@ -121,6 +121,7 @@ class Facts(object):
|
|||
{ 'path' : '/opt/local/bin/pkgin', 'name' : 'pkgin' },
|
||||
{ 'path' : '/opt/local/bin/port', 'name' : 'macports' },
|
||||
{ 'path' : '/sbin/apk', 'name' : 'apk' },
|
||||
{ 'path' : '/usr/sbin/pkg', 'name' : 'pkgng' },
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
|
@ -175,7 +176,8 @@ class Facts(object):
|
|||
SLED = 'Suse', OpenSuSE = 'Suse', SuSE = 'Suse', Gentoo = 'Gentoo',
|
||||
Archlinux = 'Archlinux', Mandriva = 'Mandrake', Mandrake = 'Mandrake',
|
||||
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':
|
||||
|
@ -189,6 +191,10 @@ class Facts(object):
|
|||
rc, out, err = module.run_command("/usr/bin/sw_vers -productVersion")
|
||||
data = out.split()[-1]
|
||||
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:
|
||||
dist = platform.dist()
|
||||
self.facts['distribution'] = dist[0].capitalize() or 'NA'
|
||||
|
@ -245,12 +251,15 @@ class Facts(object):
|
|||
def get_public_ssh_host_keys(self):
|
||||
dsa_filename = '/etc/ssh/ssh_host_dsa_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':
|
||||
dsa_filename = '/etc/ssh_host_dsa_key.pub'
|
||||
rsa_filename = '/etc/ssh_host_rsa_key.pub'
|
||||
ecdsa_filename = '/etc/ssh_host_ecdsa_key.pub'
|
||||
dsa = get_file_content(dsa_filename)
|
||||
rsa = get_file_content(rsa_filename)
|
||||
ecdsa = get_file_content(ecdsa_filename)
|
||||
if dsa is None:
|
||||
dsa = 'NA'
|
||||
else:
|
||||
|
@ -259,6 +268,10 @@ class Facts(object):
|
|||
rsa = 'NA'
|
||||
else:
|
||||
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):
|
||||
self.facts['pkg_mgr'] = 'unknown'
|
||||
|
@ -1620,7 +1633,7 @@ class LinuxVirtual(Virtual):
|
|||
return
|
||||
|
||||
# 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 = []
|
||||
for line in open("/proc/modules").readlines():
|
||||
data = line.split(" ", 1)
|
||||
|
|
132
plugins/inventory/vagrant.py
Executable file
132
plugins/inventory/vagrant.py
Executable 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)
|
|
@ -477,7 +477,7 @@ class TestRunner(unittest.TestCase):
|
|||
# The order of the test cases is important
|
||||
|
||||
# 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', [
|
||||
"dest=%s" % sample,
|
||||
"regexp='^(First): '",
|
||||
|
@ -492,7 +492,7 @@ class TestRunner(unittest.TestCase):
|
|||
|
||||
# insertafter with EOF
|
||||
# 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', [
|
||||
"dest=%s" % sample,
|
||||
"insertafter=EOF",
|
||||
|
@ -508,7 +508,7 @@ class TestRunner(unittest.TestCase):
|
|||
|
||||
# with invalid insertafter regex
|
||||
# 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', [
|
||||
"dest=%s" % sample,
|
||||
"insertafter='^abcdefgh'",
|
||||
|
@ -522,7 +522,7 @@ class TestRunner(unittest.TestCase):
|
|||
|
||||
# with an insertafter regex
|
||||
# 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', [
|
||||
"dest=%s" % sample,
|
||||
"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'
|
||||
idx = artifact.index(target_line)
|
||||
|
||||
testline = r'\\1 of megaphone'
|
||||
testline = r'\1 of megaphone'
|
||||
testline_after = 'combination of megaphone'
|
||||
testcase = ('lineinfile', [
|
||||
"dest=%s" % sample,
|
||||
|
@ -558,7 +558,7 @@ class TestRunner(unittest.TestCase):
|
|||
assert target_line not in artifact
|
||||
|
||||
# Go again, should be unchanged now.
|
||||
testline = r'\\1 of megaphone'
|
||||
testline = r'\1 of megaphone'
|
||||
testline_after = 'combination of megaphone'
|
||||
testcase = ('lineinfile', [
|
||||
"dest=%s" % sample,
|
||||
|
@ -574,11 +574,11 @@ class TestRunner(unittest.TestCase):
|
|||
f = open(sample, 'a+')
|
||||
f.write("1 + 1 = 3" + os.linesep)
|
||||
f.close()
|
||||
testline = r"2 + \\g<num> = 3"
|
||||
testline = r"2 + \g<num> = 3"
|
||||
testline_after = "2 + 1 = 3"
|
||||
testcase = ('lineinfile', [
|
||||
"dest=%s" % sample,
|
||||
r"regexp='1 \\+ (?P<num>\\d) = 3'",
|
||||
r"regexp='1 \+ (?P<num>\d) = 3'",
|
||||
"line='%s'" % testline,
|
||||
"backrefs=yes",
|
||||
])
|
||||
|
|
8
test/inventory_dir/3comments
Normal file
8
test/inventory_dir/3comments
Normal 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: ' "
|
Loading…
Reference in a new issue