From 252a51be912e0b0b3b09e563e67383f700ee4153 Mon Sep 17 00:00:00 2001 From: Jan-Piet Mens Date: Thu, 21 Nov 2013 09:39:52 +0100 Subject: [PATCH 001/182] New lookup plugin csvreader --- lib/ansible/runner/lookup_plugins/csvfile.py | 82 ++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 lib/ansible/runner/lookup_plugins/csvfile.py diff --git a/lib/ansible/runner/lookup_plugins/csvfile.py b/lib/ansible/runner/lookup_plugins/csvfile.py new file mode 100644 index 0000000000..7c6f376bf2 --- /dev/null +++ b/lib/ansible/runner/lookup_plugins/csvfile.py @@ -0,0 +1,82 @@ +# (c) 2013, Jan-Piet Mens +# +# 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 . + +from ansible import utils, errors +import os +import codecs +import csv + +class LookupModule(object): + + def __init__(self, basedir=None, **kwargs): + self.basedir = basedir + + def read_csv(self, filename, key, delimiter, dflt=None, col=1): + + try: + f = codecs.open(filename, 'r', encoding='utf-8') + creader = csv.reader(f, delimiter=delimiter) + + for row in creader: + if row[0] == key: + return row[int(col)] + except Exception, e: + raise errors.AnsibleError("csvfile: %s" % str(e)) + + return dflt + + def run(self, terms, inject=None, **kwargs): + + terms = utils.listify_lookup_plugin_terms(terms, self.basedir, inject) + + if isinstance(terms, basestring): + terms = [ terms ] + + ret = [] + for term in terms: + params = term.split() + key = params[0] + + paramvals = { + 'file' : 'ansible.csv', + 'default' : None, + 'delimiter' : "TAB", + 'col' : "1", # column to return + } + + # parameters specified? + try: + for param in params[1:]: + name, value = param.split('=') + assert(name in paramvals) + paramvals[name] = value + except (ValueError, AssertionError) as e: + raise errors.AnsibleError(e) + + if paramvals['delimiter'] == 'TAB': + paramvals['delimiter'] = "\t" + + path = utils.path_dwim(self.basedir, paramvals['file']) + + var = self.read_csv(path, key, paramvals['delimiter'], paramvals['default'], paramvals['col']) + if var is not None: + if type(var) is list: + for v in var: + ret.append(v) + else: + ret.append(var) + return ret From 2f9470ec1d3d03feec67afd5a9f38f1d52d8ea1f Mon Sep 17 00:00:00 2001 From: Serge van Ginderachter Date: Mon, 25 Nov 2013 15:31:56 +0100 Subject: [PATCH 002/182] synchronize: allow ansible_ssh_host to be templated This is already enabled in the runner also. --- lib/ansible/runner/action_plugins/synchronize.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/ansible/runner/action_plugins/synchronize.py b/lib/ansible/runner/action_plugins/synchronize.py index 004deded22..7e27add804 100644 --- a/lib/ansible/runner/action_plugins/synchronize.py +++ b/lib/ansible/runner/action_plugins/synchronize.py @@ -69,6 +69,8 @@ class ActionModule(object): # from the perspective of the rsync call the delegate is the localhost src_host = '127.0.0.1' dest_host = inject.get('ansible_ssh_host', inject['inventory_hostname']) + # allow ansible_ssh_host to be templated + dest_host = template.template(self.runner.basedir, dest_host, inject, fail_on_undefined=True) # edge case: explicit delegate and dest_host are the same if dest_host == inject['delegate_to']: From 2300902784f51d4085514d6e2c2828bf7b7120e2 Mon Sep 17 00:00:00 2001 From: Kamil Kisiel Date: Mon, 2 Dec 2013 23:50:33 -0800 Subject: [PATCH 003/182] Don't modify hgrc when running purge --- library/source_control/hg | 48 +++++---------------------------------- 1 file changed, 6 insertions(+), 42 deletions(-) diff --git a/library/source_control/hg b/library/source_control/hg index e6730ad6f5..fcaa73457a 100644 --- a/library/source_control/hg +++ b/library/source_control/hg @@ -59,9 +59,7 @@ options: choices: [ "yes", "no" ] purge: description: - - Deletes untracked files. Runs C(hg purge). Note this requires C(purge) extension to - be enabled if C(purge=yes). This module will modify hgrc file on behalf of the user - and undo the changes before exiting the task. + - Deletes untracked files. Runs C(hg purge). required: false default: "no" choices: [ "yes", "no" ] @@ -85,36 +83,6 @@ EXAMPLES = ''' - hg: repo=https://bitbucket.org/user/repo1 dest=/home/user/repo1 revision=stable purge=yes ''' -def _set_hgrc(hgrc, vals): - parser = ConfigParser.SafeConfigParser() - parser.read(hgrc) - - # val is a list of triple-tuple of the form [(section, option, value),...] - for each in vals: - (section, option, value) = each - if not parser.has_section(section): - parser.add_section(section) - parser.set(section, option, value) - - f = open(hgrc, 'w') - parser.write(f) - f.close() - - -def _undo_hgrc(hgrc, vals): - parser = ConfigParser.SafeConfigParser() - parser.read(hgrc) - - for each in vals: - (section, option, value) = each - if parser.has_section(section): - parser.remove_option(section, option) - - f = open(hgrc, 'w') - parser.write(f) - f.close() - - class Hg(object): def __init__(self, module, dest, repo, revision, hg_path): @@ -129,7 +97,8 @@ class Hg(object): return (rc, out, err) def _list_untracked(self): - return self._command(['purge', '-R', self.dest, '--print']) + args = ['purge', '--config', 'extensions.purge=', '-R', self.dest, '--print'] + return self._command(args) def get_revision(self): """ @@ -168,10 +137,6 @@ class Hg(object): return True def purge(self): - hgrc = os.path.join(self.dest, '.hg/hgrc') - purge_option = [('extensions', 'purge', '')] - _set_hgrc(hgrc, purge_option) # enable purge extension - # before purge, find out if there are any untracked files (rc1, out1, err1) = self._list_untracked() if rc1 != 0: @@ -179,10 +144,9 @@ class Hg(object): # there are some untrackd files if out1 != '': - (rc2, out2, err2) = self._command(['purge', '-R', self.dest]) - if rc2 == 0: - _undo_hgrc(hgrc, purge_option) - else: + args = ['purge', '--config', 'extensions.purge=', '-R', self.dest] + (rc2, out2, err2) = self._command(args) + if rc2 != 0: self.module.fail_json(msg=err2) return True else: From 3a0d2860e041569bb3ec458be08b9e2eb6a92c0b Mon Sep 17 00:00:00 2001 From: willthames Date: Wed, 4 Dec 2013 11:18:15 +1000 Subject: [PATCH 004/182] Allow multiple users to use ec2 inventory scripts Move the ec2 cache files from a shared /tmp location to the user's ansible tmp directory. --- plugins/inventory/ec2.ini | 2 +- plugins/inventory/ec2.py | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/inventory/ec2.ini b/plugins/inventory/ec2.ini index 01a4982d62..345b185995 100644 --- a/plugins/inventory/ec2.ini +++ b/plugins/inventory/ec2.ini @@ -47,7 +47,7 @@ route53 = False # will be written to this directory: # - ansible-ec2.cache # - ansible-ec2.index -cache_path = /tmp +cache_path = ~/.ansible/tmp # The number of seconds a cache file is considered valid. After this many # seconds, a new API call will be made, and the cache file will be updated. diff --git a/plugins/inventory/ec2.py b/plugins/inventory/ec2.py index b64025647e..f8cb367d8c 100755 --- a/plugins/inventory/ec2.py +++ b/plugins/inventory/ec2.py @@ -220,9 +220,12 @@ class Ec2Inventory(object): config.get('ec2', 'route53_excluded_zones', '').split(',')) # Cache related - cache_path = config.get('ec2', 'cache_path') - self.cache_path_cache = cache_path + "/ansible-ec2.cache" - self.cache_path_index = cache_path + "/ansible-ec2.index" + cache_dir = os.path.expanduser(config.get('ec2', 'cache_path')) + if not os.path.exists(cache_dir): + os.makedirs(cache_dir) + + self.cache_path_cache = cache_dir + "/ansible-ec2.cache" + self.cache_path_index = cache_dir + "/ansible-ec2.index" self.cache_max_age = config.getint('ec2', 'cache_max_age') From 0e11e4c86f580142d6268dcb281c07fc02dd529f Mon Sep 17 00:00:00 2001 From: "Jasper N. Brouwer" Date: Wed, 4 Dec 2013 21:49:00 +0100 Subject: [PATCH 005/182] Fixed false positive on initctl as enable_cmd Also on update-rc.d In service module --- library/system/service | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/system/service b/library/system/service index aac7319d75..2490c6a30f 100644 --- a/library/system/service +++ b/library/system/service @@ -431,10 +431,10 @@ class LinuxService(Service): if check_systemd(self.name): # service is managed by systemd self.enable_cmd = location['systemctl'] - elif os.path.exists("/etc/init/%s.conf" % self.name): + elif os.path.exists("/etc/init/%s.conf" % self.name) and location['initctl']: # service is managed by upstart self.enable_cmd = location['initctl'] - elif os.path.exists("/etc/init.d/%s" % self.name): + elif os.path.exists("/etc/init.d/%s" % self.name) and location['update-rc.d']: # service is managed by with SysV init scripts, but with update-rc.d self.enable_cmd = location['update-rc.d'] else: @@ -649,7 +649,7 @@ class LinuxService(Service): return if self.enable: - # make sure the init.d symlinks are created + # make sure the init.d symlinks are created # otherwise enable might not work (rc, out, err) = self.execute_command("%s %s defaults" \ % (self.enable_cmd, self.name)) From e9a0aff77cfc0073cc99689da9f70854d1b58813 Mon Sep 17 00:00:00 2001 From: Will Thames Date: Tue, 10 Dec 2013 21:32:54 +1000 Subject: [PATCH 006/182] Fail EC2 inventory better with restricted EC2 users If a user has full EC2 access but no RDS access, the user should still be able to query the EC2 inventory, although will not obtain any information about any RDS instances. --- plugins/inventory/ec2.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/inventory/ec2.py b/plugins/inventory/ec2.py index a38545052d..502dc433fc 100755 --- a/plugins/inventory/ec2.py +++ b/plugins/inventory/ec2.py @@ -295,9 +295,10 @@ class Ec2Inventory(object): for instance in instances: self.add_rds_instance(instance, region) except boto.exception.BotoServerError as e: - print "Looks like AWS RDS is down: " - print e - sys.exit(1) + if not e.reason == "Forbidden": + print "Looks like AWS RDS is down: " + print e + sys.exit(1) def get_instance(self, region, instance_id): ''' Gets details about a specific instance ''' From 54a28eb45b01d2f63a0ee207426f6133936737a7 Mon Sep 17 00:00:00 2001 From: drewlll2ll Date: Thu, 12 Dec 2013 09:44:29 -0500 Subject: [PATCH 007/182] Added prev_state for directory --- library/files/file | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/files/file b/library/files/file index 134b5284ef..8e2af964b9 100644 --- a/library/files/file +++ b/library/files/file @@ -307,6 +307,10 @@ def main(): if not force: module.fail_json(dest=path, src=src, msg='Cannot link, file exists at destination') changed = True + elif prev_state == 'directory': + if not force: + module.fail_json(dest=path, src=src, msg='Cannot link, directory exists at destination') + changed = True else: module.fail_json(dest=path, src=src, msg='unexpected position reached') From ed7c9a1fbb7c311806d812f9e2f236109658aab9 Mon Sep 17 00:00:00 2001 From: Mike Grozak Date: Fri, 13 Dec 2013 17:10:50 +0100 Subject: [PATCH 008/182] Added to the file module the functionality to force conversion hard link and symlink when the force attribute is set to 'yes' --- library/files/file | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/files/file b/library/files/file index 134b5284ef..5e308e1126 100644 --- a/library/files/file +++ b/library/files/file @@ -245,7 +245,7 @@ def main(): module.exit_json(path=path, changed=True) if prev_state != 'absent' and prev_state != state: - if not (force and (prev_state == 'file' or prev_state == 'directory') and state == 'link') and state != 'touch': + if not (force and (prev_state == 'file' or prev_state == 'hard' or prev_state == 'directory') and state == 'link') and state != 'touch': module.fail_json(path=path, msg='refusing to convert between %s and %s for %s' % (prev_state, state, src)) if prev_state == 'absent' and state == 'absent': From c8d5846ab93e3aab13ae692b88cc6bed64fb83b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Bocahu?= Date: Mon, 16 Dec 2013 19:55:20 +0100 Subject: [PATCH 009/182] Let merge hash_bahaviour work with dynamic inventory --- lib/ansible/inventory/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ansible/inventory/__init__.py b/lib/ansible/inventory/__init__.py index f202a14192..57b0f318b0 100644 --- a/lib/ansible/inventory/__init__.py +++ b/lib/ansible/inventory/__init__.py @@ -351,7 +351,7 @@ class Inventory(object): vars.update(host.get_variables()) if self.parser is not None: - vars.update(self.parser.get_host_variables(host)) + vars = utils.combine_vars(vars, self.parser.get_host_variables(host)) return vars def add_group(self, group): From 12005a1cd0e234f64ac2d7c865a7cbd77ea4fbc4 Mon Sep 17 00:00:00 2001 From: willthames Date: Tue, 17 Dec 2013 12:04:12 +1000 Subject: [PATCH 010/182] Move more responsibility to common EC2 module Moved `AWS_REGIONS` into `ec2` module Created `ec2_connect` method in `ec2` module Updated modules able to use `ec2_connect` and `AWS_REGIONS` --- lib/ansible/module_utils/ec2.py | 34 +++++++++++++++++++++++++ library/cloud/ec2 | 28 +-------------------- library/cloud/ec2_ami | 28 +-------------------- library/cloud/ec2_eip | 44 +++------------------------------ library/cloud/ec2_elb | 9 ------- library/cloud/ec2_group | 22 ++--------------- library/cloud/ec2_tag | 28 +-------------------- library/cloud/ec2_vol | 28 +-------------------- library/cloud/ec2_vpc | 9 ------- library/cloud/elasticache | 9 ------- library/cloud/rds | 30 ++-------------------- 11 files changed, 45 insertions(+), 224 deletions(-) diff --git a/lib/ansible/module_utils/ec2.py b/lib/ansible/module_utils/ec2.py index 0698c89056..4e8fa11f0f 100644 --- a/lib/ansible/module_utils/ec2.py +++ b/lib/ansible/module_utils/ec2.py @@ -1,3 +1,13 @@ +AWS_REGIONS = ['ap-northeast-1', + 'ap-southeast-1', + 'ap-southeast-2', + 'eu-west-1', + 'sa-east-1', + 'us-east-1', + 'us-west-1', + 'us-west-2'] + + def get_ec2_creds(module): # Check module args for credentials, then check environment vars @@ -36,3 +46,27 @@ def get_ec2_creds(module): region = os.environ['AWS_REGION'] return ec2_url, ec2_access_key, ec2_secret_key, region + + +def ec2_connect(module): + + """ Return an ec2 connection""" + + ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module) + + # If we have a region specified, connect to its endpoint. + if region: + try: + ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key) + except boto.exception.NoAuthHandlerFound, e: + module.fail_json(msg = str(e)) + # Otherwise, no region so we fallback to the old connection method + elif ec2_url: + try: + ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key) + except boto.exception.NoAuthHandlerFound, e: + module.fail_json(msg = str(e)) + else: + module.fail_json(msg="Either region or ec2_url must be specified") + return ec2 + diff --git a/library/cloud/ec2 b/library/cloud/ec2 index 0e0b8aaf0f..a565b0359a 100644 --- a/library/cloud/ec2 +++ b/library/cloud/ec2 @@ -294,15 +294,6 @@ local_action: import sys import time -AWS_REGIONS = ['ap-northeast-1', - 'ap-southeast-1', - 'ap-southeast-2', - 'eu-west-1', - 'sa-east-1', - 'us-east-1', - 'us-west-1', - 'us-west-2'] - try: import boto.ec2 from boto.exception import EC2ResponseError @@ -653,24 +644,7 @@ def main(): ) ) - # def get_ec2_creds(module): - # return ec2_url, ec2_access_key, ec2_secret_key, region - ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module) - - # If we have a region specified, connect to its endpoint. - if region: - try: - ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg = str(e)) - # If we specified an ec2_url then try connecting to it - elif ec2_url: - try: - ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg = str(e)) - else: - module.fail_json(msg="Either region or ec2_url must be specified") + ec2 = ec2_connect(module) if module.params.get('state') == 'absent': instance_ids = module.params.get('instance_ids') diff --git a/library/cloud/ec2_ami b/library/cloud/ec2_ami index ea7e0ad86d..f90f23db99 100644 --- a/library/cloud/ec2_ami +++ b/library/cloud/ec2_ami @@ -156,15 +156,6 @@ EXAMPLES = ''' import sys import time -AWS_REGIONS = ['ap-northeast-1', - 'ap-southeast-1', - 'ap-southeast-2', - 'eu-west-1', - 'sa-east-1', - 'us-east-1', - 'us-west-1', - 'us-west-2'] - try: import boto import boto.ec2 @@ -279,24 +270,7 @@ def main(): ) ) - # def get_ec2_creds(module): - # return ec2_url, ec2_access_key, ec2_secret_key, region - ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module) - - # If we have a region specified, connect to its endpoint. - if region: - try: - ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg = str(e)) - # If we specified an ec2_url then try connecting to it - elif ec2_url: - try: - ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg = str(e)) - else: - module.fail_json(msg="Either region or ec2_url must be specified") + ec2 = ec2_connect(module) if module.params.get('state') == 'absent': if not module.params.get('image_id'): diff --git a/library/cloud/ec2_eip b/library/cloud/ec2_eip index 1c5db8cf4c..4399c6bdf6 100644 --- a/library/cloud/ec2_eip +++ b/library/cloud/ec2_eip @@ -102,38 +102,6 @@ else: boto_found = True -def connect(ec2_url, ec2_access_key, ec2_secret_key, region, module): - - """ Return an ec2 connection""" - # allow environment variables to be used if ansible vars aren't set - if not ec2_url and 'EC2_URL' in os.environ: - ec2_url = os.environ['EC2_URL'] - if not ec2_secret_key and 'EC2_SECRET_KEY' in os.environ: - ec2_secret_key = os.environ['EC2_SECRET_KEY'] - if not ec2_access_key and 'EC2_ACCESS_KEY' in os.environ: - ec2_access_key = os.environ['EC2_ACCESS_KEY'] - - # If we have a region specified, connect to its endpoint. - if region: - try: - ec2 = boto.ec2.connect_to_region(region, - aws_access_key_id=ec2_access_key, - aws_secret_access_key=ec2_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg = str(" %s %s %s " % (region, ec2_access_key, - ec2_secret_key))) - # Otherwise, no region so we fallback to the old connection method - else: - try: - if ec2_url: # if we have an URL set, connect to the specified endpoint - ec2 = boto.connect_ec2_endpoint(ec2_url, ec2_access_key, ec2_secret_key) - else: # otherwise it's Amazon. - ec2 = boto.connect_ec2(ec2_access_key, ec2_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg = str(e)) - return ec2 - - def associate_ip_and_instance(ec2, address, instance_id, module): if ip_is_associated_with_instance(ec2, address.public_ip, instance_id, module): module.exit_json(changed=False, public_ip=address.public_ip) @@ -248,8 +216,8 @@ def main(): state = dict(required=False, default='present', choices=['present', 'absent']), ec2_url = dict(required=False, aliases=['EC2_URL']), - ec2_secret_key = dict(required=False, aliases=['EC2_SECRET_KEY'], no_log=True), - ec2_access_key = dict(required=False, aliases=['EC2_ACCESS_KEY']), + ec2_secret_key = dict(aliases=['aws_secret_key', 'secret_key'], no_log=True), + ec2_access_key = dict(aliases=['aws_access_key', 'access_key']), region = dict(required=False, aliases=['ec2_region']), in_vpc = dict(required=False, choices=BOOLEANS, default=False), ), @@ -259,13 +227,7 @@ def main(): if not boto_found: module.fail_json(msg="boto is required") - ec2_url, ec2_access_key, ec2_secret_key, region = get_ec2_creds(module) - - ec2 = connect(ec2_url, - ec2_access_key, - ec2_secret_key, - region, - module) + ec2 = ec2_connect(module) instance_id = module.params.get('instance_id') public_ip = module.params.get('public_ip') diff --git a/library/cloud/ec2_elb b/library/cloud/ec2_elb index 3132d9e951..4488cf8460 100644 --- a/library/cloud/ec2_elb +++ b/library/cloud/ec2_elb @@ -102,15 +102,6 @@ import time import sys import os -AWS_REGIONS = ['ap-northeast-1', - 'ap-southeast-1', - 'ap-southeast-2', - 'eu-west-1', - 'sa-east-1', - 'us-east-1', - 'us-west-1', - 'us-west-2'] - try: import boto import boto.ec2 diff --git a/library/cloud/ec2_group b/library/cloud/ec2_group index c325c1ce30..bdba3f5b05 100644 --- a/library/cloud/ec2_group +++ b/library/cloud/ec2_group @@ -113,16 +113,12 @@ def main(): ec2_url=dict(aliases=['EC2_URL']), ec2_secret_key=dict(aliases=['EC2_SECRET_KEY', 'aws_secret_key'], no_log=True), ec2_access_key=dict(aliases=['EC2_ACCESS_KEY', 'aws_access_key']), - region=dict(choices=['eu-west-1', 'sa-east-1', 'us-east-1', 'ap-northeast-1', 'us-west-2', 'us-west-1', 'ap-southeast-1', 'ap-southeast-2']), + region=dict(choices=AWS_REGIONS), state = dict(default='present', choices=['present', 'absent']), ), supports_check_mode=True, ) - # def get_ec2_creds(module): - # return ec2_url, ec2_access_key, ec2_secret_key, region - ec2_url, ec2_access_key, ec2_secret_key, region = get_ec2_creds(module) - name = module.params['name'] description = module.params['description'] vpc_id = module.params['vpc_id'] @@ -131,21 +127,7 @@ def main(): changed = False - # If we have a region specified, connect to its endpoint. - if region: - try: - ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=ec2_access_key, aws_secret_access_key=ec2_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg=str(e)) - # Otherwise, no region so we fallback to the old connection method - else: - try: - if ec2_url: # if we have an URL set, connect to the specified endpoint - ec2 = boto.connect_ec2_endpoint(ec2_url, ec2_access_key, ec2_secret_key) - else: # otherwise it's Amazon. - ec2 = boto.connect_ec2(ec2_access_key, ec2_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg=str(e)) + ec2 = ec2_connect(module) # find the group if present group = None diff --git a/library/cloud/ec2_tag b/library/cloud/ec2_tag index 71e6792698..9bcf8a5dab 100644 --- a/library/cloud/ec2_tag +++ b/library/cloud/ec2_tag @@ -101,15 +101,6 @@ except ImportError: print "failed=True msg='boto required for this module'" sys.exit(1) -AWS_REGIONS = ['ap-northeast-1', - 'ap-southeast-1', - 'ap-southeast-2', - 'eu-west-1', - 'sa-east-1', - 'us-east-1', - 'us-west-1', - 'us-west-2'] - def main(): module = AnsibleModule( argument_spec = dict( @@ -123,28 +114,11 @@ def main(): ) ) - # def get_ec2_creds(module): - # return ec2_url, ec2_access_key, ec2_secret_key, region - ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module) - resource = module.params.get('resource') tags = module.params['tags'] state = module.params.get('state') - # If we have a region specified, connect to its endpoint. - if region: - try: - ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg = str(e)) - # Otherwise, no region so we fallback to the old connection method - elif ec2_url: - try: - ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg = str(e)) - else: - module.fail_json(msg="Either region or ec2_url must be specified") + ec2 = ec2_connect(module) # We need a comparison here so that we can accurately report back changed status. # Need to expand the gettags return format and compare with "tags" and then tag or detag as appropriate. diff --git a/library/cloud/ec2_vol b/library/cloud/ec2_vol index e5c7d1eab1..a60e0b71f8 100644 --- a/library/cloud/ec2_vol +++ b/library/cloud/ec2_vol @@ -127,15 +127,6 @@ except ImportError: print "failed=True msg='boto required for this module'" sys.exit(1) -AWS_REGIONS = ['ap-northeast-1', - 'ap-southeast-1', - 'ap-southeast-2', - 'eu-west-1', - 'sa-east-1', - 'us-east-1', - 'us-west-1', - 'us-west-2'] - def main(): module = AnsibleModule( argument_spec = dict( @@ -151,30 +142,13 @@ def main(): ) ) - # def get_ec2_creds(module): - # return ec2_url, ec2_access_key, ec2_secret_key, region - ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module) - instance = module.params.get('instance') volume_size = module.params.get('volume_size') iops = module.params.get('iops') device_name = module.params.get('device_name') zone = module.params.get('zone') - # If we have a region specified, connect to its endpoint. - if region: - try: - ec2 = boto.ec2.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg = str(e)) - # Otherwise, no region so we fallback to the old connection method - elif ec2_url: - try: - ec2 = boto.connect_ec2_endpoint(ec2_url, aws_access_key, aws_secret_key) - except boto.exception.NoAuthHandlerFound, e: - module.fail_json(msg = str(e)) - else: - module.fail_json(msg="Either region or ec2_url must be specified") + ec2 = ec2_connect(module) # Here we need to get the zone info for the instance. This covers situation where # instance is specified but zone isn't. diff --git a/library/cloud/ec2_vpc b/library/cloud/ec2_vpc index 663a574f95..53b60c9dfc 100644 --- a/library/cloud/ec2_vpc +++ b/library/cloud/ec2_vpc @@ -164,15 +164,6 @@ except ImportError: print "failed=True msg='boto required for this module'" sys.exit(1) -AWS_REGIONS = ['ap-northeast-1', - 'ap-southeast-1', - 'ap-southeast-2', - 'eu-west-1', - 'sa-east-1', - 'us-east-1', - 'us-west-1', - 'us-west-2'] - def get_vpc_info(vpc): """ Retrieves vpc information from an instance diff --git a/library/cloud/elasticache b/library/cloud/elasticache index 9b40107d98..de2c863656 100644 --- a/library/cloud/elasticache +++ b/library/cloud/elasticache @@ -137,15 +137,6 @@ import sys import os import time -AWS_REGIONS = ['ap-northeast-1', - 'ap-southeast-1', - 'ap-southeast-2', - 'eu-west-1', - 'sa-east-1', - 'us-east-1', - 'us-west-1', - 'us-west-2'] - try: import boto from boto.elasticache.layer1 import ElastiCacheConnection diff --git a/library/cloud/rds b/library/cloud/rds index 56e32ad208..4b3af90e4e 100644 --- a/library/cloud/rds +++ b/library/cloud/rds @@ -262,15 +262,6 @@ EXAMPLES = ''' import sys import time -AWS_REGIONS = ['ap-northeast-1', - 'ap-southeast-1', - 'ap-southeast-2', - 'eu-west-1', - 'sa-east-1', - 'us-east-1', - 'us-west-1', - 'us-west-2'] - try: import boto.rds except ImportError: @@ -346,25 +337,7 @@ def main(): apply_immediately = module.params.get('apply_immediately') new_instance_name = module.params.get('new_instance_name') - # allow environment variables to be used if ansible vars aren't set - if not region: - if 'AWS_REGION' in os.environ: - region = os.environ['AWS_REGION'] - elif 'EC2_REGION' in os.environ: - region = os.environ['EC2_REGION'] - - if not aws_secret_key: - if 'AWS_SECRET_KEY' in os.environ: - aws_secret_key = os.environ['AWS_SECRET_KEY'] - elif 'EC2_SECRET_KEY' in os.environ: - aws_secret_key = os.environ['EC2_SECRET_KEY'] - - if not aws_access_key: - if 'AWS_ACCESS_KEY' in os.environ: - aws_access_key = os.environ['AWS_ACCESS_KEY'] - elif 'EC2_ACCESS_KEY' in os.environ: - aws_access_key = os.environ['EC2_ACCESS_KEY'] - + ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module) if not region: module.fail_json(msg = str("region not specified and unable to determine region from EC2_REGION.")) @@ -577,5 +550,6 @@ def main(): # import module snippets from ansible.module_utils.basic import * +from ansible.module_utils.ec2 import * main() From ab52efc7fb37c8c6b7d9c19ecb27b82f97f192eb Mon Sep 17 00:00:00 2001 From: "Brad P. Crochet" Date: Tue, 17 Dec 2013 13:24:20 -0500 Subject: [PATCH 011/182] Add support for neutronclient The quantum_* modules will now try neutronclient first, and fall back to quantumclient. If that fails, error out. The code now references neutron instead of quantum in all internal cases. --- library/cloud/quantum_floating_ip | 69 +++++++++--------- library/cloud/quantum_floating_ip_associate | 56 ++++++++------- library/cloud/quantum_network | 73 +++++++++---------- library/cloud/quantum_router | 55 ++++++++------- library/cloud/quantum_router_gateway | 73 +++++++++---------- library/cloud/quantum_router_interface | 73 +++++++++---------- library/cloud/quantum_subnet | 77 +++++++++++---------- 7 files changed, 249 insertions(+), 227 deletions(-) diff --git a/library/cloud/quantum_floating_ip b/library/cloud/quantum_floating_ip index 1d755e67c2..54e1c68d79 100644 --- a/library/cloud/quantum_floating_ip +++ b/library/cloud/quantum_floating_ip @@ -18,11 +18,14 @@ try: from novaclient.v1_1 import client as nova_client - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient import time except ImportError: - print("failed=True msg='glanceclient,keystoneclient and quantumclient client are required'") + print("failed=True msg='novaclient,keystoneclient and quantumclient (or neutronclient) are required'") DOCUMENTATION = ''' --- @@ -72,7 +75,7 @@ options: - The name of the instance to which the IP address should be assigned required: true default: None -requirements: ["novaclient", "quantumclient", "keystoneclient"] +requirements: ["novaclient", "quantumclient", "neutronclient", "keystoneclient"] ''' EXAMPLES = ''' @@ -99,10 +102,10 @@ def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') except Exception as e: - module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) + module.fail_json(msg = "Error getting network endpoint: %s" % e.message) return endpoint -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -111,10 +114,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) - return quantum + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron def _get_server_state(module, nova): server_info = None @@ -130,68 +133,68 @@ def _get_server_state(module, nova): break except Exception as e: module.fail_json(msg = "Error in getting the server list: %s" % e.message) - return server_info, server - -def _get_port_info(quantum, module, instance_id): + return server_info, server + +def _get_port_info(neutron, module, instance_id): kwargs = { 'device_id': instance_id, } try: - ports = quantum.list_ports(**kwargs) + ports = neutron.list_ports(**kwargs) except Exception as e: module.fail_json( msg = "Error in listing ports: %s" % e.message) if not ports['ports']: return None, None return ports['ports'][0]['fixed_ips'][0]['ip_address'], ports['ports'][0]['id'] - -def _get_floating_ip(module, quantum, fixed_ip_address): + +def _get_floating_ip(module, neutron, fixed_ip_address): kwargs = { 'fixed_ip_address': fixed_ip_address } try: - ips = quantum.list_floatingips(**kwargs) + ips = neutron.list_floatingips(**kwargs) except Exception as e: module.fail_json(msg = "error in fetching the floatingips's %s" % e.message) if not ips['floatingips']: return None, None return ips['floatingips'][0]['id'], ips['floatingips'][0]['floating_ip_address'] -def _create_floating_ip(quantum, module, port_id, net_id): +def _create_floating_ip(neutron, module, port_id, net_id): kwargs = { 'port_id': port_id, 'floating_network_id': net_id } try: - result = quantum.create_floatingip({'floatingip': kwargs}) + result = neutron.create_floatingip({'floatingip': kwargs}) except Exception as e: module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message) module.exit_json(changed=True, result=result, public_ip=result['floatingip']['floating_ip_address']) -def _get_net_id(quantum, module): +def _get_net_id(neutron, module): kwargs = { 'name': module.params['network_name'], } try: - networks = quantum.list_networks(**kwargs) + networks = neutron.list_networks(**kwargs) except Exception as e: - module.fail_json("Error in listing quantum networks: %s" % e.message) + module.fail_json("Error in listing neutron networks: %s" % e.message) if not networks['networks']: return None return networks['networks'][0]['id'] -def _update_floating_ip(quantum, module, port_id, floating_ip_id): +def _update_floating_ip(neutron, module, port_id, floating_ip_id): kwargs = { 'port_id': port_id } try: - result = quantum.update_floatingip(floating_ip_id, {'floatingip': kwargs}) + result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs}) except Exception as e: module.fail_json(msg="There was an error in updating the floating ip address: %s" % e.message) module.exit_json(changed=True, result=result) def main(): - + module = AnsibleModule( argument_spec = dict( login_username = dict(default='admin'), @@ -200,39 +203,39 @@ def main(): auth_url = dict(default='http://127.0.0.1:35357/v2.0/'), region_name = dict(default=None), network_name = dict(required=True), - instance_name = dict(required=True), + instance_name = dict(required=True), state = dict(default='present', choices=['absent', 'present']) ), ) - + try: - nova = nova_client.Client(module.params['login_username'], module.params['login_password'], + nova = nova_client.Client(module.params['login_username'], module.params['login_password'], module.params['login_tenant_name'], module.params['auth_url'], service_type='compute') - quantum = _get_quantum_client(module, module.params) + neutron = _get_neutron_client(module, module.params) except Exception as e: module.fail_json(msg="Error in authenticating to nova: %s" % e.message) - + server_info, server_obj = _get_server_state(module, nova) if not server_info: module.fail_json(msg="The instance name provided cannot be found") - fixed_ip, port_id = _get_port_info(quantum, module, server_info['id']) + fixed_ip, port_id = _get_port_info(neutron, module, server_info['id']) if not port_id: module.fail_json(msg="Cannot find a port for this instance, maybe fixed ip is not assigned") - floating_id, floating_ip = _get_floating_ip(module, quantum, fixed_ip) + floating_id, floating_ip = _get_floating_ip(module, neutron, fixed_ip) if module.params['state'] == 'present': if floating_ip: module.exit_json(changed = False, public_ip=floating_ip) - net_id = _get_net_id(quantum, module) + net_id = _get_net_id(neutron, module) if not net_id: module.fail_json(msg = "cannot find the network specified, please check") - _create_floating_ip(quantum, module, port_id, net_id) + _create_floating_ip(neutron, module, port_id, net_id) if module.params['state'] == 'absent': if floating_ip: - _update_floating_ip(quantum, module, None, floating_id) + _update_floating_ip(neutron, module, None, floating_id) module.exit_json(changed=False) # this is magic, see lib/ansible/module.params['common.py diff --git a/library/cloud/quantum_floating_ip_associate b/library/cloud/quantum_floating_ip_associate index 9b720ea232..e878fe5086 100644 --- a/library/cloud/quantum_floating_ip_associate +++ b/library/cloud/quantum_floating_ip_associate @@ -18,11 +18,14 @@ try: from novaclient.v1_1 import client as nova_client - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client 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='novaclient, keystone, and quantumclient (or neutronclient) client are required'" DOCUMENTATION = ''' --- @@ -72,7 +75,7 @@ options: - floating ip that should be assigned to the instance required: true default: None -requirements: ["quantumclient", "keystoneclient"] +requirements: ["quantumclient", "neutronclient", "keystoneclient"] ''' EXAMPLES = ''' @@ -103,10 +106,10 @@ def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') except Exception as e: - module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) + module.fail_json(msg = "Error getting network endpoint: %s" % e.message) return endpoint -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -115,10 +118,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) - return quantum + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron def _get_server_state(module, nova): server_info = None @@ -134,24 +137,24 @@ def _get_server_state(module, nova): break except Exception as e: module.fail_json(msg = "Error in getting the server list: %s" % e.message) - return server_info, server - -def _get_port_id(quantum, module, instance_id): + return server_info, server + +def _get_port_id(neutron, module, instance_id): kwargs = dict(device_id = instance_id) try: - ports = quantum.list_ports(**kwargs) + ports = neutron.list_ports(**kwargs) except Exception as e: module.fail_json( msg = "Error in listing ports: %s" % e.message) if not ports['ports']: return None return ports['ports'][0]['id'] - -def _get_floating_ip_id(module, quantum): + +def _get_floating_ip_id(module, neutron): kwargs = { 'floating_ip_address': module.params['ip_address'] } try: - ips = quantum.list_floatingips(**kwargs) + ips = neutron.list_floatingips(**kwargs) except Exception as e: module.fail_json(msg = "error in fetching the floatingips's %s" % e.message) if not ips['floatingips']: @@ -163,18 +166,18 @@ def _get_floating_ip_id(module, quantum): state = "attached" return state, ip -def _update_floating_ip(quantum, module, port_id, floating_ip_id): +def _update_floating_ip(neutron, module, port_id, floating_ip_id): kwargs = { 'port_id': port_id } try: - result = quantum.update_floatingip(floating_ip_id, {'floatingip': kwargs}) + result = neutron.update_floatingip(floating_ip_id, {'floatingip': kwargs}) except Exception as e: module.fail_json(msg = "There was an error in updating the floating ip address: %s" % e.message) module.exit_json(changed = True, result = result, public_ip=module.params['ip_address']) def main(): - + module = AnsibleModule( argument_spec = dict( login_username = dict(default='admin'), @@ -183,33 +186,34 @@ def main(): auth_url = dict(default='http://127.0.0.1:35357/v2.0/'), region_name = dict(default=None), ip_address = dict(required=True), - instance_name = dict(required=True), + instance_name = dict(required=True), state = dict(default='present', choices=['absent', 'present']) ), ) - + try: - nova = nova_client.Client(module.params['login_username'], module.params['login_password'], module.params['login_tenant_name'], module.params['auth_url'], service_type='compute') + nova = nova_client.Client(module.params['login_username'], module.params['login_password'], + module.params['login_tenant_name'], module.params['auth_url'], service_type='compute') except Exception as e: module.fail_json( msg = " Error in authenticating to nova: %s" % e.message) - quantum = _get_quantum_client(module, module.params) - state, floating_ip_id = _get_floating_ip_id(module, quantum) + neutron = _get_neutron_client(module, module.params) + state, floating_ip_id = _get_floating_ip_id(module, neutron) if module.params['state'] == 'present': if state == 'attached': module.exit_json(changed = False, result = 'attached', public_ip=module.params['ip_address']) server_info, server_obj = _get_server_state(module, nova) if not server_info: module.fail_json(msg = " The instance name provided cannot be found") - port_id = _get_port_id(quantum, module, server_info['id']) + port_id = _get_port_id(neutron, module, server_info['id']) if not port_id: module.fail_json(msg = "Cannot find a port for this instance, maybe fixed ip is not assigned") - _update_floating_ip(quantum, module, port_id, floating_ip_id) + _update_floating_ip(neutron, module, port_id, floating_ip_id) if module.params['state'] == 'absent': if state == 'detached': module.exit_json(changed = False, result = 'detached') if state == 'attached': - _update_floating_ip(quantum, module, None, floating_ip_id) + _update_floating_ip(neutron, module, None, floating_ip_id) module.exit_json(changed = True, result = "detached") # this is magic, see lib/ansible/module.params['common.py diff --git a/library/cloud/quantum_network b/library/cloud/quantum_network index 4eb416f752..be6951c0b7 100644 --- a/library/cloud/quantum_network +++ b/library/cloud/quantum_network @@ -17,10 +17,13 @@ # along with this software. If not, see . try: - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient except ImportError: - print("failed=True msg='quantumclient and keystone client are required'") + print("failed=True msg='quantumclient (or neutronclient) and keystone client are required'") DOCUMENTATION = ''' --- @@ -67,7 +70,7 @@ options: default: present name: description: - - Name to be assigned to the nework + - Name to be assigned to the nework required: true default: None provider_network_type: @@ -100,7 +103,7 @@ options: - Whether the state should be marked as up or down required: false default: true -requirements: ["quantumclient", "keystoneclient"] +requirements: ["quantumclient", "neutronclient", "keystoneclient"] ''' @@ -125,21 +128,21 @@ def _get_ksclient(module, kwargs): password=kwargs.get('login_password'), tenant_name=kwargs.get('login_tenant_name'), auth_url=kwargs.get('auth_url')) - except Exception as e: + except Exception as e: module.fail_json(msg = "Error authenticating to the keystone: %s" %e.message) - global _os_keystone + global _os_keystone _os_keystone = kclient - return kclient - + return kclient + def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') except Exception as e: - module.fail_json(msg = "Error getting endpoint for Quantum: %s " %e.message) + module.fail_json(msg = "Error getting network endpoint: %s " %e.message) return endpoint -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -148,10 +151,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = " Error in connecting to quantum: %s " %e.message) - return quantum + module.fail_json(msg = " Error in connecting to neutron: %s " %e.message) + return neutron def _set_tenant_id(module): global _os_tenant_id @@ -159,7 +162,7 @@ def _set_tenant_id(module): tenant_name = module.params['login_tenant_name'] else: tenant_name = module.params['tenant_name'] - + for tenant in _os_keystone.tenants.list(): if tenant.name == tenant_name: _os_tenant_id = tenant.id @@ -168,22 +171,22 @@ def _set_tenant_id(module): module.fail_json(msg = "The tenant id cannot be found, please check the paramters") -def _get_net_id(quantum, module): +def _get_net_id(neutron, module): kwargs = { 'tenant_id': _os_tenant_id, 'name': module.params['name'], } try: - networks = quantum.list_networks(**kwargs) + networks = neutron.list_networks(**kwargs) except Exception as e: - module.fail_json(msg = "Error in listing quantum networks: %s" % e.message) - if not networks['networks']: + module.fail_json(msg = "Error in listing neutron networks: %s" % e.message) + if not networks['networks']: return None return networks['networks'][0]['id'] -def _create_network(module, quantum): +def _create_network(module, neutron): - quantum.format = 'json' + neutron.format = 'json' network = { 'name': module.params.get('name'), @@ -212,21 +215,21 @@ def _create_network(module, quantum): network.pop('provider:segmentation_id', None) try: - net = quantum.create_network({'network':network}) + net = neutron.create_network({'network':network}) except Exception as e: module.fail_json(msg = "Error in creating network: %s" % e.message) return net['network']['id'] - -def _delete_network(module, net_id, quantum): + +def _delete_network(module, net_id, neutron): try: - id = quantum.delete_network(net_id) - except Exception as e: + id = neutron.delete_network(net_id) + except Exception as e: module.fail_json(msg = "Error in deleting the network: %s" % e.message) return True def main(): - + module = AnsibleModule( argument_spec = dict( login_username = dict(default='admin'), @@ -237,8 +240,8 @@ def main(): name = dict(required=True), tenant_name = dict(default=None), provider_network_type = dict(default=None, choices=['local', 'vlan', 'flat', 'gre']), - provider_physical_network = dict(default=None), - provider_segmentation_id = dict(default=None), + provider_physical_network = dict(default=None), + provider_segmentation_id = dict(default=None), router_external = dict(default=False, type='bool'), shared = dict(default=False, type='bool'), admin_state_up = dict(default=True, type='bool'), @@ -254,24 +257,24 @@ def main(): if not module.params['provider_segmentation_id']: module.fail_json(msg = " for vlan & gre networks, variable provider_segmentation_id should be set.") - quantum = _get_quantum_client(module, module.params) + neutron = _get_neutron_client(module, module.params) - _set_tenant_id(module) + _set_tenant_id(module) - if module.params['state'] == 'present': - network_id = _get_net_id(quantum, module) + if module.params['state'] == 'present': + network_id = _get_net_id(neutron, module) if not network_id: - network_id = _create_network(module, quantum) + network_id = _create_network(module, neutron) module.exit_json(changed = True, result = "Created", id = network_id) else: module.exit_json(changed = False, result = "Success", id = network_id) if module.params['state'] == 'absent': - network_id = _get_net_id(quantum, module) + network_id = _get_net_id(neutron, module) if not network_id: module.exit_json(changed = False, result = "Success") else: - _delete_network(module, network_id, quantum) + _delete_network(module, network_id, neutron) module.exit_json(changed = True, result = "Deleted") # this is magic, see lib/ansible/module.params['common.py diff --git a/library/cloud/quantum_router b/library/cloud/quantum_router index 26387de205..9d31da2f19 100644 --- a/library/cloud/quantum_router +++ b/library/cloud/quantum_router @@ -17,10 +17,13 @@ # along with this software. If not, see . try: - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient except ImportError: - print("failed=True msg='quantumclient and keystone client are required'") + print("failed=True msg='quantumclient (or neutronclient) and keystone client are required'") DOCUMENTATION = ''' --- @@ -75,7 +78,7 @@ options: - desired admin state of the created router . required: false default: true -requirements: ["quantumclient", "keystoneclient"] +requirements: ["quantumclient", "neutronclient", "keystoneclient"] ''' EXAMPLES = ''' @@ -96,21 +99,21 @@ def _get_ksclient(module, kwargs): password=kwargs.get('login_password'), tenant_name=kwargs.get('login_tenant_name'), auth_url=kwargs.get('auth_url')) - except Exception as e: + except Exception as e: module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message) - global _os_keystone + global _os_keystone _os_keystone = kclient - return kclient - + return kclient + def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') except Exception as e: - module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) + module.fail_json(msg = "Error getting network endpoint: %s" % e.message) return endpoint -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -119,10 +122,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) - return quantum + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron def _set_tenant_id(module): global _os_tenant_id @@ -139,38 +142,38 @@ def _set_tenant_id(module): module.fail_json(msg = "The tenant id cannot be found, please check the paramters") -def _get_router_id(module, quantum): +def _get_router_id(module, neutron): kwargs = { 'name': module.params['name'], 'tenant_id': _os_tenant_id, } try: - routers = quantum.list_routers(**kwargs) + routers = neutron.list_routers(**kwargs) except Exception as e: module.fail_json(msg = "Error in getting the router list: %s " % e.message) if not routers['routers']: return None return routers['routers'][0]['id'] -def _create_router(module, quantum): +def _create_router(module, neutron): router = { 'name': module.params['name'], 'tenant_id': _os_tenant_id, 'admin_state_up': module.params['admin_state_up'], } try: - new_router = quantum.create_router(dict(router=router)) + new_router = neutron.create_router(dict(router=router)) except Exception as e: module.fail_json( msg = "Error in creating router: %s" % e.message) return new_router['router']['id'] -def _delete_router(module, quantum, router_id): +def _delete_router(module, neutron, router_id): try: - quantum.delete_router(router_id) + neutron.delete_router(router_id) except: module.fail_json("Error in deleting the router") return True - + def main(): module = AnsibleModule( argument_spec = dict( @@ -185,26 +188,26 @@ def main(): admin_state_up = dict(type='bool', default=True), ), ) - - quantum = _get_quantum_client(module, module.params) + + neutron = _get_neutron_client(module, module.params) _set_tenant_id(module) if module.params['state'] == 'present': - router_id = _get_router_id(module, quantum) + router_id = _get_router_id(module, neutron) if not router_id: - router_id = _create_router(module, quantum) + router_id = _create_router(module, neutron) module.exit_json(changed=True, result="Created", id=router_id) else: module.exit_json(changed=False, result="success" , id=router_id) else: - router_id = _get_router_id(module, quantum) + router_id = _get_router_id(module, neutron) if not router_id: module.exit_json(changed=False, result="success") else: - _delete_router(module, quantum, router_id) + _delete_router(module, neutron, router_id) module.exit_json(changed=True, result="deleted") - + # this is magic, see lib/ansible/module.params['common.py from ansible.module_utils.basic import * main() diff --git a/library/cloud/quantum_router_gateway b/library/cloud/quantum_router_gateway index 60d500e6f6..68372e785d 100644 --- a/library/cloud/quantum_router_gateway +++ b/library/cloud/quantum_router_gateway @@ -17,10 +17,13 @@ # along with this software. If not, see . try: - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient except ImportError: - print("failed=True msg='quantumclient and keystone client are required'") + print("failed=True msg='quantumclient (or neutronclient) and keystone client are required'") DOCUMENTATION = ''' --- module: quantum_router_gateway @@ -69,7 +72,7 @@ options: - Name of the external network which should be attached to the router. required: true default: None -requirements: ["quantumclient", "keystoneclient"] +requirements: ["quantumclient", "neutronclient", "keystoneclient"] ''' EXAMPLES = ''' @@ -86,21 +89,21 @@ def _get_ksclient(module, kwargs): password=kwargs.get('login_password'), tenant_name=kwargs.get('login_tenant_name'), auth_url=kwargs.get('auth_url')) - except Exception as e: + except Exception as e: module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message) - global _os_keystone + global _os_keystone _os_keystone = kclient - return kclient - + return kclient + def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') except Exception as e: - module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) + module.fail_json(msg = "Error getting network endpoint: %s" % e.message) return endpoint -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -109,68 +112,68 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) - return quantum + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron -def _get_router_id(module, quantum): +def _get_router_id(module, neutron): kwargs = { 'name': module.params['router_name'], } try: - routers = quantum.list_routers(**kwargs) + routers = neutron.list_routers(**kwargs) except Exception as e: module.fail_json(msg = "Error in getting the router list: %s " % e.message) if not routers['routers']: return None return routers['routers'][0]['id'] -def _get_net_id(quantum, module): +def _get_net_id(neutron, module): kwargs = { 'name': module.params['network_name'], 'router:external': True } try: - networks = quantum.list_networks(**kwargs) + networks = neutron.list_networks(**kwargs) except Exception as e: - module.fail_json("Error in listing quantum networks: %s" % e.message) + module.fail_json("Error in listing neutron networks: %s" % e.message) if not networks['networks']: return None return networks['networks'][0]['id'] -def _get_port_id(quantum, module, router_id, network_id): +def _get_port_id(neutron, module, router_id, network_id): kwargs = { 'device_id': router_id, 'network_id': network_id, } try: - ports = quantum.list_ports(**kwargs) + ports = neutron.list_ports(**kwargs) except Exception as e: module.fail_json( msg = "Error in listing ports: %s" % e.message) if not ports['ports']: return None return ports['ports'][0]['id'] -def _add_gateway_router(quantum, module, router_id, network_id): +def _add_gateway_router(neutron, module, router_id, network_id): kwargs = { 'network_id': network_id } try: - quantum.add_gateway_router(router_id, kwargs) + neutron.add_gateway_router(router_id, kwargs) except Exception as e: module.fail_json(msg = "Error in adding gateway to router: %s" % e.message) return True - -def _remove_gateway_router(quantum, module, router_id): + +def _remove_gateway_router(neutron, module, router_id): try: - quantum.remove_gateway_router(router_id) + neutron.remove_gateway_router(router_id) except Exception as e: module.fail_json(msg = "Error in removing gateway to router: %s" % e.message) return True - + def main(): - + module = AnsibleModule( argument_spec = dict( login_username = dict(default='admin'), @@ -183,29 +186,29 @@ def main(): state = dict(default='present', choices=['absent', 'present']), ), ) - - quantum = _get_quantum_client(module, module.params) - router_id = _get_router_id(module, quantum) + + neutron = _get_neutron_client(module, module.params) + router_id = _get_router_id(module, neutron) if not router_id: module.fail_json(msg="failed to get the router id, please check the router name") - network_id = _get_net_id(quantum, module) + network_id = _get_net_id(neutron, module) if not network_id: module.fail_json(msg="failed to get the network id, please check the network name and make sure it is external") - + if module.params['state'] == 'present': - port_id = _get_port_id(quantum, module, router_id, network_id) + port_id = _get_port_id(neutron, module, router_id, network_id) if not port_id: - _add_gateway_router(quantum, module, router_id, network_id) + _add_gateway_router(neutron, module, router_id, network_id) module.exit_json(changed=True, result="created") module.exit_json(changed=False, result="success") if module.params['state'] == 'absent': - port_id = _get_port_id(quantum, module, router_id, network_id) + port_id = _get_port_id(neutron, module, router_id, network_id) if not port_id: module.exit_json(changed=False, result="Success") - _remove_gateway_router(quantum, module, router_id) + _remove_gateway_router(neutron, module, router_id) module.exit_json(changed=True, result="Deleted") # this is magic, see lib/ansible/module.params['common.py diff --git a/library/cloud/quantum_router_interface b/library/cloud/quantum_router_interface index f34aecacf5..05f1f303a8 100644 --- a/library/cloud/quantum_router_interface +++ b/library/cloud/quantum_router_interface @@ -17,10 +17,13 @@ # along with this software. If not, see . try: - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient except ImportError: - print("failed=True msg='quantumclient and keystone client are required'") + print("failed=True msg='quantumclient (or neutronclient) and keystone client are required'") DOCUMENTATION = ''' --- module: quantum_router_interface @@ -81,7 +84,7 @@ EXAMPLES = ''' # Attach tenant1's subnet to the external router - quantum_router_interface: state=present login_username=admin login_password=admin - login_tenant_name=admin + login_tenant_name=admin tenant_name=tenant1 router_name=external_route subnet_name=t1subnet @@ -97,21 +100,21 @@ def _get_ksclient(module, kwargs): password=kwargs.get('login_password'), tenant_name=kwargs.get('login_tenant_name'), auth_url=kwargs.get('auth_url')) - except Exception as e: + except Exception as e: module.fail_json(msg = "Error authenticating to the keystone: %s " % e.message) - global _os_keystone + global _os_keystone _os_keystone = kclient - return kclient - + return kclient + def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') except Exception as e: - module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) + module.fail_json(msg = "Error getting network endpoint: %s" % e.message) return endpoint -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -120,10 +123,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = "Error in connecting to quantum: %s " % e.message) - return quantum + module.fail_json(msg = "Error in connecting to neutron: %s " % e.message) + return neutron def _set_tenant_id(module): global _os_tenant_id @@ -140,12 +143,12 @@ def _set_tenant_id(module): module.fail_json(msg = "The tenant id cannot be found, please check the paramters") -def _get_router_id(module, quantum): +def _get_router_id(module, neutron): kwargs = { 'name': module.params['router_name'], } try: - routers = quantum.list_routers(**kwargs) + routers = neutron.list_routers(**kwargs) except Exception as e: module.fail_json(msg = "Error in getting the router list: %s " % e.message) if not routers['routers']: @@ -153,27 +156,27 @@ def _get_router_id(module, quantum): return routers['routers'][0]['id'] -def _get_subnet_id(module, quantum): +def _get_subnet_id(module, neutron): subnet_id = None kwargs = { 'tenant_id': _os_tenant_id, 'name': module.params['subnet_name'], } try: - subnets = quantum.list_subnets(**kwargs) + subnets = neutron.list_subnets(**kwargs) except Exception as e: module.fail_json( msg = " Error in getting the subnet list:%s " % e.message) if not subnets['subnets']: return None return subnets['subnets'][0]['id'] - -def _get_port_id(quantum, module, router_id, subnet_id): + +def _get_port_id(neutron, module, router_id, subnet_id): kwargs = { 'tenant_id': _os_tenant_id, 'device_id': router_id, } try: - ports = quantum.list_ports(**kwargs) + ports = neutron.list_ports(**kwargs) except Exception as e: module.fail_json( msg = "Error in listing ports: %s" % e.message) if not ports['ports']: @@ -184,26 +187,26 @@ def _get_port_id(quantum, module, router_id, subnet_id): return port['id'] return None -def _add_interface_router(quantum, module, router_id, subnet_id): +def _add_interface_router(neutron, module, router_id, subnet_id): kwargs = { 'subnet_id': subnet_id } try: - quantum.add_interface_router(router_id, kwargs) + neutron.add_interface_router(router_id, kwargs) except Exception as e: module.fail_json(msg = "Error in adding interface to router: %s" % e.message) return True - -def _remove_interface_router(quantum, module, router_id, subnet_id): + +def _remove_interface_router(neutron, module, router_id, subnet_id): kwargs = { 'subnet_id': subnet_id } try: - quantum.remove_interface_router(router_id, kwargs) + neutron.remove_interface_router(router_id, kwargs) except Exception as e: module.fail_json(msg="Error in removing interface from router: %s" % e.message) return True - + def main(): module = AnsibleModule( argument_spec = dict( @@ -218,32 +221,32 @@ def main(): state = dict(default='present', choices=['absent', 'present']), ), ) - - quantum = _get_quantum_client(module, module.params) + + neutron = _get_neutron_client(module, module.params) _set_tenant_id(module) - router_id = _get_router_id(module, quantum) + router_id = _get_router_id(module, neutron) if not router_id: module.fail_json(msg="failed to get the router id, please check the router name") - subnet_id = _get_subnet_id(module, quantum) + subnet_id = _get_subnet_id(module, neutron) if not subnet_id: module.fail_json(msg="failed to get the subnet id, please check the subnet name") - + if module.params['state'] == 'present': - port_id = _get_port_id(quantum, module, router_id, subnet_id) + port_id = _get_port_id(neutron, module, router_id, subnet_id) if not port_id: - _add_interface_router(quantum, module, router_id, subnet_id) + _add_interface_router(neutron, module, router_id, subnet_id) module.exit_json(changed=True, result="created", id=port_id) module.exit_json(changed=False, result="success", id=port_id) if module.params['state'] == 'absent': - port_id = _get_port_id(quantum, module, router_id, subnet_id) + port_id = _get_port_id(neutron, module, router_id, subnet_id) if not port_id: module.exit_json(changed = False, result = "Success") - _remove_interface_router(quantum, module, router_id, subnet_id) + _remove_interface_router(neutron, module, router_id, subnet_id) module.exit_json(changed=True, result="Deleted") - + # this is magic, see lib/ansible/module.params['common.py from ansible.module_utils.basic import * main() diff --git a/library/cloud/quantum_subnet b/library/cloud/quantum_subnet index 372d346f71..9d40131061 100644 --- a/library/cloud/quantum_subnet +++ b/library/cloud/quantum_subnet @@ -17,10 +17,13 @@ # along with this software. If not, see . try: - from quantumclient.quantum import client + try: + from neutronclient.neutron import client + except ImportError: + from quantumclient.quantum import client from keystoneclient.v2_0 import client as ksclient except ImportError: - print("failed=True msg='quantum and keystone client are required'") + print("failed=True msg='quantumclient (or neutronclient) and keystoneclient are required'") DOCUMENTATION = ''' --- @@ -77,7 +80,7 @@ options: default: None ip_version: description: - - The IP version of the subnet 4 or 6 + - The IP version of the subnet 4 or 6 required: false default: 4 enable_dhcp: @@ -105,7 +108,7 @@ options: - From the subnet pool the last IP that should be assigned to the virtual machines required: false default: None -requirements: ["quantum", "keystoneclient"] +requirements: ["quantumclient", "neutronclient", "keystoneclient"] ''' EXAMPLES = ''' @@ -125,21 +128,21 @@ def _get_ksclient(module, kwargs): password=kwargs.get('login_password'), tenant_name=kwargs.get('login_tenant_name'), auth_url=kwargs.get('auth_url')) - except Exception as e: + except Exception as e: module.fail_json(msg = "Error authenticating to the keystone: %s" %e.message) - global _os_keystone + global _os_keystone _os_keystone = kclient - return kclient - + return kclient + def _get_endpoint(module, ksclient): try: endpoint = ksclient.service_catalog.url_for(service_type='network', endpoint_type='publicURL') except Exception as e: - module.fail_json(msg = "Error getting endpoint for glance: %s" % e.message) + module.fail_json(msg = "Error getting network endpoint: %s" % e.message) return endpoint -def _get_quantum_client(module, kwargs): +def _get_neutron_client(module, kwargs): _ksclient = _get_ksclient(module, kwargs) token = _ksclient.auth_token endpoint = _get_endpoint(module, _ksclient) @@ -148,10 +151,10 @@ def _get_quantum_client(module, kwargs): 'endpoint_url': endpoint } try: - quantum = client.Client('2.0', **kwargs) + neutron = client.Client('2.0', **kwargs) except Exception as e: - module.fail_json(msg = " Error in connecting to quantum: %s" % e.message) - return quantum + module.fail_json(msg = " Error in connecting to neutron: %s" % e.message) + return neutron def _set_tenant_id(module): global _os_tenant_id @@ -167,24 +170,24 @@ def _set_tenant_id(module): if not _os_tenant_id: module.fail_json(msg = "The tenant id cannot be found, please check the paramters") -def _get_net_id(quantum, module): +def _get_net_id(neutron, module): kwargs = { 'tenant_id': _os_tenant_id, 'name': module.params['network_name'], } try: - networks = quantum.list_networks(**kwargs) + networks = neutron.list_networks(**kwargs) except Exception as e: - module.fail_json("Error in listing quantum networks: %s" % e.message) + module.fail_json("Error in listing neutron networks: %s" % e.message) if not networks['networks']: return None return networks['networks'][0]['id'] -def _get_subnet_id(module, quantum): +def _get_subnet_id(module, neutron): global _os_network_id subnet_id = None - _os_network_id = _get_net_id(quantum, module) + _os_network_id = _get_net_id(neutron, module) if not _os_network_id: module.fail_json(msg = "network id of network not found.") else: @@ -193,15 +196,15 @@ def _get_subnet_id(module, quantum): 'name': module.params['name'], } try: - subnets = quantum.list_subnets(**kwargs) + subnets = neutron.list_subnets(**kwargs) except Exception as e: module.fail_json( msg = " Error in getting the subnet list:%s " % e.message) if not subnets['subnets']: return None return subnets['subnets'][0]['id'] -def _create_subnet(module, quantum): - quantum.format = 'json' +def _create_subnet(module, neutron): + neutron.format = 'json' subnet = { 'name': module.params['name'], 'ip_version': module.params['ip_version'], @@ -214,7 +217,7 @@ def _create_subnet(module, quantum): } if module.params['allocation_pool_start'] and module.params['allocation_pool_end']: allocation_pools = [ - { + { 'start' : module.params['allocation_pool_start'], 'end' : module.params['allocation_pool_end'] } @@ -227,22 +230,22 @@ def _create_subnet(module, quantum): else: subnet.pop('dns_nameservers') try: - new_subnet = quantum.create_subnet(dict(subnet=subnet)) + new_subnet = neutron.create_subnet(dict(subnet=subnet)) except Exception, e: - module.fail_json(msg = "Failure in creating subnet: %s" % e.message) + module.fail_json(msg = "Failure in creating subnet: %s" % e.message) return new_subnet['subnet']['id'] - - -def _delete_subnet(module, quantum, subnet_id): + + +def _delete_subnet(module, neutron, subnet_id): try: - quantum.delete_subnet(subnet_id) + neutron.delete_subnet(subnet_id) except Exception as e: module.fail_json( msg = "Error in deleting subnet: %s" % e.message) return True - - + + def main(): - + module = AnsibleModule( argument_spec = dict( login_username = dict(default='admin'), @@ -263,23 +266,23 @@ def main(): allocation_pool_end = dict(default=None), ), ) - quantum = _get_quantum_client(module, module.params) + neutron = _get_neutron_client(module, module.params) _set_tenant_id(module) if module.params['state'] == 'present': - subnet_id = _get_subnet_id(module, quantum) + subnet_id = _get_subnet_id(module, neutron) if not subnet_id: - subnet_id = _create_subnet(module, quantum) + subnet_id = _create_subnet(module, neutron) module.exit_json(changed = True, result = "Created" , id = subnet_id) else: module.exit_json(changed = False, result = "success" , id = subnet_id) else: - subnet_id = _get_subnet_id(module, quantum) + subnet_id = _get_subnet_id(module, neutron) if not subnet_id: module.exit_json(changed = False, result = "success") else: - _delete_subnet(module, quantum, subnet_id) + _delete_subnet(module, neutron, subnet_id) module.exit_json(changed = True, result = "deleted") - + # this is magic, see lib/ansible/module.params['common.py from ansible.module_utils.basic import * main() From 47f995ef64f435478308f7a3dbe812fc0d76c5d4 Mon Sep 17 00:00:00 2001 From: "Brad P. Crochet" Date: Tue, 17 Dec 2013 14:24:30 -0500 Subject: [PATCH 012/182] Specify internal_network_name This allow one to specify a specific internal network name for the case where there is more than one nic on an instance. Without this, the list of ports may not return the correct order. Therefore, it is necessary to specify the exact internal network to attach the floating ip to --- library/cloud/quantum_floating_ip | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/library/cloud/quantum_floating_ip b/library/cloud/quantum_floating_ip index 54e1c68d79..531ddfe2b8 100644 --- a/library/cloud/quantum_floating_ip +++ b/library/cloud/quantum_floating_ip @@ -67,7 +67,7 @@ options: default: present network_name: description: - - Name of the network from which IP has to be assigned to VM. Please make sure the network is an external network + - Name of the network from which IP has to be assigned to VM. Please make sure the network is an external network required: true default: None instance_name: @@ -75,6 +75,11 @@ options: - The name of the instance to which the IP address should be assigned required: true default: None + internal_network_name: + description: + - The name of the network of the port to associate with the floating ip. Necessary when VM multiple networks. + required: false + default: None requirements: ["novaclient", "quantumclient", "neutronclient", "keystoneclient"] ''' @@ -82,7 +87,7 @@ EXAMPLES = ''' # Assign a floating ip to the instance from an external network - quantum_floating_ip: state=present login_username=admin login_password=admin login_tenant_name=admin network_name=external_network - instance_name=vm1 + instance_name=vm1 internal_network_name=internal_network ''' def _get_ksclient(module, kwargs): @@ -135,7 +140,13 @@ def _get_server_state(module, nova): module.fail_json(msg = "Error in getting the server list: %s" % e.message) return server_info, server -def _get_port_info(neutron, module, instance_id): +def _get_port_info(neutron, module, instance_id, internal_network_name=None): + if internal_network_name: + kwargs = { + 'name': internal_network_name, + } + networks = neutron.list_networks(**kwargs) + subnet_id = networks['networks'][0]['subnets'][0] kwargs = { 'device_id': instance_id, } @@ -143,9 +154,16 @@ def _get_port_info(neutron, module, instance_id): ports = neutron.list_ports(**kwargs) except Exception as e: module.fail_json( msg = "Error in listing ports: %s" % e.message) + if subnet_id: + port = next(port for port in ports['ports'] if port['fixed_ips'][0]['subnet_id'] == subnet_id) + port_id = port['id'] + fixed_ip_address = port['fixed_ips'][0]['ip_address'] + else: + port_id = ports['ports'][0]['id'] + fixed_ip_address = ports['ports'][0]['fixed_ips'][0]['ip_address'] if not ports['ports']: return None, None - return ports['ports'][0]['fixed_ips'][0]['ip_address'], ports['ports'][0]['id'] + return fixed_ip_address, port_id def _get_floating_ip(module, neutron, fixed_ip_address): kwargs = { @@ -204,7 +222,8 @@ def main(): region_name = dict(default=None), network_name = dict(required=True), instance_name = dict(required=True), - state = dict(default='present', choices=['absent', 'present']) + state = dict(default='present', choices=['absent', 'present']), + internal_network_name = dict(default=None), ), ) @@ -219,7 +238,7 @@ def main(): if not server_info: module.fail_json(msg="The instance name provided cannot be found") - fixed_ip, port_id = _get_port_info(neutron, module, server_info['id']) + fixed_ip, port_id = _get_port_info(neutron, module, server_info['id'], module.params['internal_network_name']) if not port_id: module.fail_json(msg="Cannot find a port for this instance, maybe fixed ip is not assigned") From a3dcac0a0ff3240d7b002e417383df2107f6fad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20L=C3=B3pez?= Date: Tue, 17 Dec 2013 14:45:42 -0500 Subject: [PATCH 013/182] django_manage need not require virtualenv in PATH The virtualenv parameter to the django_manage command is used to locate the virtualenv and build it if necessary. Access to the virtualenv executable is only needed if the virtualenv directory doesn't exist and needs to be built. This patch allows for the situation where a virtualenv that is not in the PATH was used to create a virtualenv prior to running the django_manage module. --- library/web_infrastructure/django_manage | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/web_infrastructure/django_manage b/library/web_infrastructure/django_manage index f6ea9c4914..68eb92c1bf 100644 --- a/library/web_infrastructure/django_manage +++ b/library/web_infrastructure/django_manage @@ -129,12 +129,11 @@ def _ensure_virtualenv(module): if venv_param is None: return - virtualenv = module.get_bin_path('virtualenv', True) - vbin = os.path.join(os.path.expanduser(venv_param), 'bin') activate = os.path.join(vbin, 'activate') if not os.path.exists(activate): + virtualenv = module.get_bin_path('virtualenv', True) vcmd = '%s %s' % (virtualenv, venv_param) vcmd = [virtualenv, venv_param] rc, out_venv, err_venv = module.run_command(vcmd) From ad6ffe00d0aaee1d7c4cddde4ef87a96f33785ca Mon Sep 17 00:00:00 2001 From: Serge van Ginderachter Date: Tue, 17 Dec 2013 21:48:29 +0100 Subject: [PATCH 014/182] bigip_monitor_http: two small bug fixes - extra properties were not set at creation, only when updating which can be overlooked when running the module from more than 1 node... - fix bas var as time_until_up didn't get used --- library/net_infrastructure/bigip_monitor_http | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/library/net_infrastructure/bigip_monitor_http b/library/net_infrastructure/bigip_monitor_http index 924c826eaa..7a05808e74 100644 --- a/library/net_infrastructure/bigip_monitor_http +++ b/library/net_infrastructure/bigip_monitor_http @@ -94,19 +94,19 @@ options: required: true default: none ip: - description: + description: - IP address part of the ipport definition. The default API setting is "0.0.0.0". required: false default: none port: - description: + description: - port address part op the ipport definition. Tyhe default API setting is 0. required: false default: none interval: - description: + description: - The interval specifying how frequently the monitor instance of this template will run. By default, this interval is used for up and down states. The default API setting is 5. @@ -199,7 +199,7 @@ def check_monitor_exists(module, api, monitor, parent): def create_monitor(api, monitor, template_attributes): - try: + try: api.LocalLB.Monitor.create_template(templates=[{'template_name': monitor, 'template_type': TEMPLATE_TYPE}], template_attributes=[template_attributes]) except bigsuds.OperationFailed, e: if "already exists" in str(e): @@ -282,7 +282,7 @@ def set_ipport(api, monitor, ipport): # =========================================== # main loop # -# writing a module for other monitor types should +# writing a module for other monitor types should # only need an updated main() (and monitor specific functions) def main(): @@ -345,19 +345,19 @@ def main(): if port is None: port = cur_ipport['ipport']['port'] else: # use API defaults if not defined to create it - if interval is None: + if interval is None: interval = 5 - if timeout is None: + if timeout is None: timeout = 16 - if ip is None: + if ip is None: ip = '0.0.0.0' - if port is None: + if port is None: port = 0 - if send is None: + if send is None: send = '' - if receive is None: + if receive is None: receive = '' - if receive_disable is None: + if receive_disable is None: receive_disable = '' # define and set address type @@ -394,7 +394,7 @@ def main(): {'type': 'ITYPE_TIMEOUT', 'value': timeout}, {'type': 'ITYPE_TIME_UNTIL_UP', - 'value': interval}] + 'value': time_until_up}] # main logic, monitor generic @@ -405,7 +405,7 @@ def main(): if state == 'absent': if monitor_exists: if not module.check_mode: - # possible race condition if same task + # possible race condition if same task # on other node deleted it first result['changed'] |= delete_monitor(api, monitor) else: @@ -414,26 +414,24 @@ def main(): else: # state present ## check for monitor itself if not monitor_exists: # create it - if not module.check_mode: + if not module.check_mode: # again, check changed status here b/c race conditions # if other task already created it result['changed'] |= create_monitor(api, monitor, template_attributes) - else: + else: result['changed'] |= True ## check for monitor parameters # whether it already existed, or was just created, now update # the update functions need to check for check mode but # cannot update settings if it doesn't exist which happens in check mode - if monitor_exists and not module.check_mode: - result['changed'] |= update_monitor_properties(api, module, monitor, - template_string_properties, - template_integer_properties) - # else assume nothing changed + result['changed'] |= update_monitor_properties(api, module, monitor, + template_string_properties, + template_integer_properties) # we just have to update the ipport if monitor already exists and it's different if monitor_exists and cur_ipport != ipport: - set_ipport(api, monitor, ipport) + set_ipport(api, monitor, ipport) result['changed'] |= True #else: monitor doesn't exist (check mode) or ipport is already ok From 4bf9f714d031d9f352dc71e23ac7522d9bf2d47f Mon Sep 17 00:00:00 2001 From: John Barker Date: Wed, 18 Dec 2013 21:33:05 +0000 Subject: [PATCH 015/182] Fix inventory parsing so that FQDN can be parsed without throwing ssh port parsing errors Fixes problesm introduced by 948d019fef8781b22960859caaa93485a074e339 Adds testcases to defend --- lib/ansible/inventory/ini.py | 8 ++++---- test/TestInventory.py | 18 ++++++++++++++++++ test/complex_hosts | 6 ++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/ansible/inventory/ini.py b/lib/ansible/inventory/ini.py index 69689758aa..69d0847c84 100644 --- a/lib/ansible/inventory/ini.py +++ b/lib/ansible/inventory/ini.py @@ -89,10 +89,10 @@ class InventoryParser(object): # 0. A hostname that contains a range pesudo-code and a port # 1. A hostname that contains just a port if hostname.count(":") > 1: - # probably an IPv6 addresss, so check for the format - # XXX:XXX::XXX.port, otherwise we'll just assume no - # port is set - if hostname.find(".") != -1: + # Possible an IPv6 address, or maybe a host line with multiple ranges + # IPv6 with Port XXX:XXX::XXX.port + # FQDN foo.example.com + if hostname.count(".") == 1: (hostname, port) = hostname.rsplit(".", 1) elif (hostname.find("[") != -1 and hostname.find("]") != -1 and diff --git a/test/TestInventory.py b/test/TestInventory.py index e48e70ae94..60ebeb7fc2 100644 --- a/test/TestInventory.py +++ b/test/TestInventory.py @@ -163,6 +163,21 @@ class TestInventory(unittest.TestCase): var = inventory.get_variables('FE80:EF45::12:1') self.assertEqual(var['ansible_ssh_port'], 2222) + def test_simple_string_fqdn(self): + inventory = Inventory('foo.example.com,bar.example.com') + hosts = inventory.list_hosts() + self.assertEqual(sorted(hosts), sorted(['foo.example.com','bar.example.com'])) + + def test_simple_string_fqdn_port(self): + inventory = Inventory('foo.example.com:2222,bar.example.com') + hosts = inventory.list_hosts() + self.assertEqual(sorted(hosts), sorted(['foo.example.com','bar.example.com'])) + + def test_simple_string_fqdn_vars(self): + inventory = Inventory('foo.example.com:2222,bar.example.com') + var = inventory.get_variables('foo.example.com') + self.assertEqual(var['ansible_ssh_port'], 2222) + def test_simple_vars(self): inventory = self.simple_inventory() vars = inventory.get_variables('thor') @@ -254,6 +269,7 @@ class TestInventory(unittest.TestCase): expected2 = ['rtp_a', 'rtp_b'] expected3 = ['rtp_a', 'rtp_b', 'rtp_c', 'tri_a', 'tri_b', 'tri_c'] expected4 = ['rtp_b', 'orlando' ] + expected5 = ['blade-a-1'] inventory = self.complex_inventory() hosts = inventory.list_hosts("nc[1]") @@ -264,6 +280,8 @@ class TestInventory(unittest.TestCase): self.compare(hosts, expected3, sort=False) hosts = inventory.list_hosts("nc[1-2]:florida[0-1]") self.compare(hosts, expected4, sort=False) + hosts = inventory.list_hosts("blade-a-1") + self.compare(hosts, expected5, sort=False) def test_complex_intersect(self): inventory = self.complex_inventory() diff --git a/test/complex_hosts b/test/complex_hosts index 0b5ce8c19c..d7f172f203 100644 --- a/test/complex_hosts +++ b/test/complex_hosts @@ -87,3 +87,9 @@ host[2:3] [role3] host[1:3:2] + +[role4] +blade-[a:c]-[1:16] +blade-[d:z]-[01:16].example.com +blade-[1:10]-[1:16] +host-e-[10:16].example.net:1234 From ed858f9f7204716e033aaac5f409679779a8d076 Mon Sep 17 00:00:00 2001 From: John Barker Date: Fri, 20 Dec 2013 20:27:24 +0000 Subject: [PATCH 016/182] Display context appropriate help and inform the user they can do '--help --- bin/ansible-galaxy | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/bin/ansible-galaxy b/bin/ansible-galaxy index bad7df0276..f07c42aa7d 100755 --- a/bin/ansible-galaxy +++ b/bin/ansible-galaxy @@ -103,8 +103,8 @@ def build_option_parser(action): the user wants to execute. """ - parser = OptionParser() - parser.set_usage("usage: %%prog [%s] [options] ..." % "|".join(VALID_ACTIONS)) + parser = OptionParser(epilog = "See '%s --help ' for more information on a specific command." % os.path.basename(sys.argv[0])) + parser.set_usage("usage: %%prog [--help] [%s] [options] ..." % "|".join(VALID_ACTIONS)) if not action: parser.print_help() @@ -206,6 +206,7 @@ def api_lookup_role_by_name(api_server, role_name): try: user_name,role_name = role_name.split(".", 1) except: + parser.print_help() print "Invalid role name (%s). You must specify username.rolename" % role_name sys.exit(1) @@ -444,7 +445,7 @@ def install_role(role_name, role_version, role_filename, options): # Action functions #------------------------------------------------------------------------------------- -def execute_init(args, options): +def execute_init(args, options, parser): """ Executes the init action, which creates the skeleton framework of a role that complies with the galaxy metadata format. @@ -476,6 +477,7 @@ def execute_init(args, options): "been modified there already." sys.exit(1) except Exception, e: + parser.print_help() print "No role name specified for init" sys.exit(1) @@ -527,7 +529,7 @@ def execute_init(args, options): f.close() print "%s was created successfully" % role_name -def execute_info(args, options): +def execute_info(args, options, parser): """ Executes the info action. This action prints out detailed information about an installed role as well as info available @@ -536,7 +538,7 @@ def execute_info(args, options): pass -def execute_install(args, options): +def execute_install(args, options, parser): """ Executes the installation action. The args list contains the roles to be installed, unless -f was specified. The list of roles @@ -551,11 +553,13 @@ def execute_install(args, options): if len(args) == 0 and not role_file: # the user needs to specify one of either --role-file # or specify a single user/role name + parser.print_help() print "You must specify a user/role name or a roles file" sys.exit() elif len(args) == 1 and role_file: # using a role file is mutually exclusive of specifying # the role name on the command line + parser.print_help() print "Please specify a user/role name, or a roles file, but not both" sys.exit(1) @@ -648,13 +652,14 @@ def execute_install(args, options): exit_without_ignore(options) sys.exit(0) -def execute_remove(args, options): +def execute_remove(args, options, parser): """ Executes the remove action. The args list contains the list of roles to be removed. This list can contain more than one role. """ if len(args) == 0: + parser.print_help() print 'You must specify at least one role to remove.' sys.exit() @@ -668,7 +673,7 @@ def execute_remove(args, options): print '%s is not installed, skipping.' % role sys.exit(0) -def execute_list(args, options): +def execute_list(args, options, parser): """ Executes the list action. The args list can contain zero or one role. If one is specified, only that role will be @@ -700,10 +705,12 @@ def execute_list(args, options): roles_path = get_opt(options, 'roles_path') roles_path = os.path.expanduser(roles_path) if not os.path.exists(roles_path): + parser.print_help() print "The path %s does not exist. Please specify a valid path with --roles-path" % roles_path sys.exit(1) elif not os.path.isdir(roles_path): print "%s exists, but it is not a directory. Please specify a valid path with --roles-path" % roles_path + parser.print_help() sys.exit(1) path_files = os.listdir(roles_path) for path_file in path_files: @@ -730,7 +737,7 @@ def main(): # execute the desired action if 1: #try: fn = globals()["execute_%s" % action] - fn(args, options) + fn(args, options, parser) #except KeyError, e: # print "Error: %s is not a valid action. Valid actions are: %s" % (action, ", ".join(VALID_ACTIONS)) # sys.exit(1) From d607f2ccf5ebf2bac9bf4387897ab749029a3ba6 Mon Sep 17 00:00:00 2001 From: John Barker Date: Fri, 20 Dec 2013 21:08:14 +0000 Subject: [PATCH 017/182] Add some blank lines in help to add readability --- bin/ansible-galaxy | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/ansible-galaxy b/bin/ansible-galaxy index f07c42aa7d..4038de6c98 100755 --- a/bin/ansible-galaxy +++ b/bin/ansible-galaxy @@ -103,8 +103,10 @@ def build_option_parser(action): the user wants to execute. """ - parser = OptionParser(epilog = "See '%s --help ' for more information on a specific command." % os.path.basename(sys.argv[0])) - parser.set_usage("usage: %%prog [--help] [%s] [options] ..." % "|".join(VALID_ACTIONS)) + usage = "usage: %%prog [%s] [--help] [options] ..." % "|".join(VALID_ACTIONS) + epilog = "\nSee '%s --help' for more information on a specific command.\n\n" % os.path.basename(sys.argv[0]) + OptionParser.format_epilog = lambda self, formatter: self.epilog + parser = OptionParser(usage=usage, epilog=epilog) if not action: parser.print_help() From fd037f59605ca56425df423784647632d87d115e Mon Sep 17 00:00:00 2001 From: Janitha Karunaratne Date: Fri, 20 Dec 2013 16:44:18 -0600 Subject: [PATCH 018/182] Fixes #5381 easy_install bug when used with virtualenv --- library/packaging/easy_install | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/packaging/easy_install b/library/packaging/easy_install index eae361c432..bdacf8e464 100644 --- a/library/packaging/easy_install +++ b/library/packaging/easy_install @@ -135,7 +135,6 @@ def main(): name = module.params['name'] env = module.params['virtualenv'] executable = module.params['executable'] - easy_install = _get_easy_install(module, env, executable) site_packages = module.params['virtualenv_site_packages'] virtualenv_command = module.params['virtualenv_command'] @@ -159,6 +158,8 @@ def main(): out += out_venv err += err_venv + easy_install = _get_easy_install(module, env, executable) + cmd = None changed = False installed = _is_package_installed(module, name, easy_install) From a52ca73167abf4e5687f19a704ad7c7a90d1404a Mon Sep 17 00:00:00 2001 From: "Jasper N. Brouwer" Date: Mon, 23 Dec 2013 09:54:16 +0100 Subject: [PATCH 019/182] Swapped conditions of the changed if statements --- library/system/service | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/system/service b/library/system/service index 2490c6a30f..ed5712c09f 100644 --- a/library/system/service +++ b/library/system/service @@ -431,10 +431,10 @@ class LinuxService(Service): if check_systemd(self.name): # service is managed by systemd self.enable_cmd = location['systemctl'] - elif os.path.exists("/etc/init/%s.conf" % self.name) and location['initctl']: + elif location['initctl'] and os.path.exists("/etc/init/%s.conf" % self.name): # service is managed by upstart self.enable_cmd = location['initctl'] - elif os.path.exists("/etc/init.d/%s" % self.name) and location['update-rc.d']: + elif location['update-rc.d'] and os.path.exists("/etc/init.d/%s" % self.name): # service is managed by with SysV init scripts, but with update-rc.d self.enable_cmd = location['update-rc.d'] else: From c328cdbb5320756be6c5300579aae14c4177846b Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Mon, 23 Dec 2013 13:56:48 -0600 Subject: [PATCH 020/182] Missed prefixed variable attribute --- docsite/rst/guide_rax.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docsite/rst/guide_rax.rst b/docsite/rst/guide_rax.rst index a4e984c60b..40722c2ef3 100644 --- a/docsite/rst/guide_rax.rst +++ b/docsite/rst/guide_rax.rst @@ -542,7 +542,7 @@ Build a complete webserver environment with servers, custom networks and load ba module: rax_clb_nodes credentials: ~/.raxpub load_balancer_id: "{{ clb.balancer.id }}" - address: "{{ item.networks.private|first }}" + address: "{{ item.rax_networks.private|first }}" port: 80 condition: enabled type: primary From 384cac77791954f1db6c173093c5c60fe12693a8 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Tue, 24 Dec 2013 14:10:08 -0500 Subject: [PATCH 021/182] Use a docsite theme that is in source control, work in progress. --- docsite/Makefile | 2 + docsite/_static/solar.css | 344 ++++++++++++++++++ docsite/_static/solarized-dark.css | 84 +++++ docsite/_static/subtle_dots.png | Bin 0 -> 1025 bytes docsite/_themes/solar/NEWS.txt | 32 ++ docsite/_themes/solar/README.rst | 28 ++ docsite/_themes/solar/layout.html | 32 ++ docsite/_themes/solar/static/solar.css | 344 ++++++++++++++++++ .../_themes/solar/static/solarized-dark.css | 84 +++++ docsite/_themes/solar/static/subtle_dots.png | Bin 0 -> 1025 bytes docsite/_themes/solar/theme.conf | 4 + docsite/conf.py | 8 +- docsite/rst/modules.rst | 3 - 13 files changed, 958 insertions(+), 7 deletions(-) create mode 100644 docsite/_static/solar.css create mode 100644 docsite/_static/solarized-dark.css create mode 100644 docsite/_static/subtle_dots.png create mode 100644 docsite/_themes/solar/NEWS.txt create mode 100644 docsite/_themes/solar/README.rst create mode 100644 docsite/_themes/solar/layout.html create mode 100644 docsite/_themes/solar/static/solar.css create mode 100644 docsite/_themes/solar/static/solarized-dark.css create mode 100644 docsite/_themes/solar/static/subtle_dots.png create mode 100644 docsite/_themes/solar/theme.conf diff --git a/docsite/Makefile b/docsite/Makefile index 56b84fbc88..173de31756 100644 --- a/docsite/Makefile +++ b/docsite/Makefile @@ -6,6 +6,8 @@ all: clean docs docs: clean modules ./build-site.py + cp _static/* htmlout/_static/ + cp _themes/solar/static/* htmlout/_static/ viewdocs: clean ./build-site.py view diff --git a/docsite/_static/solar.css b/docsite/_static/solar.css new file mode 100644 index 0000000000..15b5adef71 --- /dev/null +++ b/docsite/_static/solar.css @@ -0,0 +1,344 @@ +/* solar.css + * Modified from sphinxdoc.css of the sphinxdoc theme. +*/ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: 'Open Sans', sans-serif; + font-size: 14px; + line-height: 150%; + text-align: center; + color: #002b36; + padding: 0; + margin: 0px 80px 0px 80px; + min-width: 740px; + -moz-box-shadow: 0px 0px 10px #93a1a1; + -webkit-box-shadow: 0px 0px 10px #93a1a1; + box-shadow: 0px 0px 10px #93a1a1; + background: url("subtle_dots.png") repeat; + +} + +div.document { + background-color: #fcfcfc; + text-align: left; + background-repeat: repeat-x; +} + +div.bodywrapper { + margin: 0 240px 0 0; + border-right: 1px dotted #eee8d5; +} + +div.body { + background-color: white; + margin: 0; + padding: 0.5em 20px 20px 20px; +} + +div.related { + font-size: 1em; + background: #002b36; + color: #839496; + padding: 5px 0px; +} + +div.related ul { + height: 2em; + margin: 2px; +} + +div.related ul li { + margin: 0; + padding: 0; + height: 2em; + float: left; +} + +div.related ul li.right { + float: right; + margin-right: 5px; +} + +div.related ul li a { + margin: 0; + padding: 2px 5px; + line-height: 2em; + text-decoration: none; + color: #839496; +} + +div.related ul li a:hover { + background-color: #073642; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; +} + +div.sphinxsidebarwrapper { + padding: 0; +} + +div.sphinxsidebar { + margin: 0; + padding: 0.5em 15px 15px 0; + width: 210px; + float: right; + font-size: 0.9em; + text-align: left; +} + +div.sphinxsidebar h3, div.sphinxsidebar h4 { + margin: 1em 0 0.5em 0; + font-size: 1em; + padding: 0.7em; + background-color: #eeeff1; +} + +div.sphinxsidebar h3 a { + color: #2E3436; +} + +div.sphinxsidebar ul { + padding-left: 1.5em; + margin-top: 7px; + padding: 0; + line-height: 150%; + color: #586e75; +} + +div.sphinxsidebar ul ul { + margin-left: 20px; +} + +div.sphinxsidebar input { + border: 1px solid #eee8d5; +} + +div.footer { + background-color: #93a1a1; + color: #eee; + padding: 3px 8px 3px 0; + clear: both; + font-size: 0.8em; + text-align: right; +} + +div.footer a { + color: #eee; + text-decoration: none; +} + +/* -- body styles ----------------------------------------------------------- */ + +p { + margin: 0.8em 0 0.5em 0; +} + +div.body a, div.sphinxsidebarwrapper a { + color: #268bd2; + text-decoration: none; +} + +div.body a:hover, div.sphinxsidebarwrapper a:hover { + border-bottom: 1px solid #268bd2; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Open Sans", sans-serif; + font-weight: 300; +} + +h1 { + margin: 0; + padding: 0.7em 0 0.3em 0; + line-height: 1.2em; + color: #002b36; + text-shadow: #eee 0.1em 0.1em 0.1em; +} + +h2 { + margin: 1.3em 0 0.2em 0; + padding: 0 0 10px 0; + color: #073642; + border-bottom: 1px solid #eee; +} + +h3 { + margin: 1em 0 -0.3em 0; + padding-bottom: 5px; +} + +h3, h4, h5, h6 { + color: #073642; + border-bottom: 1px dotted #eee; +} + +div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a { + color: #657B83!important; +} + +h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { + display: none; + margin: 0 0 0 0.3em; + padding: 0 0.2em 0 0.2em; + color: #aaa!important; +} + +h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, +h5:hover a.anchor, h6:hover a.anchor { + display: inline; +} + +h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, +h5 a.anchor:hover, h6 a.anchor:hover { + color: #777; + background-color: #eee; +} + +a.headerlink { + color: #c60f0f!important; + font-size: 1em; + margin-left: 6px; + padding: 0 4px 0 4px; + text-decoration: none!important; +} + +a.headerlink:hover { + background-color: #ccc; + color: white!important; +} + + +cite, code, tt { + font-family: 'Source Code Pro', monospace; + font-size: 0.9em; + letter-spacing: 0.01em; + background-color: #eeeff2; + font-style: normal; +} + +hr { + border: 1px solid #eee; + margin: 2em; +} + +.highlight { + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; +} + +pre { + font-family: 'Source Code Pro', monospace; + font-style: normal; + font-size: 0.9em; + letter-spacing: 0.015em; + line-height: 120%; + padding: 0.7em; + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} + +pre a { + color: inherit; + text-decoration: underline; +} + +td.linenos pre { + padding: 0.5em 0; +} + +div.quotebar { + background-color: #f8f8f8; + max-width: 250px; + float: right; + padding: 2px 7px; + border: 1px solid #ccc; +} + +div.topic { + background-color: #f8f8f8; +} + +table { + border-collapse: collapse; + margin: 0 -0.5em 0 -0.5em; +} + +table td, table th { + padding: 0.2em 0.5em 0.2em 0.5em; +} + +div.admonition { + font-size: 0.9em; + margin: 1em 0 1em 0; + border: 1px solid #eee; + background-color: #f7f7f7; + padding: 0; + -moz-box-shadow: 0px 8px 6px -8px #93a1a1; + -webkit-box-shadow: 0px 8px 6px -8px #93a1a1; + box-shadow: 0px 8px 6px -8px #93a1a1; +} + +div.admonition p { + margin: 0.5em 1em 0.5em 1em; + padding: 0.2em; +} + +div.admonition pre { + margin: 0.4em 1em 0.4em 1em; +} + +div.admonition p.admonition-title +{ + margin: 0; + padding: 0.2em 0 0.2em 0.6em; + color: white; + border-bottom: 1px solid #eee8d5; + font-weight: bold; + background-color: #268bd2; +} + +div.warning p.admonition-title, +div.important p.admonition-title { + background-color: #cb4b16; +} + +div.hint p.admonition-title, +div.tip p.admonition-title { + background-color: #859900; +} + +div.caution p.admonition-title, +div.attention p.admonition-title, +div.danger p.admonition-title, +div.error p.admonition-title { + background-color: #dc322f; +} + +div.admonition ul, div.admonition ol { + margin: 0.1em 0.5em 0.5em 3em; + padding: 0; +} + +div.versioninfo { + margin: 1em 0 0 0; + border: 1px solid #eee; + background-color: #DDEAF0; + padding: 8px; + line-height: 1.3em; + font-size: 0.9em; +} + +div.viewcode-block:target { + background-color: #f4debf; + border-top: 1px solid #eee; + border-bottom: 1px solid #eee; +} diff --git a/docsite/_static/solarized-dark.css b/docsite/_static/solarized-dark.css new file mode 100644 index 0000000000..6ebb945ce0 --- /dev/null +++ b/docsite/_static/solarized-dark.css @@ -0,0 +1,84 @@ +/* solarized dark style for solar theme */ + +/*style pre scrollbar*/ +pre::-webkit-scrollbar, .highlight::-webkit-scrollbar { + height: 0.5em; + background: #073642; +} + +pre::-webkit-scrollbar-thumb { + border-radius: 1em; + background: #93a1a1; +} + +/* pygments style */ +.highlight .hll { background-color: #ffffcc } +.highlight { background: #002B36!important; color: #93A1A1 } +.highlight .c { color: #586E75 } /* Comment */ +.highlight .err { color: #93A1A1 } /* Error */ +.highlight .g { color: #93A1A1 } /* Generic */ +.highlight .k { color: #859900 } /* Keyword */ +.highlight .l { color: #93A1A1 } /* Literal */ +.highlight .n { color: #93A1A1 } /* Name */ +.highlight .o { color: #859900 } /* Operator */ +.highlight .x { color: #CB4B16 } /* Other */ +.highlight .p { color: #93A1A1 } /* Punctuation */ +.highlight .cm { color: #586E75 } /* Comment.Multiline */ +.highlight .cp { color: #859900 } /* Comment.Preproc */ +.highlight .c1 { color: #586E75 } /* Comment.Single */ +.highlight .cs { color: #859900 } /* Comment.Special */ +.highlight .gd { color: #2AA198 } /* Generic.Deleted */ +.highlight .ge { color: #93A1A1; font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #DC322F } /* Generic.Error */ +.highlight .gh { color: #CB4B16 } /* Generic.Heading */ +.highlight .gi { color: #859900 } /* Generic.Inserted */ +.highlight .go { color: #93A1A1 } /* Generic.Output */ +.highlight .gp { color: #93A1A1 } /* Generic.Prompt */ +.highlight .gs { color: #93A1A1; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #CB4B16 } /* Generic.Subheading */ +.highlight .gt { color: #93A1A1 } /* Generic.Traceback */ +.highlight .kc { color: #CB4B16 } /* Keyword.Constant */ +.highlight .kd { color: #268BD2 } /* Keyword.Declaration */ +.highlight .kn { color: #859900 } /* Keyword.Namespace */ +.highlight .kp { color: #859900 } /* Keyword.Pseudo */ +.highlight .kr { color: #268BD2 } /* Keyword.Reserved */ +.highlight .kt { color: #DC322F } /* Keyword.Type */ +.highlight .ld { color: #93A1A1 } /* Literal.Date */ +.highlight .m { color: #2AA198 } /* Literal.Number */ +.highlight .s { color: #2AA198 } /* Literal.String */ +.highlight .na { color: #93A1A1 } /* Name.Attribute */ +.highlight .nb { color: #B58900 } /* Name.Builtin */ +.highlight .nc { color: #268BD2 } /* Name.Class */ +.highlight .no { color: #CB4B16 } /* Name.Constant */ +.highlight .nd { color: #268BD2 } /* Name.Decorator */ +.highlight .ni { color: #CB4B16 } /* Name.Entity */ +.highlight .ne { color: #CB4B16 } /* Name.Exception */ +.highlight .nf { color: #268BD2 } /* Name.Function */ +.highlight .nl { color: #93A1A1 } /* Name.Label */ +.highlight .nn { color: #93A1A1 } /* Name.Namespace */ +.highlight .nx { color: #93A1A1 } /* Name.Other */ +.highlight .py { color: #93A1A1 } /* Name.Property */ +.highlight .nt { color: #268BD2 } /* Name.Tag */ +.highlight .nv { color: #268BD2 } /* Name.Variable */ +.highlight .ow { color: #859900 } /* Operator.Word */ +.highlight .w { color: #93A1A1 } /* Text.Whitespace */ +.highlight .mf { color: #2AA198 } /* Literal.Number.Float */ +.highlight .mh { color: #2AA198 } /* Literal.Number.Hex */ +.highlight .mi { color: #2AA198 } /* Literal.Number.Integer */ +.highlight .mo { color: #2AA198 } /* Literal.Number.Oct */ +.highlight .sb { color: #586E75 } /* Literal.String.Backtick */ +.highlight .sc { color: #2AA198 } /* Literal.String.Char */ +.highlight .sd { color: #93A1A1 } /* Literal.String.Doc */ +.highlight .s2 { color: #2AA198 } /* Literal.String.Double */ +.highlight .se { color: #CB4B16 } /* Literal.String.Escape */ +.highlight .sh { color: #93A1A1 } /* Literal.String.Heredoc */ +.highlight .si { color: #2AA198 } /* Literal.String.Interpol */ +.highlight .sx { color: #2AA198 } /* Literal.String.Other */ +.highlight .sr { color: #DC322F } /* Literal.String.Regex */ +.highlight .s1 { color: #2AA198 } /* Literal.String.Single */ +.highlight .ss { color: #2AA198 } /* Literal.String.Symbol */ +.highlight .bp { color: #268BD2 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #268BD2 } /* Name.Variable.Class */ +.highlight .vg { color: #268BD2 } /* Name.Variable.Global */ +.highlight .vi { color: #268BD2 } /* Name.Variable.Instance */ +.highlight .il { color: #2AA198 } /* Literal.Number.Integer.Long */ diff --git a/docsite/_static/subtle_dots.png b/docsite/_static/subtle_dots.png new file mode 100644 index 0000000000000000000000000000000000000000..bb2d6117e5e207e79aa312e26cb3d7ff0dcfdb04 GIT binary patch literal 1025 zcmaJ=&ui0A9DiMRn4@EZiMV<2k$DoEUu(N2wAQw1ZDARu>##SMCa-JQk{6TLH5-Ud zgt41~7mwam^d`zwh8GVWJScO2fH(0Vc+iV4UE6uE1|E6eFQ3on`}6Hpmaa~W&yE8C zCdwtHO8@8RnH(DdAoT6$^b9p-NTE)aag%g37lEvS8wi#yZ4FhCW^CPfk5T}H8M9U= zb#+nJvBhcuhV?CnvH?hCd`HvQ5rGY~X4+}y`>Qt$G>tTKEw1valSeJHwC$qh?WLN& zy{=0Jler92zDxxyL^SAI8@4C=X{M_y(|h3N7}$l7^)z#wR9&sWJa!R`vwT$N69P=c zSRuwsG3gQ%d4cByk>eMl0x!oGWL|*1kD<|AqbXOFVlNi8(oBmGN9MRrr^9yUS?sQH zf+R^CFLI(7rHH7vWfRSh+TL7WK|!AGnhr6s4Fg53f!id_&`gg@u$+Oc?e*G38^-yX z!wD=Ol+*{R>i?mZH9&i$ica$Vr?6Msau8QV9&Woj9b9uR2<6Cm7ik2$HH5*;WCgkACNhdHwqUnp*&LpWFjTL{HHPOR z4&`z*87>&?UxV(u==KEH!P-(|u=vQP+wRh}KA+mzqfeq;Ri8=5AfL KCFN;u_0At+f;@fz literal 0 HcmV?d00001 diff --git a/docsite/_themes/solar/NEWS.txt b/docsite/_themes/solar/NEWS.txt new file mode 100644 index 0000000000..d9743ee67f --- /dev/null +++ b/docsite/_themes/solar/NEWS.txt @@ -0,0 +1,32 @@ +News +==== + +1.3 +--- +* Release date: 2012-11-01. +* Source Code Pro is now used for code samples. +* Reduced font size of pre elements. +* Horizontal rule for header elements. +* HTML pre contents are now wrapped (no scrollbars). +* Changed permalink color from black to a lighter one. + +1.2 +--- +* Release date: 2012-10-03. +* Style additional admonition levels. +* Increase padding for navigation links (minor). +* Add shadow for admonition items (minor). + +1.1 +--- +* Release date: 2012-09-05. +* Add a new background. +* Revert font of headings to Open Sans Light. +* Darker color for h3 - h6. +* Removed dependency on solarized dark pygments style. +* Nice looking scrollbars for pre element. + +1.0 +--- +* Release date: 2012-08-24. +* Initial release. diff --git a/docsite/_themes/solar/README.rst b/docsite/_themes/solar/README.rst new file mode 100644 index 0000000000..caeedbd29d --- /dev/null +++ b/docsite/_themes/solar/README.rst @@ -0,0 +1,28 @@ +Solar theme for Python Sphinx +============================= +Solar is an attempt to create a theme for Sphinx based on the `Solarized `_ color scheme. + +Preview +------- +http://vimalkumar.in/sphinx-themes/solar + +Download +-------- +Released versions are available from http://github.com/vkvn/sphinx-themes/downloads + +Installation +------------ +#. Extract the archive. +#. Modify ``conf.py`` of an existing Sphinx project or create new project using ``sphinx-quickstart``. +#. Change the ``html_theme`` parameter to ``solar``. +#. Change the ``html_theme_path`` to the location containing the extracted archive. + +License +------- +`GNU General Public License `_. + +Credits +------- +Modified from the default Sphinx theme -- Sphinxdoc + +Background pattern from http://subtlepatterns.com. diff --git a/docsite/_themes/solar/layout.html b/docsite/_themes/solar/layout.html new file mode 100644 index 0000000000..6c57110d41 --- /dev/null +++ b/docsite/_themes/solar/layout.html @@ -0,0 +1,32 @@ +{% extends "basic/layout.html" %} + +{%- block doctype -%} + +{%- endblock -%} + +{%- block extrahead -%} + + +{%- endblock -%} + +{# put the sidebar before the body #} +{% block sidebar1 %}{{ sidebar() }}{% endblock %} +{% block sidebar2 %}{% endblock %} + +{%- block footer %} + +{%- endblock %} diff --git a/docsite/_themes/solar/static/solar.css b/docsite/_themes/solar/static/solar.css new file mode 100644 index 0000000000..15b5adef71 --- /dev/null +++ b/docsite/_themes/solar/static/solar.css @@ -0,0 +1,344 @@ +/* solar.css + * Modified from sphinxdoc.css of the sphinxdoc theme. +*/ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: 'Open Sans', sans-serif; + font-size: 14px; + line-height: 150%; + text-align: center; + color: #002b36; + padding: 0; + margin: 0px 80px 0px 80px; + min-width: 740px; + -moz-box-shadow: 0px 0px 10px #93a1a1; + -webkit-box-shadow: 0px 0px 10px #93a1a1; + box-shadow: 0px 0px 10px #93a1a1; + background: url("subtle_dots.png") repeat; + +} + +div.document { + background-color: #fcfcfc; + text-align: left; + background-repeat: repeat-x; +} + +div.bodywrapper { + margin: 0 240px 0 0; + border-right: 1px dotted #eee8d5; +} + +div.body { + background-color: white; + margin: 0; + padding: 0.5em 20px 20px 20px; +} + +div.related { + font-size: 1em; + background: #002b36; + color: #839496; + padding: 5px 0px; +} + +div.related ul { + height: 2em; + margin: 2px; +} + +div.related ul li { + margin: 0; + padding: 0; + height: 2em; + float: left; +} + +div.related ul li.right { + float: right; + margin-right: 5px; +} + +div.related ul li a { + margin: 0; + padding: 2px 5px; + line-height: 2em; + text-decoration: none; + color: #839496; +} + +div.related ul li a:hover { + background-color: #073642; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; +} + +div.sphinxsidebarwrapper { + padding: 0; +} + +div.sphinxsidebar { + margin: 0; + padding: 0.5em 15px 15px 0; + width: 210px; + float: right; + font-size: 0.9em; + text-align: left; +} + +div.sphinxsidebar h3, div.sphinxsidebar h4 { + margin: 1em 0 0.5em 0; + font-size: 1em; + padding: 0.7em; + background-color: #eeeff1; +} + +div.sphinxsidebar h3 a { + color: #2E3436; +} + +div.sphinxsidebar ul { + padding-left: 1.5em; + margin-top: 7px; + padding: 0; + line-height: 150%; + color: #586e75; +} + +div.sphinxsidebar ul ul { + margin-left: 20px; +} + +div.sphinxsidebar input { + border: 1px solid #eee8d5; +} + +div.footer { + background-color: #93a1a1; + color: #eee; + padding: 3px 8px 3px 0; + clear: both; + font-size: 0.8em; + text-align: right; +} + +div.footer a { + color: #eee; + text-decoration: none; +} + +/* -- body styles ----------------------------------------------------------- */ + +p { + margin: 0.8em 0 0.5em 0; +} + +div.body a, div.sphinxsidebarwrapper a { + color: #268bd2; + text-decoration: none; +} + +div.body a:hover, div.sphinxsidebarwrapper a:hover { + border-bottom: 1px solid #268bd2; +} + +h1, h2, h3, h4, h5, h6 { + font-family: "Open Sans", sans-serif; + font-weight: 300; +} + +h1 { + margin: 0; + padding: 0.7em 0 0.3em 0; + line-height: 1.2em; + color: #002b36; + text-shadow: #eee 0.1em 0.1em 0.1em; +} + +h2 { + margin: 1.3em 0 0.2em 0; + padding: 0 0 10px 0; + color: #073642; + border-bottom: 1px solid #eee; +} + +h3 { + margin: 1em 0 -0.3em 0; + padding-bottom: 5px; +} + +h3, h4, h5, h6 { + color: #073642; + border-bottom: 1px dotted #eee; +} + +div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a { + color: #657B83!important; +} + +h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { + display: none; + margin: 0 0 0 0.3em; + padding: 0 0.2em 0 0.2em; + color: #aaa!important; +} + +h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, +h5:hover a.anchor, h6:hover a.anchor { + display: inline; +} + +h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, +h5 a.anchor:hover, h6 a.anchor:hover { + color: #777; + background-color: #eee; +} + +a.headerlink { + color: #c60f0f!important; + font-size: 1em; + margin-left: 6px; + padding: 0 4px 0 4px; + text-decoration: none!important; +} + +a.headerlink:hover { + background-color: #ccc; + color: white!important; +} + + +cite, code, tt { + font-family: 'Source Code Pro', monospace; + font-size: 0.9em; + letter-spacing: 0.01em; + background-color: #eeeff2; + font-style: normal; +} + +hr { + border: 1px solid #eee; + margin: 2em; +} + +.highlight { + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; +} + +pre { + font-family: 'Source Code Pro', monospace; + font-style: normal; + font-size: 0.9em; + letter-spacing: 0.015em; + line-height: 120%; + padding: 0.7em; + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} + +pre a { + color: inherit; + text-decoration: underline; +} + +td.linenos pre { + padding: 0.5em 0; +} + +div.quotebar { + background-color: #f8f8f8; + max-width: 250px; + float: right; + padding: 2px 7px; + border: 1px solid #ccc; +} + +div.topic { + background-color: #f8f8f8; +} + +table { + border-collapse: collapse; + margin: 0 -0.5em 0 -0.5em; +} + +table td, table th { + padding: 0.2em 0.5em 0.2em 0.5em; +} + +div.admonition { + font-size: 0.9em; + margin: 1em 0 1em 0; + border: 1px solid #eee; + background-color: #f7f7f7; + padding: 0; + -moz-box-shadow: 0px 8px 6px -8px #93a1a1; + -webkit-box-shadow: 0px 8px 6px -8px #93a1a1; + box-shadow: 0px 8px 6px -8px #93a1a1; +} + +div.admonition p { + margin: 0.5em 1em 0.5em 1em; + padding: 0.2em; +} + +div.admonition pre { + margin: 0.4em 1em 0.4em 1em; +} + +div.admonition p.admonition-title +{ + margin: 0; + padding: 0.2em 0 0.2em 0.6em; + color: white; + border-bottom: 1px solid #eee8d5; + font-weight: bold; + background-color: #268bd2; +} + +div.warning p.admonition-title, +div.important p.admonition-title { + background-color: #cb4b16; +} + +div.hint p.admonition-title, +div.tip p.admonition-title { + background-color: #859900; +} + +div.caution p.admonition-title, +div.attention p.admonition-title, +div.danger p.admonition-title, +div.error p.admonition-title { + background-color: #dc322f; +} + +div.admonition ul, div.admonition ol { + margin: 0.1em 0.5em 0.5em 3em; + padding: 0; +} + +div.versioninfo { + margin: 1em 0 0 0; + border: 1px solid #eee; + background-color: #DDEAF0; + padding: 8px; + line-height: 1.3em; + font-size: 0.9em; +} + +div.viewcode-block:target { + background-color: #f4debf; + border-top: 1px solid #eee; + border-bottom: 1px solid #eee; +} diff --git a/docsite/_themes/solar/static/solarized-dark.css b/docsite/_themes/solar/static/solarized-dark.css new file mode 100644 index 0000000000..6ebb945ce0 --- /dev/null +++ b/docsite/_themes/solar/static/solarized-dark.css @@ -0,0 +1,84 @@ +/* solarized dark style for solar theme */ + +/*style pre scrollbar*/ +pre::-webkit-scrollbar, .highlight::-webkit-scrollbar { + height: 0.5em; + background: #073642; +} + +pre::-webkit-scrollbar-thumb { + border-radius: 1em; + background: #93a1a1; +} + +/* pygments style */ +.highlight .hll { background-color: #ffffcc } +.highlight { background: #002B36!important; color: #93A1A1 } +.highlight .c { color: #586E75 } /* Comment */ +.highlight .err { color: #93A1A1 } /* Error */ +.highlight .g { color: #93A1A1 } /* Generic */ +.highlight .k { color: #859900 } /* Keyword */ +.highlight .l { color: #93A1A1 } /* Literal */ +.highlight .n { color: #93A1A1 } /* Name */ +.highlight .o { color: #859900 } /* Operator */ +.highlight .x { color: #CB4B16 } /* Other */ +.highlight .p { color: #93A1A1 } /* Punctuation */ +.highlight .cm { color: #586E75 } /* Comment.Multiline */ +.highlight .cp { color: #859900 } /* Comment.Preproc */ +.highlight .c1 { color: #586E75 } /* Comment.Single */ +.highlight .cs { color: #859900 } /* Comment.Special */ +.highlight .gd { color: #2AA198 } /* Generic.Deleted */ +.highlight .ge { color: #93A1A1; font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #DC322F } /* Generic.Error */ +.highlight .gh { color: #CB4B16 } /* Generic.Heading */ +.highlight .gi { color: #859900 } /* Generic.Inserted */ +.highlight .go { color: #93A1A1 } /* Generic.Output */ +.highlight .gp { color: #93A1A1 } /* Generic.Prompt */ +.highlight .gs { color: #93A1A1; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #CB4B16 } /* Generic.Subheading */ +.highlight .gt { color: #93A1A1 } /* Generic.Traceback */ +.highlight .kc { color: #CB4B16 } /* Keyword.Constant */ +.highlight .kd { color: #268BD2 } /* Keyword.Declaration */ +.highlight .kn { color: #859900 } /* Keyword.Namespace */ +.highlight .kp { color: #859900 } /* Keyword.Pseudo */ +.highlight .kr { color: #268BD2 } /* Keyword.Reserved */ +.highlight .kt { color: #DC322F } /* Keyword.Type */ +.highlight .ld { color: #93A1A1 } /* Literal.Date */ +.highlight .m { color: #2AA198 } /* Literal.Number */ +.highlight .s { color: #2AA198 } /* Literal.String */ +.highlight .na { color: #93A1A1 } /* Name.Attribute */ +.highlight .nb { color: #B58900 } /* Name.Builtin */ +.highlight .nc { color: #268BD2 } /* Name.Class */ +.highlight .no { color: #CB4B16 } /* Name.Constant */ +.highlight .nd { color: #268BD2 } /* Name.Decorator */ +.highlight .ni { color: #CB4B16 } /* Name.Entity */ +.highlight .ne { color: #CB4B16 } /* Name.Exception */ +.highlight .nf { color: #268BD2 } /* Name.Function */ +.highlight .nl { color: #93A1A1 } /* Name.Label */ +.highlight .nn { color: #93A1A1 } /* Name.Namespace */ +.highlight .nx { color: #93A1A1 } /* Name.Other */ +.highlight .py { color: #93A1A1 } /* Name.Property */ +.highlight .nt { color: #268BD2 } /* Name.Tag */ +.highlight .nv { color: #268BD2 } /* Name.Variable */ +.highlight .ow { color: #859900 } /* Operator.Word */ +.highlight .w { color: #93A1A1 } /* Text.Whitespace */ +.highlight .mf { color: #2AA198 } /* Literal.Number.Float */ +.highlight .mh { color: #2AA198 } /* Literal.Number.Hex */ +.highlight .mi { color: #2AA198 } /* Literal.Number.Integer */ +.highlight .mo { color: #2AA198 } /* Literal.Number.Oct */ +.highlight .sb { color: #586E75 } /* Literal.String.Backtick */ +.highlight .sc { color: #2AA198 } /* Literal.String.Char */ +.highlight .sd { color: #93A1A1 } /* Literal.String.Doc */ +.highlight .s2 { color: #2AA198 } /* Literal.String.Double */ +.highlight .se { color: #CB4B16 } /* Literal.String.Escape */ +.highlight .sh { color: #93A1A1 } /* Literal.String.Heredoc */ +.highlight .si { color: #2AA198 } /* Literal.String.Interpol */ +.highlight .sx { color: #2AA198 } /* Literal.String.Other */ +.highlight .sr { color: #DC322F } /* Literal.String.Regex */ +.highlight .s1 { color: #2AA198 } /* Literal.String.Single */ +.highlight .ss { color: #2AA198 } /* Literal.String.Symbol */ +.highlight .bp { color: #268BD2 } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #268BD2 } /* Name.Variable.Class */ +.highlight .vg { color: #268BD2 } /* Name.Variable.Global */ +.highlight .vi { color: #268BD2 } /* Name.Variable.Instance */ +.highlight .il { color: #2AA198 } /* Literal.Number.Integer.Long */ diff --git a/docsite/_themes/solar/static/subtle_dots.png b/docsite/_themes/solar/static/subtle_dots.png new file mode 100644 index 0000000000000000000000000000000000000000..bb2d6117e5e207e79aa312e26cb3d7ff0dcfdb04 GIT binary patch literal 1025 zcmaJ=&ui0A9DiMRn4@EZiMV<2k$DoEUu(N2wAQw1ZDARu>##SMCa-JQk{6TLH5-Ud zgt41~7mwam^d`zwh8GVWJScO2fH(0Vc+iV4UE6uE1|E6eFQ3on`}6Hpmaa~W&yE8C zCdwtHO8@8RnH(DdAoT6$^b9p-NTE)aag%g37lEvS8wi#yZ4FhCW^CPfk5T}H8M9U= zb#+nJvBhcuhV?CnvH?hCd`HvQ5rGY~X4+}y`>Qt$G>tTKEw1valSeJHwC$qh?WLN& zy{=0Jler92zDxxyL^SAI8@4C=X{M_y(|h3N7}$l7^)z#wR9&sWJa!R`vwT$N69P=c zSRuwsG3gQ%d4cByk>eMl0x!oGWL|*1kD<|AqbXOFVlNi8(oBmGN9MRrr^9yUS?sQH zf+R^CFLI(7rHH7vWfRSh+TL7WK|!AGnhr6s4Fg53f!id_&`gg@u$+Oc?e*G38^-yX z!wD=Ol+*{R>i?mZH9&i$ica$Vr?6Msau8QV9&Woj9b9uR2<6Cm7ik2$HH5*;WCgkACNhdHwqUnp*&LpWFjTL{HHPOR z4&`z*87>&?UxV(u==KEH!P-(|u=vQP+wRh}KA+mzqfeq;Ri8=5AfL KCFN;u_0At+f;@fz literal 0 HcmV?d00001 diff --git a/docsite/_themes/solar/theme.conf b/docsite/_themes/solar/theme.conf new file mode 100644 index 0000000000..d8fc2f35d9 --- /dev/null +++ b/docsite/_themes/solar/theme.conf @@ -0,0 +1,4 @@ +[theme] +inherit = basic +stylesheet = solar.css +pygments_style = none diff --git a/docsite/conf.py b/docsite/conf.py index 6c6aff2d65..4b66a71382 100644 --- a/docsite/conf.py +++ b/docsite/conf.py @@ -50,7 +50,7 @@ source_suffix = '.rst' master_doc = 'index' # General substitutions. -project = 'Ansible 1.2 Documentation' +project = 'Ansible Documentation' copyright = "2013 AnsibleWorks" # The default replacements for |version| and |release|, also used in various @@ -101,17 +101,17 @@ pygments_style = 'sphinx' # ----------------------- html_theme_path = ['_themes'] -html_theme = 'aworks' +html_theme = 'solar' html_short_title = 'Ansible Documentation' # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. -html_style = 'default.css' +html_style = 'solar.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -html_title = 'Ansible 1.2 Documentation' +html_title = 'Ansible Documentation' # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None diff --git a/docsite/rst/modules.rst b/docsite/rst/modules.rst index 800348e982..ba8a7a02ac 100644 --- a/docsite/rst/modules.rst +++ b/docsite/rst/modules.rst @@ -1,9 +1,6 @@ Ansible Modules =============== -.. contents:: - :depth: 3 - .. _modules_intro: Introduction From 1696b14beb67c5108f5a495aba36e077f40bf3da Mon Sep 17 00:00:00 2001 From: Joshua Lund Date: Tue, 24 Dec 2013 15:11:24 -0700 Subject: [PATCH 022/182] Fixing a typo in the Amazon Web Services and Rackspace Cloud guides --- docsite/rst/guide_aws.rst | 2 +- docsite/rst/guide_rax.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docsite/rst/guide_aws.rst b/docsite/rst/guide_aws.rst index 0573528548..70329d1de1 100644 --- a/docsite/rst/guide_aws.rst +++ b/docsite/rst/guide_aws.rst @@ -55,7 +55,7 @@ The ec2 module provides the ability to provision instances within EC2. Typicall exporting the variable as EC2_URL=https://myhost:8773/services/Eucalyptus. This can be set using the 'environment' keyword in Ansible if you like. -Here is an example of provisioning a number of instances in ad-hoc mode mode: +Here is an example of provisioning a number of instances in ad-hoc mode: .. code-block:: bash diff --git a/docsite/rst/guide_rax.rst b/docsite/rst/guide_rax.rst index 40722c2ef3..92786a7cf7 100644 --- a/docsite/rst/guide_rax.rst +++ b/docsite/rst/guide_rax.rst @@ -100,7 +100,7 @@ provisioning task will be performed from your Ansible control server against the specifying your username and API key as environment variables or passing them as module arguments. -Here is a basic example of provisioning a instance in ad-hoc mode mode: +Here is a basic example of provisioning a instance in ad-hoc mode: .. code-block:: bash From c88c3a40c634706ad29e8a0b0f061c6d8f8843e4 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Tue, 24 Dec 2013 17:23:38 -0500 Subject: [PATCH 023/182] Use the sphinx_rtd_theme --- docsite/Makefile | 2 -- docsite/README.md | 17 ++++++++++------- docsite/conf.py | 16 ++++++++++------ 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/docsite/Makefile b/docsite/Makefile index 173de31756..56b84fbc88 100644 --- a/docsite/Makefile +++ b/docsite/Makefile @@ -6,8 +6,6 @@ all: clean docs docs: clean modules ./build-site.py - cp _static/* htmlout/_static/ - cp _themes/solar/static/* htmlout/_static/ viewdocs: clean ./build-site.py view diff --git a/docsite/README.md b/docsite/README.md index dd4f460805..bc6ee18139 100644 --- a/docsite/README.md +++ b/docsite/README.md @@ -5,12 +5,14 @@ This project hosts the source behind [ansibleworks.com/docs](http://www.ansiblew Contributions to the documentation are welcome. To make changes, submit a pull request that changes the reStructuredText files in the "rst/" directory only, and Michael can -do a docs build and push the static files. If you wish to verify output from the markup -such as link references, you may [install Sphinx] and build the documentation by running -`make viewdocs` from the `ansible/docsite` directory. To include module documentation -you'll need to run `make webdocs` at the top level of the repository. The generated -html files are in docsite/htmlout/ and really won't be formatted right until up -on ansibleworks.com. +do a docs build and push the static files. + +If you wish to verify output from the markup +such as link references, you may install sphinx and build the documentation by running +`make viewdocs` from the `ansible/docsite` directory. + +To include module documentation you'll need to run `make webdocs` at the top level of the repository. The generated +html files are in docsite/htmlout/. If you do not want to learn the reStructuredText format, you can also [file issues] about documentation problems on the Ansible GitHub project. @@ -19,7 +21,8 @@ Note that module documentation can actually be [generated from a DOCUMENTATION d in the modules directory, so corrections to modules written as such need to be made in the module source, rather than in docsite source. -[install Sphinx]: http://sphinx-doc.org/install.html +To install sphinx and the required theme, install pip and then "pip install sphinx sphinx_rtd_theme" + [file issues]: https://github.com/ansible/ansible/issues [module-docs]: http://www.ansibleworks.com/docs/developing_modules.html#documenting-your-module diff --git a/docsite/conf.py b/docsite/conf.py index 4b66a71382..5615ff135f 100644 --- a/docsite/conf.py +++ b/docsite/conf.py @@ -16,13 +16,17 @@ import sys import os +# pip install sphinx_rtd_theme +import sphinx_rtd_theme +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. #sys.path.append(os.path.abspath('some/directory')) - -sys.path.insert(0, os.path.join('ansible', 'lib')) -sys.path.append(os.path.abspath('_themes')) +# +#sys.path.insert(0, os.path.join('ansible', 'lib')) +#sys.path.append(os.path.abspath('_themes')) VERSION='0.01' AUTHOR='AnsibleWorks' @@ -100,14 +104,14 @@ pygments_style = 'sphinx' # Options for HTML output # ----------------------- -html_theme_path = ['_themes'] -html_theme = 'solar' +#html_theme_path = ['_themes'] +html_theme = 'sphinx_rtd_theme' html_short_title = 'Ansible Documentation' # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. -html_style = 'solar.css' +#html_style = 'solar.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". From be78b50ea0720508af3bca588a4db047f7aef431 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Tue, 24 Dec 2013 17:24:05 -0500 Subject: [PATCH 024/182] Remove static theme files since we're just grabbing the theme from pip. --- docsite/_themes/aworks/layout.html | 49 - docsite/_themes/aworks/relations.html | 8 - docsite/_themes/aworks/searchbox.html | 7 - docsite/_themes/aworks/sourcelink.html | 4 - docsite/_themes/aworks/theme.conf | 5 - docsite/_themes/bootstrap/globaltoc.html | 5 - docsite/_themes/bootstrap/layout.html | 155 -- docsite/_themes/bootstrap/localtoc.html | 5 - docsite/_themes/bootstrap/relations.html | 8 - docsite/_themes/bootstrap/searchbox.html | 7 - docsite/_themes/bootstrap/sourcelink.html | 4 - .../bootstrap/static/bootstrap-dropdown.js | 55 - .../bootstrap/static/bootstrap-scrollspy.js | 107 - .../bootstrap/static/bootstrap-sphinx.css_t | 24 - .../_themes/bootstrap/static/bootstrap.css | 2469 ----------------- docsite/_themes/bootstrap/theme.conf | 5 - docsite/_themes/solar/NEWS.txt | 32 - docsite/_themes/solar/README.rst | 28 - docsite/_themes/solar/layout.html | 32 - docsite/_themes/solar/static/solar.css | 344 --- .../_themes/solar/static/solarized-dark.css | 84 - docsite/_themes/solar/static/subtle_dots.png | Bin 1025 -> 0 bytes docsite/_themes/solar/theme.conf | 4 - 23 files changed, 3441 deletions(-) delete mode 100644 docsite/_themes/aworks/layout.html delete mode 100644 docsite/_themes/aworks/relations.html delete mode 100644 docsite/_themes/aworks/searchbox.html delete mode 100644 docsite/_themes/aworks/sourcelink.html delete mode 100644 docsite/_themes/aworks/theme.conf delete mode 100644 docsite/_themes/bootstrap/globaltoc.html delete mode 100644 docsite/_themes/bootstrap/layout.html delete mode 100644 docsite/_themes/bootstrap/localtoc.html delete mode 100644 docsite/_themes/bootstrap/relations.html delete mode 100644 docsite/_themes/bootstrap/searchbox.html delete mode 100644 docsite/_themes/bootstrap/sourcelink.html delete mode 100644 docsite/_themes/bootstrap/static/bootstrap-dropdown.js delete mode 100644 docsite/_themes/bootstrap/static/bootstrap-scrollspy.js delete mode 100644 docsite/_themes/bootstrap/static/bootstrap-sphinx.css_t delete mode 100644 docsite/_themes/bootstrap/static/bootstrap.css delete mode 100644 docsite/_themes/bootstrap/theme.conf delete mode 100644 docsite/_themes/solar/NEWS.txt delete mode 100644 docsite/_themes/solar/README.rst delete mode 100644 docsite/_themes/solar/layout.html delete mode 100644 docsite/_themes/solar/static/solar.css delete mode 100644 docsite/_themes/solar/static/solarized-dark.css delete mode 100644 docsite/_themes/solar/static/subtle_dots.png delete mode 100644 docsite/_themes/solar/theme.conf diff --git a/docsite/_themes/aworks/layout.html b/docsite/_themes/aworks/layout.html deleted file mode 100644 index 28068796da..0000000000 --- a/docsite/_themes/aworks/layout.html +++ /dev/null @@ -1,49 +0,0 @@ - - -
-
- -
-
- -{% macro navBar() %} - -{% endmacro %} - -{# Silence the sidebar's, relbar's #} -{% block sidebar1 %}{% endblock %} -{% block sidebar2 %}{% endblock %} -{% block relbar1 %}{% endblock %} -{% block relbar2 %}{% endblock %} - -{%- block content %} -
-
- - {% block header %}{{ navBar() }}{% endblock %} - - -
- {% block body %} {% endblock %} -
-
-
-{%- endblock %} - - diff --git a/docsite/_themes/aworks/relations.html b/docsite/_themes/aworks/relations.html deleted file mode 100644 index b3df68e3b5..0000000000 --- a/docsite/_themes/aworks/relations.html +++ /dev/null @@ -1,8 +0,0 @@ -{%- if prev %} -
  • {{ "«"|safe }} {{ prev.title }}
  • -{%- endif %} -{%- if next %} -
  • {{ next.title }} {{ "»"|safe }}
  • -{%- endif %} diff --git a/docsite/_themes/aworks/searchbox.html b/docsite/_themes/aworks/searchbox.html deleted file mode 100644 index 3063dde3d1..0000000000 --- a/docsite/_themes/aworks/searchbox.html +++ /dev/null @@ -1,7 +0,0 @@ -{%- if pagename != "search" %} -
    - - - -
    -{%- endif %} diff --git a/docsite/_themes/aworks/sourcelink.html b/docsite/_themes/aworks/sourcelink.html deleted file mode 100644 index 21ae2d8fef..0000000000 --- a/docsite/_themes/aworks/sourcelink.html +++ /dev/null @@ -1,4 +0,0 @@ -{%- if show_source and has_source and sourcename %} -
  • {{ _('Source') }}
  • -{%- endif %} diff --git a/docsite/_themes/aworks/theme.conf b/docsite/_themes/aworks/theme.conf deleted file mode 100644 index 64f9efd569..0000000000 --- a/docsite/_themes/aworks/theme.conf +++ /dev/null @@ -1,5 +0,0 @@ -# Twitter Bootstrap Theme -[theme] -inherit = basic -stylesheet = basic.css -pygments_style = tango diff --git a/docsite/_themes/bootstrap/globaltoc.html b/docsite/_themes/bootstrap/globaltoc.html deleted file mode 100644 index b5a5cb760f..0000000000 --- a/docsite/_themes/bootstrap/globaltoc.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/docsite/_themes/bootstrap/layout.html b/docsite/_themes/bootstrap/layout.html deleted file mode 100644 index 43431de062..0000000000 --- a/docsite/_themes/bootstrap/layout.html +++ /dev/null @@ -1,155 +0,0 @@ -{% extends "basic/layout.html" %} -{% set script_files = script_files + ['_static/bootstrap-dropdown.js', '_static/bootstrap-scrollspy.js'] %} -{% set css_files = ['_static/bootstrap.css', '_static/bootstrap-sphinx.css', '_static/ansible-local.css'] + css_files %} - -{# Sidebar: Rework into our Boostrap nav section. #} -{% macro navBar() %} -
    -
    -
    - - - -
    -
    -
    -{% endmacro %} -

    - -{%- block extrahead %} - - - - - -{% endblock %} - -{% block header %}{{ navBar() }}{% endblock %} - -{# Silence the sidebar's, relbar's #} -{% block sidebar1 %}{% endblock %} -{% block sidebar2 %}{% endblock %} -{% block relbar1 %}{% endblock %} -{% block relbar2 %}{% endblock %} - -{%- block content %} - -
    - {% block body %} {% endblock %} -
    -
    - -{%- endblock %} - -{%- block footer %} -
    -
    -AnsibleWorks -

    - {%- if show_copyright %} - {%- if hasdoc('copyright') %} - {% trans path=pathto('copyright'), copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %}
    - {%- else %} - {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %}
    - {%- endif %} - {%- endif %} - {%- if last_updated %} - {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %}
    - {%- endif %} -

    - -
    -
    -{%- endblock %} - diff --git a/docsite/_themes/bootstrap/localtoc.html b/docsite/_themes/bootstrap/localtoc.html deleted file mode 100644 index d3014cf7fa..0000000000 --- a/docsite/_themes/bootstrap/localtoc.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/docsite/_themes/bootstrap/relations.html b/docsite/_themes/bootstrap/relations.html deleted file mode 100644 index b3df68e3b5..0000000000 --- a/docsite/_themes/bootstrap/relations.html +++ /dev/null @@ -1,8 +0,0 @@ -{%- if prev %} -
  • {{ "«"|safe }} {{ prev.title }}
  • -{%- endif %} -{%- if next %} -
  • {{ next.title }} {{ "»"|safe }}
  • -{%- endif %} diff --git a/docsite/_themes/bootstrap/searchbox.html b/docsite/_themes/bootstrap/searchbox.html deleted file mode 100644 index 3063dde3d1..0000000000 --- a/docsite/_themes/bootstrap/searchbox.html +++ /dev/null @@ -1,7 +0,0 @@ -{%- if pagename != "search" %} -
    - - - -
    -{%- endif %} diff --git a/docsite/_themes/bootstrap/sourcelink.html b/docsite/_themes/bootstrap/sourcelink.html deleted file mode 100644 index 21ae2d8fef..0000000000 --- a/docsite/_themes/bootstrap/sourcelink.html +++ /dev/null @@ -1,4 +0,0 @@ -{%- if show_source and has_source and sourcename %} -
  • {{ _('Source') }}
  • -{%- endif %} diff --git a/docsite/_themes/bootstrap/static/bootstrap-dropdown.js b/docsite/_themes/bootstrap/static/bootstrap-dropdown.js deleted file mode 100644 index fda6da597e..0000000000 --- a/docsite/_themes/bootstrap/static/bootstrap-dropdown.js +++ /dev/null @@ -1,55 +0,0 @@ -/* ============================================================ - * bootstrap-dropdown.js v1.4.0 - * http://twitter.github.com/bootstrap/javascript.html#dropdown - * ============================================================ - * Copyright 2011 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============================================================ */ - - -!function( $ ){ - - "use strict" - - /* DROPDOWN PLUGIN DEFINITION - * ========================== */ - - $.fn.dropdown = function ( selector ) { - return this.each(function () { - $(this).delegate(selector || d, 'click', function (e) { - var li = $(this).parent('li') - , isActive = li.hasClass('open') - - clearMenus() - !isActive && li.toggleClass('open') - return false - }) - }) - } - - /* APPLY TO STANDARD DROPDOWN ELEMENTS - * =================================== */ - - var d = 'a.menu, .dropdown-toggle' - - function clearMenus() { - $(d).parent('li').removeClass('open') - } - - $(function () { - $('html').bind("click", clearMenus) - $('body').dropdown( '[data-dropdown] a.menu, [data-dropdown] .dropdown-toggle' ) - }) - -}( window.jQuery || window.ender ); diff --git a/docsite/_themes/bootstrap/static/bootstrap-scrollspy.js b/docsite/_themes/bootstrap/static/bootstrap-scrollspy.js deleted file mode 100644 index efbc432960..0000000000 --- a/docsite/_themes/bootstrap/static/bootstrap-scrollspy.js +++ /dev/null @@ -1,107 +0,0 @@ -/* ============================================================= - * bootstrap-scrollspy.js v1.4.0 - * http://twitter.github.com/bootstrap/javascript.html#scrollspy - * ============================================================= - * Copyright 2011 Twitter, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============================================================== */ - - -!function ( $ ) { - - "use strict" - - var $window = $(window) - - function ScrollSpy( topbar, selector ) { - var processScroll = $.proxy(this.processScroll, this) - this.$topbar = $(topbar) - this.selector = selector || 'li > a' - this.refresh() - this.$topbar.delegate(this.selector, 'click', processScroll) - $window.scroll(processScroll) - this.processScroll() - } - - ScrollSpy.prototype = { - - refresh: function () { - this.targets = this.$topbar.find(this.selector).map(function () { - var href = $(this).attr('href') - return /^#\w/.test(href) && $(href).length ? href : null - }) - - this.offsets = $.map(this.targets, function (id) { - return $(id).offset().top - }) - } - - , processScroll: function () { - var scrollTop = $window.scrollTop() + 10 - , offsets = this.offsets - , targets = this.targets - , activeTarget = this.activeTarget - , i - - for (i = offsets.length; i--;) { - activeTarget != targets[i] - && scrollTop >= offsets[i] - && (!offsets[i + 1] || scrollTop <= offsets[i + 1]) - && this.activateButton( targets[i] ) - } - } - - , activateButton: function (target) { - this.activeTarget = target - - this.$topbar - .find(this.selector).parent('.active') - .removeClass('active') - - this.$topbar - .find(this.selector + '[href="' + target + '"]') - .parent('li') - .addClass('active') - } - - } - - /* SCROLLSPY PLUGIN DEFINITION - * =========================== */ - - $.fn.scrollSpy = function( options ) { - var scrollspy = this.data('scrollspy') - - if (!scrollspy) { - return this.each(function () { - $(this).data('scrollspy', new ScrollSpy( this, options )) - }) - } - - if ( options === true ) { - return scrollspy - } - - if ( typeof options == 'string' ) { - scrollspy[options]() - } - - return this - } - - $(document).ready(function () { - $('body').scrollSpy('[data-scrollspy] li > a') - }) - -}( window.jQuery || window.ender ); \ No newline at end of file diff --git a/docsite/_themes/bootstrap/static/bootstrap-sphinx.css_t b/docsite/_themes/bootstrap/static/bootstrap-sphinx.css_t deleted file mode 100644 index 044eb4f206..0000000000 --- a/docsite/_themes/bootstrap/static/bootstrap-sphinx.css_t +++ /dev/null @@ -1,24 +0,0 @@ -/* -* bootstrap-sphinx.css -* ~~~~~~~~~~~~~~~~~~~~ -* -* Sphinx stylesheet -- Twitter Bootstrap theme. -*/ - -body { - padding-top: 42px; -} - -div.documentwrapper { - float: left; - width: 100%; -} - -{%- block sidebarlogo %} - {%- if logo %} -.topbar h3 a, .topbar .brand { - background: transparent url("{{ logo }}") no-repeat 22px 3px; - padding-left: 62px; -} - {%- endif %} -{%- endblock %} diff --git a/docsite/_themes/bootstrap/static/bootstrap.css b/docsite/_themes/bootstrap/static/bootstrap.css deleted file mode 100644 index 3ab0f1ff55..0000000000 --- a/docsite/_themes/bootstrap/static/bootstrap.css +++ /dev/null @@ -1,2469 +0,0 @@ -/*! - * Bootstrap v1.4.0 - * - * Copyright 2011 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world @twitter by @mdo and @fat. - * Date: Sun Nov 20 21:42:29 PST 2011 - */ -/* Reset.less - * Props to Eric Meyer (meyerweb.com) for his CSS reset file. We're using an adapted version here that cuts out some of the reset HTML elements we will never need here (i.e., dfn, samp, etc). - * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ -html, body { - margin: 0; - padding: 0; -} -h1, -h2, -h3, -h4, -h5, -h6, -p, -blockquote, -pre, -a, -abbr, -acronym, -address, -cite, -code, -del, -dfn, -em, -img, -q, -s, -samp, -small, -strike, -strong, -sub, -sup, -tt, -var, -dd, -dl, -dt, -li, -ol, -ul, -fieldset, -form, -label, -legend, -button, -table, -caption, -tbody, -tfoot, -thead, -tr, -th, -td { - margin: 0; - padding: 0; - border: 0; - font-weight: normal; - font-style: normal; - font-size: 100%; - line-height: 1; - font-family: inherit; -} -table { - border-collapse: collapse; - border-spacing: 0; -} -ol, ul { - list-style: none; -} -q:before, -q:after, -blockquote:before, -blockquote:after { - content: ""; -} -html { - overflow-y: scroll; - font-size: 100%; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} -a:focus { - outline: thin dotted; -} -a:hover, a:active { - outline: 0; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -nav, -section { - display: block; -} -audio, canvas, video { - display: inline-block; - *display: inline; - *zoom: 1; -} -audio:not([controls]) { - display: none; -} -sub, sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} -sup { - top: -0.5em; -} -sub { - bottom: -0.25em; -} -img { - border: 0; - -ms-interpolation-mode: bicubic; -} -button, -input, -select, -textarea { - font-size: 100%; - margin: 0; - vertical-align: baseline; - *vertical-align: middle; -} -button, input { - line-height: normal; - *overflow: visible; -} -button::-moz-focus-inner, input::-moz-focus-inner { - border: 0; - padding: 0; -} -button, -input[type="button"], -input[type="reset"], -input[type="submit"] { - cursor: pointer; - -webkit-appearance: button; -} -input[type="search"] { - -webkit-appearance: textfield; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -textarea { - overflow: auto; - vertical-align: top; -} -/* Variables.less - * Variables to customize the look and feel of Bootstrap - * ----------------------------------------------------- */ -/* Mixins.less - * Snippets of reusable CSS to develop faster and keep code readable - * ----------------------------------------------------------------- */ -/* - * Scaffolding - * Basic and global styles for generating a grid system, structural layout, and page templates - * ------------------------------------------------------------------------------------------- */ -body { - background-color: #ffffff; - margin: 0; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - font-weight: normal; - line-height: 18px; - color: #404040; -} -.container { - width: 940px; - margin-left: auto; - margin-right: auto; - zoom: 1; -} -.container:before, .container:after { - display: table; - content: ""; - zoom: 1; -} -.container:after { - clear: both; -} -.container-fluid { - position: relative; - min-width: 940px; - padding-left: 20px; - padding-right: 20px; - zoom: 1; -} -.container-fluid:before, .container-fluid:after { - display: table; - content: ""; - zoom: 1; -} -.container-fluid:after { - clear: both; -} -.container-fluid > .sidebar { - position: absolute; - top: 0; - left: 20px; - width: 220px; -} -.container-fluid > .content { - margin-left: 240px; -} -a { - color: #0069d6; - text-decoration: none; - line-height: inherit; - font-weight: inherit; -} -a:hover { - color: #00438a; - text-decoration: underline; -} -.pull-right { - float: right; -} -.pull-left { - float: left; -} -.hide { - display: none; -} -.show { - display: block; -} -.row { - zoom: 1; - margin-left: -20px; -} -.row:before, .row:after { - display: table; - content: ""; - zoom: 1; -} -.row:after { - clear: both; -} -.row > [class*="span"] { - display: inline; - float: left; - margin-left: 20px; -} -.span1 { - width: 40px; -} -.span2 { - width: 100px; -} -.span3 { - width: 160px; -} -.span4 { - width: 220px; -} -.span5 { - width: 280px; -} -.span6 { - width: 340px; -} -.span7 { - width: 400px; -} -.span8 { - width: 460px; -} -.span9 { - width: 520px; -} -.span10 { - width: 580px; -} -.span11 { - width: 640px; -} -.span12 { - width: 700px; -} -.span13 { - width: 760px; -} -.span14 { - width: 820px; -} -.span15 { - width: 880px; -} -.span16 { - width: 940px; -} -.span17 { - width: 1000px; -} -.span18 { - width: 1060px; -} -.span19 { - width: 1120px; -} -.span20 { - width: 1180px; -} -.span21 { - width: 1240px; -} -.span22 { - width: 1300px; -} -.span23 { - width: 1360px; -} -.span24 { - width: 1420px; -} -.row > .offset1 { - margin-left: 80px; -} -.row > .offset2 { - margin-left: 140px; -} -.row > .offset3 { - margin-left: 200px; -} -.row > .offset4 { - margin-left: 260px; -} -.row > .offset5 { - margin-left: 320px; -} -.row > .offset6 { - margin-left: 380px; -} -.row > .offset7 { - margin-left: 440px; -} -.row > .offset8 { - margin-left: 500px; -} -.row > .offset9 { - margin-left: 560px; -} -.row > .offset10 { - margin-left: 620px; -} -.row > .offset11 { - margin-left: 680px; -} -.row > .offset12 { - margin-left: 740px; -} -.span-one-third { - width: 300px; -} -.span-two-thirds { - width: 620px; -} -.row > .offset-one-third { - margin-left: 340px; -} -.row > .offset-two-thirds { - margin-left: 660px; -} -/* Typography.less - * Headings, body text, lists, code, and more for a versatile and durable typography system - * ---------------------------------------------------------------------------------------- */ -p { - font-size: 13px; - font-weight: normal; - line-height: 18px; - margin-bottom: 9px; -} -p small { - font-size: 11px; - color: #bfbfbf; -} -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: bold; - color: #404040; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small { - color: #bfbfbf; -} -h1 { - margin-bottom: 18px; - font-size: 30px; - line-height: 36px; -} -h1 small { - font-size: 18px; -} -h2 { - font-size: 24px; - line-height: 36px; -} -h2 small { - font-size: 14px; -} -h3, -h4, -h5, -h6 { - line-height: 36px; -} -h3 { - font-size: 18px; -} -h3 small { - font-size: 14px; -} -h4 { - font-size: 16px; -} -h4 small { - font-size: 12px; -} -h5 { - font-size: 14px; -} -h6 { - font-size: 13px; - color: #bfbfbf; - text-transform: uppercase; -} -ul, ol { - margin: 0 0 18px 25px; -} -ul ul, -ul ol, -ol ol, -ol ul { - margin-bottom: 0; -} -ul { - list-style: disc; -} -ol { - list-style: decimal; -} -li { - line-height: 18px; - color: #808080; -} -ul.unstyled { - list-style: none; - margin-left: 0; -} -dl { - margin-bottom: 18px; -} -dl dt, dl dd { - line-height: 18px; -} -dl dt { - font-weight: bold; -} -dl dd { - margin-left: 9px; -} -hr { - margin: 20px 0 19px; - border: 0; - border-bottom: 1px solid #eee; -} -strong { - font-style: inherit; - font-weight: bold; -} -em { - font-style: italic; - font-weight: inherit; - line-height: inherit; -} -.muted { - color: #bfbfbf; -} -blockquote { - margin-bottom: 18px; - border-left: 5px solid #eee; - padding-left: 15px; -} -blockquote p { - font-size: 14px; - font-weight: 300; - line-height: 18px; - margin-bottom: 0; -} -blockquote small { - display: block; - font-size: 12px; - font-weight: 300; - line-height: 18px; - color: #bfbfbf; -} -blockquote small:before { - content: '\2014 \00A0'; -} -address { - display: block; - line-height: 18px; - margin-bottom: 18px; -} -code, pre { - padding: 0 3px 2px; - font-family: Monaco, Andale Mono, Courier New, monospace; - font-size: 12px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.module { - font-family: Monaco, Andale Mono, Courier New, monospace; - font-size: 12px; -} -code { - color: rgba(0, 0, 0, 0.75); -} -pre { - background-color: #f5f5f5; - display: block; - padding: 8.5px; - margin: 0 0 18px; - line-height: 18px; - font-size: 12px; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, 0.15); - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - white-space: pre; - white-space: pre-wrap; - word-wrap: break-word; -} -/* Forms.less - * Base styles for various input types, form layouts, and states - * ------------------------------------------------------------- */ -form { - margin-bottom: 18px; -} -fieldset { - margin-bottom: 18px; - padding-top: 18px; -} -fieldset legend { - display: block; - padding-left: 150px; - font-size: 19.5px; - line-height: 1; - color: #404040; - *padding: 0 0 5px 145px; - /* IE6-7 */ - - *line-height: 1.5; - /* IE6-7 */ - -} -form .clearfix { - margin-bottom: 18px; - zoom: 1; -} -form .clearfix:before, form .clearfix:after { - display: table; - content: ""; - zoom: 1; -} -form .clearfix:after { - clear: both; -} -label, -input, -select, -textarea { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 13px; - font-weight: normal; - line-height: normal; -} -label { - padding-top: 6px; - font-size: 13px; - line-height: 18px; - float: left; - width: 130px; - text-align: right; - color: #404040; -} -form .input { - margin-left: 150px; -} -input[type=checkbox], input[type=radio] { - cursor: pointer; -} -input, -textarea, -select, -.uneditable-input { - display: inline-block; - width: 210px; - height: 18px; - padding: 4px; - font-size: 13px; - line-height: 18px; - color: #808080; - border: 1px solid #ccc; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -select { - padding: initial; -} -input[type=checkbox], input[type=radio] { - width: auto; - height: auto; - padding: 0; - margin: 3px 0; - *margin-top: 0; - /* IE6-7 */ - - line-height: normal; - border: none; -} -input[type=file] { - background-color: #ffffff; - padding: initial; - border: initial; - line-height: initial; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} -input[type=button], input[type=reset], input[type=submit] { - width: auto; - height: auto; -} -select, input[type=file] { - height: 27px; - *height: auto; - line-height: 27px; - *margin-top: 4px; - /* For IE7, add top margin to align select with labels */ - -} -select[multiple] { - height: inherit; - background-color: #ffffff; -} -textarea { - height: auto; -} -.uneditable-input { - background-color: #ffffff; - display: block; - border-color: #eee; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); - cursor: not-allowed; -} -:-moz-placeholder { - color: #bfbfbf; -} -::-webkit-input-placeholder { - color: #bfbfbf; -} -input, textarea { - -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; - -moz-transition: border linear 0.2s, box-shadow linear 0.2s; - -ms-transition: border linear 0.2s, box-shadow linear 0.2s; - -o-transition: border linear 0.2s, box-shadow linear 0.2s; - transition: border linear 0.2s, box-shadow linear 0.2s; - -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); - -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); -} -input:focus, textarea:focus { - outline: 0; - border-color: rgba(82, 168, 236, 0.8); - -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); - -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); - box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); -} -input[type=file]:focus, input[type=checkbox]:focus, select:focus { - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - outline: 1px dotted #666; -} -form .clearfix.error > label, form .clearfix.error .help-block, form .clearfix.error .help-inline { - color: #b94a48; -} -form .clearfix.error input, form .clearfix.error textarea { - color: #b94a48; - border-color: #ee5f5b; -} -form .clearfix.error input:focus, form .clearfix.error textarea:focus { - border-color: #e9322d; - -webkit-box-shadow: 0 0 6px #f8b9b7; - -moz-box-shadow: 0 0 6px #f8b9b7; - box-shadow: 0 0 6px #f8b9b7; -} -form .clearfix.error .input-prepend .add-on, form .clearfix.error .input-append .add-on { - color: #b94a48; - background-color: #fce6e6; - border-color: #b94a48; -} -form .clearfix.warning > label, form .clearfix.warning .help-block, form .clearfix.warning .help-inline { - color: #c09853; -} -form .clearfix.warning input, form .clearfix.warning textarea { - color: #c09853; - border-color: #ccae64; -} -form .clearfix.warning input:focus, form .clearfix.warning textarea:focus { - border-color: #be9a3f; - -webkit-box-shadow: 0 0 6px #e5d6b1; - -moz-box-shadow: 0 0 6px #e5d6b1; - box-shadow: 0 0 6px #e5d6b1; -} -form .clearfix.warning .input-prepend .add-on, form .clearfix.warning .input-append .add-on { - color: #c09853; - background-color: #d2b877; - border-color: #c09853; -} -form .clearfix.success > label, form .clearfix.success .help-block, form .clearfix.success .help-inline { - color: #468847; -} -form .clearfix.success input, form .clearfix.success textarea { - color: #468847; - border-color: #57a957; -} -form .clearfix.success input:focus, form .clearfix.success textarea:focus { - border-color: #458845; - -webkit-box-shadow: 0 0 6px #9acc9a; - -moz-box-shadow: 0 0 6px #9acc9a; - box-shadow: 0 0 6px #9acc9a; -} -form .clearfix.success .input-prepend .add-on, form .clearfix.success .input-append .add-on { - color: #468847; - background-color: #bcddbc; - border-color: #468847; -} -.input-mini, -input.mini, -textarea.mini, -select.mini { - width: 60px; -} -.input-small, -input.small, -textarea.small, -select.small { - width: 90px; -} -.input-medium, -input.medium, -textarea.medium, -select.medium { - width: 150px; -} -.input-large, -input.large, -textarea.large, -select.large { - width: 210px; -} -.input-xlarge, -input.xlarge, -textarea.xlarge, -select.xlarge { - width: 270px; -} -.input-xxlarge, -input.xxlarge, -textarea.xxlarge, -select.xxlarge { - width: 530px; -} -textarea.xxlarge { - overflow-y: auto; -} -input.span1, textarea.span1 { - display: inline-block; - float: none; - width: 30px; - margin-left: 0; -} -input.span2, textarea.span2 { - display: inline-block; - float: none; - width: 90px; - margin-left: 0; -} -input.span3, textarea.span3 { - display: inline-block; - float: none; - width: 150px; - margin-left: 0; -} -input.span4, textarea.span4 { - display: inline-block; - float: none; - width: 210px; - margin-left: 0; -} -input.span5, textarea.span5 { - display: inline-block; - float: none; - width: 270px; - margin-left: 0; -} -input.span6, textarea.span6 { - display: inline-block; - float: none; - width: 330px; - margin-left: 0; -} -input.span7, textarea.span7 { - display: inline-block; - float: none; - width: 390px; - margin-left: 0; -} -input.span8, textarea.span8 { - display: inline-block; - float: none; - width: 450px; - margin-left: 0; -} -input.span9, textarea.span9 { - display: inline-block; - float: none; - width: 510px; - margin-left: 0; -} -input.span10, textarea.span10 { - display: inline-block; - float: none; - width: 570px; - margin-left: 0; -} -input.span11, textarea.span11 { - display: inline-block; - float: none; - width: 630px; - margin-left: 0; -} -input.span12, textarea.span12 { - display: inline-block; - float: none; - width: 690px; - margin-left: 0; -} -input.span13, textarea.span13 { - display: inline-block; - float: none; - width: 750px; - margin-left: 0; -} -input.span14, textarea.span14 { - display: inline-block; - float: none; - width: 810px; - margin-left: 0; -} -input.span15, textarea.span15 { - display: inline-block; - float: none; - width: 870px; - margin-left: 0; -} -input.span16, textarea.span16 { - display: inline-block; - float: none; - width: 930px; - margin-left: 0; -} -input[disabled], -select[disabled], -textarea[disabled], -input[readonly], -select[readonly], -textarea[readonly] { - background-color: #f5f5f5; - border-color: #ddd; - cursor: not-allowed; -} -.actions { - background: #f5f5f5; - margin-top: 18px; - margin-bottom: 18px; - padding: 17px 20px 18px 150px; - border-top: 1px solid #ddd; - -webkit-border-radius: 0 0 3px 3px; - -moz-border-radius: 0 0 3px 3px; - border-radius: 0 0 3px 3px; -} -.actions .secondary-action { - float: right; -} -.actions .secondary-action a { - line-height: 30px; -} -.actions .secondary-action a:hover { - text-decoration: underline; -} -.help-inline, .help-block { - font-size: 13px; - line-height: 18px; - color: #bfbfbf; -} -.help-inline { - padding-left: 5px; - *position: relative; - /* IE6-7 */ - - *top: -5px; - /* IE6-7 */ - -} -.help-block { - display: block; - max-width: 600px; -} -.inline-inputs { - color: #808080; -} -.inline-inputs span { - padding: 0 2px 0 1px; -} -.input-prepend input, .input-append input { - -webkit-border-radius: 0 3px 3px 0; - -moz-border-radius: 0 3px 3px 0; - border-radius: 0 3px 3px 0; -} -.input-prepend .add-on, .input-append .add-on { - position: relative; - background: #f5f5f5; - border: 1px solid #ccc; - z-index: 2; - float: left; - display: block; - width: auto; - min-width: 16px; - height: 18px; - padding: 4px 4px 4px 5px; - margin-right: -1px; - font-weight: normal; - line-height: 18px; - color: #bfbfbf; - text-align: center; - text-shadow: 0 1px 0 #ffffff; - -webkit-border-radius: 3px 0 0 3px; - -moz-border-radius: 3px 0 0 3px; - border-radius: 3px 0 0 3px; -} -.input-prepend .active, .input-append .active { - background: #a9dba9; - border-color: #46a546; -} -.input-prepend .add-on { - *margin-top: 1px; - /* IE6-7 */ - -} -.input-append input { - float: left; - -webkit-border-radius: 3px 0 0 3px; - -moz-border-radius: 3px 0 0 3px; - border-radius: 3px 0 0 3px; -} -.input-append .add-on { - -webkit-border-radius: 0 3px 3px 0; - -moz-border-radius: 0 3px 3px 0; - border-radius: 0 3px 3px 0; - margin-right: 0; - margin-left: -1px; -} -.inputs-list { - margin: 0 0 5px; - width: 100%; -} -.inputs-list li { - display: block; - padding: 0; - width: 100%; -} -.inputs-list label { - display: block; - float: none; - width: auto; - padding: 0; - margin-left: 20px; - line-height: 18px; - text-align: left; - white-space: normal; -} -.inputs-list label strong { - color: #808080; -} -.inputs-list label small { - font-size: 11px; - font-weight: normal; -} -.inputs-list .inputs-list { - margin-left: 25px; - margin-bottom: 10px; - padding-top: 0; -} -.inputs-list:first-child { - padding-top: 6px; -} -.inputs-list li + li { - padding-top: 2px; -} -.inputs-list input[type=radio], .inputs-list input[type=checkbox] { - margin-bottom: 0; - margin-left: -20px; - float: left; -} -.form-stacked { - padding-left: 20px; -} -.form-stacked fieldset { - padding-top: 9px; -} -.form-stacked legend { - padding-left: 0; -} -.form-stacked label { - display: block; - float: none; - width: auto; - font-weight: bold; - text-align: left; - line-height: 20px; - padding-top: 0; -} -.form-stacked .clearfix { - margin-bottom: 9px; -} -.form-stacked .clearfix div.input { - margin-left: 0; -} -.form-stacked .inputs-list { - margin-bottom: 0; -} -.form-stacked .inputs-list li { - padding-top: 0; -} -.form-stacked .inputs-list li label { - font-weight: normal; - padding-top: 0; -} -.form-stacked div.clearfix.error { - padding-top: 10px; - padding-bottom: 10px; - padding-left: 10px; - margin-top: 0; - margin-left: -10px; -} -.form-stacked .actions { - margin-left: -20px; - padding-left: 20px; -} -/* - * Tables.less - * Tables for, you guessed it, tabular data - * ---------------------------------------- */ -table { - width: 100%; - margin-bottom: 18px; - padding: 0; - font-size: 13px; - border-collapse: collapse; -} -table th, table td { - padding: 10px 10px 9px; - line-height: 18px; - text-align: left; -} -table th { - padding-top: 9px; - font-weight: bold; - vertical-align: middle; -} -table td { - vertical-align: top; - border-top: 1px solid #ddd; -} -table tbody th { - border-top: 1px solid #ddd; - vertical-align: top; -} -.condensed-table th, .condensed-table td { - padding: 5px 5px 4px; -} -.bordered-table { - border: 1px solid #ddd; - border-collapse: separate; - *border-collapse: collapse; - /* IE7, collapse table to remove spacing */ - - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.bordered-table th + th, .bordered-table td + td, .bordered-table th + td { - border-left: 1px solid #ddd; -} -.bordered-table thead tr:first-child th:first-child, .bordered-table tbody tr:first-child td:first-child { - -webkit-border-radius: 4px 0 0 0; - -moz-border-radius: 4px 0 0 0; - border-radius: 4px 0 0 0; -} -.bordered-table thead tr:first-child th:last-child, .bordered-table tbody tr:first-child td:last-child { - -webkit-border-radius: 0 4px 0 0; - -moz-border-radius: 0 4px 0 0; - border-radius: 0 4px 0 0; -} -.bordered-table tbody tr:last-child td:first-child { - -webkit-border-radius: 0 0 0 4px; - -moz-border-radius: 0 0 0 4px; - border-radius: 0 0 0 4px; -} -.bordered-table tbody tr:last-child td:last-child { - -webkit-border-radius: 0 0 4px 0; - -moz-border-radius: 0 0 4px 0; - border-radius: 0 0 4px 0; -} -table .span1 { - width: 20px; -} -table .span2 { - width: 60px; -} -table .span3 { - width: 100px; -} -table .span4 { - width: 140px; -} -table .span5 { - width: 180px; -} -table .span6 { - width: 220px; -} -table .span7 { - width: 260px; -} -table .span8 { - width: 300px; -} -table .span9 { - width: 340px; -} -table .span10 { - width: 380px; -} -table .span11 { - width: 420px; -} -table .span12 { - width: 460px; -} -table .span13 { - width: 500px; -} -table .span14 { - width: 540px; -} -table .span15 { - width: 580px; -} -table .span16 { - width: 620px; -} -.zebra-striped tbody tr:nth-child(odd) td, .zebra-striped tbody tr:nth-child(odd) th { - background-color: #f9f9f9; -} -.zebra-striped tbody tr:hover td, .zebra-striped tbody tr:hover th { - background-color: #f5f5f5; -} -table .header { - cursor: pointer; -} -table .header:after { - content: ""; - float: right; - margin-top: 7px; - border-width: 0 4px 4px; - border-style: solid; - border-color: #000 transparent; - visibility: hidden; -} -table .headerSortUp, table .headerSortDown { - background-color: rgba(141, 192, 219, 0.25); - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); -} -table .header:hover:after { - visibility: visible; -} -table .headerSortDown:after, table .headerSortDown:hover:after { - visibility: visible; - filter: alpha(opacity=60); - -khtml-opacity: 0.6; - -moz-opacity: 0.6; - opacity: 0.6; -} -table .headerSortUp:after { - border-bottom: none; - border-left: 4px solid transparent; - border-right: 4px solid transparent; - border-top: 4px solid #000; - visibility: visible; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - filter: alpha(opacity=60); - -khtml-opacity: 0.6; - -moz-opacity: 0.6; - opacity: 0.6; -} -table .blue { - color: #049cdb; - border-bottom-color: #049cdb; -} -table .headerSortUp.blue, table .headerSortDown.blue { - background-color: #ade6fe; -} -table .green { - color: #46a546; - border-bottom-color: #46a546; -} -table .headerSortUp.green, table .headerSortDown.green { - background-color: #cdeacd; -} -table .red { - color: #9d261d; - border-bottom-color: #9d261d; -} -table .headerSortUp.red, table .headerSortDown.red { - background-color: #f4c8c5; -} -table .yellow { - color: #ffc40d; - border-bottom-color: #ffc40d; -} -table .headerSortUp.yellow, table .headerSortDown.yellow { - background-color: #fff6d9; -} -table .orange { - color: #f89406; - border-bottom-color: #f89406; -} -table .headerSortUp.orange, table .headerSortDown.orange { - background-color: #fee9cc; -} -table .purple { - color: #7a43b6; - border-bottom-color: #7a43b6; -} -table .headerSortUp.purple, table .headerSortDown.purple { - background-color: #e2d5f0; -} -/* Patterns.less - * Repeatable UI elements outside the base styles provided from the scaffolding - * ---------------------------------------------------------------------------- */ -.topbar { - height: 40px; - position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 10000; - overflow: visible; -} -.topbar a { - color: #bfbfbf; - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); -} -.topbar h3 a:hover, .topbar .brand:hover, .topbar ul .active > a { - background-color: #333; - background-color: rgba(255, 255, 255, 0.05); - color: #ffffff; - text-decoration: none; -} -.topbar h3 { - position: relative; -} -.topbar h3 a, .topbar .brand { - float: left; - display: block; - padding: 8px 20px 12px; - margin-left: -20px; - color: #ffffff; - font-size: 20px; - font-weight: 200; - line-height: 1; -} -.topbar p { - margin: 0; - line-height: 40px; -} -.topbar p a:hover { - background-color: transparent; - color: #ffffff; -} -.topbar form { - float: left; - margin: 5px 0 0 0; - position: relative; - filter: alpha(opacity=100); - -khtml-opacity: 1; - -moz-opacity: 1; - opacity: 1; -} -.topbar form.pull-right { - float: right; -} -.topbar input { - background-color: #444; - background-color: rgba(255, 255, 255, 0.3); - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: normal; - font-weight: 13px; - line-height: 1; - padding: 4px 9px; - color: #ffffff; - color: rgba(255, 255, 255, 0.75); - border: 1px solid #111; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); - -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0px rgba(255, 255, 255, 0.25); - -webkit-transition: none; - -moz-transition: none; - -ms-transition: none; - -o-transition: none; - transition: none; -} -.topbar input:-moz-placeholder { - color: #e6e6e6; -} -.topbar input::-webkit-input-placeholder { - color: #e6e6e6; -} -.topbar input:hover { - background-color: #bfbfbf; - background-color: rgba(255, 255, 255, 0.5); - color: #ffffff; -} -.topbar input:focus, .topbar input.focused { - outline: 0; - background-color: #ffffff; - color: #404040; - text-shadow: 0 1px 0 #ffffff; - border: 0; - padding: 5px 10px; - -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); - box-shadow: 0 0 3px rgba(0, 0, 0, 0.15); -} -.topbar-inner, .topbar .fill { - background-color: #222; - background-color: #222222; - background-repeat: repeat-x; - background-image: -khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222)); - background-image: -moz-linear-gradient(top, #333333, #222222); - background-image: -ms-linear-gradient(top, #333333, #222222); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222)); - background-image: -webkit-linear-gradient(top, #333333, #222222); - background-image: -o-linear-gradient(top, #333333, #222222); - background-image: linear-gradient(top, #333333, #222222); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0); - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), inset 0 -1px 0 rgba(0, 0, 0, 0.1); -} -.topbar div > ul, .nav { - display: block; - float: left; - margin: 0 10px 0 0; - position: relative; - left: 0; -} -.topbar div > ul > li, .nav > li { - display: block; - float: left; -} -.topbar div > ul a, .nav a { - display: block; - float: none; - padding: 10px 10px 11px; - line-height: 19px; - text-decoration: none; -} -.topbar div > ul a:hover, .nav a:hover { - color: #ffffff; - text-decoration: none; -} -.topbar div > ul .active > a, .nav .active > a { - background-color: #222; - background-color: rgba(0, 0, 0, 0.5); -} -.topbar div > ul.secondary-nav, .nav.secondary-nav { - float: right; - margin-left: 10px; - margin-right: 0; -} -.topbar div > ul.secondary-nav .menu-dropdown, -.nav.secondary-nav .menu-dropdown, -.topbar div > ul.secondary-nav .dropdown-menu, -.nav.secondary-nav .dropdown-menu { - right: 0; - border: 0; -} -.topbar div > ul a.menu:hover, -.nav a.menu:hover, -.topbar div > ul li.open .menu, -.nav li.open .menu, -.topbar div > ul .dropdown-toggle:hover, -.nav .dropdown-toggle:hover, -.topbar div > ul .dropdown.open .dropdown-toggle, -.nav .dropdown.open .dropdown-toggle { - background: #444; - background: rgba(255, 255, 255, 0.05); -} -.topbar div > ul .menu-dropdown, -.nav .menu-dropdown, -.topbar div > ul .dropdown-menu, -.nav .dropdown-menu { - background-color: #333; -} -.topbar div > ul .menu-dropdown a.menu, -.nav .menu-dropdown a.menu, -.topbar div > ul .dropdown-menu a.menu, -.nav .dropdown-menu a.menu, -.topbar div > ul .menu-dropdown .dropdown-toggle, -.nav .menu-dropdown .dropdown-toggle, -.topbar div > ul .dropdown-menu .dropdown-toggle, -.nav .dropdown-menu .dropdown-toggle { - color: #ffffff; -} -.topbar div > ul .menu-dropdown a.menu.open, -.nav .menu-dropdown a.menu.open, -.topbar div > ul .dropdown-menu a.menu.open, -.nav .dropdown-menu a.menu.open, -.topbar div > ul .menu-dropdown .dropdown-toggle.open, -.nav .menu-dropdown .dropdown-toggle.open, -.topbar div > ul .dropdown-menu .dropdown-toggle.open, -.nav .dropdown-menu .dropdown-toggle.open { - background: #444; - background: rgba(255, 255, 255, 0.05); -} -.topbar div > ul .menu-dropdown li a, -.nav .menu-dropdown li a, -.topbar div > ul .dropdown-menu li a, -.nav .dropdown-menu li a { - color: #999; - text-shadow: 0 1px 0 rgba(0, 0, 0, 0.5); -} -.topbar div > ul .menu-dropdown li a:hover, -.nav .menu-dropdown li a:hover, -.topbar div > ul .dropdown-menu li a:hover, -.nav .dropdown-menu li a:hover { - background-color: #191919; - background-repeat: repeat-x; - background-image: -khtml-gradient(linear, left top, left bottom, from(#292929), to(#191919)); - background-image: -moz-linear-gradient(top, #292929, #191919); - background-image: -ms-linear-gradient(top, #292929, #191919); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #292929), color-stop(100%, #191919)); - background-image: -webkit-linear-gradient(top, #292929, #191919); - background-image: -o-linear-gradient(top, #292929, #191919); - background-image: linear-gradient(top, #292929, #191919); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#292929', endColorstr='#191919', GradientType=0); - color: #ffffff; -} -.topbar div > ul .menu-dropdown .active a, -.nav .menu-dropdown .active a, -.topbar div > ul .dropdown-menu .active a, -.nav .dropdown-menu .active a { - color: #ffffff; -} -.topbar div > ul .menu-dropdown .divider, -.nav .menu-dropdown .divider, -.topbar div > ul .dropdown-menu .divider, -.nav .dropdown-menu .divider { - background-color: #222; - border-color: #444; -} -.topbar ul .menu-dropdown li a, .topbar ul .dropdown-menu li a { - padding: 4px 15px; -} -li.menu, .dropdown { - position: relative; -} -a.menu:after, .dropdown-toggle:after { - width: 0; - height: 0; - display: inline-block; - content: "↓"; - text-indent: -99999px; - vertical-align: top; - margin-top: 8px; - margin-left: 4px; - border-left: 4px solid transparent; - border-right: 4px solid transparent; - border-top: 4px solid #ffffff; - filter: alpha(opacity=50); - -khtml-opacity: 0.5; - -moz-opacity: 0.5; - opacity: 0.5; -} -.menu-dropdown, .dropdown-menu { - background-color: #ffffff; - float: left; - display: none; - position: absolute; - top: 40px; - z-index: 900; - min-width: 160px; - max-width: 220px; - _width: 160px; - margin-left: 0; - margin-right: 0; - padding: 6px 0; - zoom: 1; - border-color: #999; - border-color: rgba(0, 0, 0, 0.2); - border-style: solid; - border-width: 0 1px 1px; - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; - -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - -moz-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - -webkit-background-clip: padding-box; - -moz-background-clip: padding-box; - background-clip: padding-box; -} -.menu-dropdown li, .dropdown-menu li { - float: none; - display: block; - background-color: none; -} -.menu-dropdown .divider, .dropdown-menu .divider { - height: 1px; - margin: 5px 0; - overflow: hidden; - background-color: #eee; - border-bottom: 1px solid #ffffff; -} -.topbar .dropdown-menu a, .dropdown-menu a { - display: block; - padding: 4px 15px; - clear: both; - font-weight: normal; - line-height: 18px; - color: #808080; - text-shadow: 0 1px 0 #ffffff; -} -.topbar .dropdown-menu a:hover, -.dropdown-menu a:hover, -.topbar .dropdown-menu a.hover, -.dropdown-menu a.hover { - background-color: #dddddd; - background-repeat: repeat-x; - background-image: -khtml-gradient(linear, left top, left bottom, from(#eeeeee), to(#dddddd)); - background-image: -moz-linear-gradient(top, #eeeeee, #dddddd); - background-image: -ms-linear-gradient(top, #eeeeee, #dddddd); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #eeeeee), color-stop(100%, #dddddd)); - background-image: -webkit-linear-gradient(top, #eeeeee, #dddddd); - background-image: -o-linear-gradient(top, #eeeeee, #dddddd); - background-image: linear-gradient(top, #eeeeee, #dddddd); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#dddddd', GradientType=0); - color: #404040; - text-decoration: none; - -webkit-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); - -moz-box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); - box-shadow: inset 0 1px 0 rgba(0, 0, 0, 0.025), inset 0 -1px rgba(0, 0, 0, 0.025); -} -.open .menu, -.dropdown.open .menu, -.open .dropdown-toggle, -.dropdown.open .dropdown-toggle { - color: #ffffff; - background: #ccc; - background: rgba(0, 0, 0, 0.3); -} -.open .menu-dropdown, -.dropdown.open .menu-dropdown, -.open .dropdown-menu, -.dropdown.open .dropdown-menu { - display: block; -} -.tabs, .pills { - margin: 0 0 18px; - padding: 0; - list-style: none; - zoom: 1; -} -.tabs:before, -.pills:before, -.tabs:after, -.pills:after { - display: table; - content: ""; - zoom: 1; -} -.tabs:after, .pills:after { - clear: both; -} -.tabs > li, .pills > li { - float: left; -} -.tabs > li > a, .pills > li > a { - display: block; -} -.tabs { - border-color: #ddd; - border-style: solid; - border-width: 0 0 1px; -} -.tabs > li { - position: relative; - margin-bottom: -1px; -} -.tabs > li > a { - padding: 0 15px; - margin-right: 2px; - line-height: 34px; - border: 1px solid transparent; - -webkit-border-radius: 4px 4px 0 0; - -moz-border-radius: 4px 4px 0 0; - border-radius: 4px 4px 0 0; -} -.tabs > li > a:hover { - text-decoration: none; - background-color: #eee; - border-color: #eee #eee #ddd; -} -.tabs .active > a, .tabs .active > a:hover { - color: #808080; - background-color: #ffffff; - border: 1px solid #ddd; - border-bottom-color: transparent; - cursor: default; -} -.tabs .menu-dropdown, .tabs .dropdown-menu { - top: 35px; - border-width: 1px; - -webkit-border-radius: 0 6px 6px 6px; - -moz-border-radius: 0 6px 6px 6px; - border-radius: 0 6px 6px 6px; -} -.tabs a.menu:after, .tabs .dropdown-toggle:after { - border-top-color: #999; - margin-top: 15px; - margin-left: 5px; -} -.tabs li.open.menu .menu, .tabs .open.dropdown .dropdown-toggle { - border-color: #999; -} -.tabs li.open a.menu:after, .tabs .dropdown.open .dropdown-toggle:after { - border-top-color: #555; -} -.pills a { - margin: 5px 3px 5px 0; - padding: 0 15px; - line-height: 30px; - text-shadow: 0 1px 1px #ffffff; - -webkit-border-radius: 15px; - -moz-border-radius: 15px; - border-radius: 15px; -} -.pills a:hover { - color: #ffffff; - text-decoration: none; - text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); - background-color: #00438a; -} -.pills .active a { - color: #ffffff; - text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25); - background-color: #0069d6; -} -.pills-vertical > li { - float: none; -} -.tab-content > .tab-pane, -.pill-content > .pill-pane, -.tab-content > div, -.pill-content > div { - display: none; -} -.tab-content > .active, .pill-content > .active { - display: block; -} -.breadcrumb { - padding: 7px 14px; - margin: 0 0 18px; - background-color: #f5f5f5; - background-repeat: repeat-x; - background-image: -khtml-gradient(linear, left top, left bottom, from(#ffffff), to(#f5f5f5)); - background-image: -moz-linear-gradient(top, #ffffff, #f5f5f5); - background-image: -ms-linear-gradient(top, #ffffff, #f5f5f5); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f5f5f5)); - background-image: -webkit-linear-gradient(top, #ffffff, #f5f5f5); - background-image: -o-linear-gradient(top, #ffffff, #f5f5f5); - background-image: linear-gradient(top, #ffffff, #f5f5f5); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0); - border: 1px solid #ddd; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - -webkit-box-shadow: inset 0 1px 0 #ffffff; - -moz-box-shadow: inset 0 1px 0 #ffffff; - box-shadow: inset 0 1px 0 #ffffff; -} -.breadcrumb li { - display: inline; - text-shadow: 0 1px 0 #ffffff; -} -.breadcrumb .divider { - padding: 0 5px; - color: #bfbfbf; -} -.breadcrumb .active a { - color: #404040; -} -.hero-unit { - background-color: #f5f5f5; - margin-bottom: 30px; - padding: 60px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.hero-unit h1 { - margin-bottom: 0; - font-size: 60px; - line-height: 1; - letter-spacing: -1px; -} -.hero-unit p { - font-size: 18px; - font-weight: 200; - line-height: 27px; -} -footer { - margin-top: 17px; - padding-top: 17px; - border-top: 1px solid #eee; -} -.page-header { - margin-bottom: 17px; - border-bottom: 1px solid #ddd; - -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); -} -.page-header h1 { - margin-bottom: 8px; -} -.btn.danger, -.alert-message.danger, -.btn.danger:hover, -.alert-message.danger:hover, -.btn.error, -.alert-message.error, -.btn.error:hover, -.alert-message.error:hover, -.btn.success, -.alert-message.success, -.btn.success:hover, -.alert-message.success:hover, -.btn.info, -.alert-message.info, -.btn.info:hover, -.alert-message.info:hover { - color: #ffffff; -} -.btn .close, .alert-message .close { - font-family: Arial, sans-serif; - line-height: 18px; -} -.btn.danger, -.alert-message.danger, -.btn.error, -.alert-message.error { - background-color: #c43c35; - background-repeat: repeat-x; - background-image: -khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35)); - background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -ms-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35)); - background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35); - background-image: -o-linear-gradient(top, #ee5f5b, #c43c35); - background-image: linear-gradient(top, #ee5f5b, #c43c35); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0); - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - border-color: #c43c35 #c43c35 #882a25; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); -} -.btn.success, .alert-message.success { - background-color: #57a957; - background-repeat: repeat-x; - background-image: -khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957)); - background-image: -moz-linear-gradient(top, #62c462, #57a957); - background-image: -ms-linear-gradient(top, #62c462, #57a957); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957)); - background-image: -webkit-linear-gradient(top, #62c462, #57a957); - background-image: -o-linear-gradient(top, #62c462, #57a957); - background-image: linear-gradient(top, #62c462, #57a957); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0); - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - border-color: #57a957 #57a957 #3d773d; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); -} -.btn.info, .alert-message.info { - background-color: #339bb9; - background-repeat: repeat-x; - background-image: -khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9)); - background-image: -moz-linear-gradient(top, #5bc0de, #339bb9); - background-image: -ms-linear-gradient(top, #5bc0de, #339bb9); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9)); - background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9); - background-image: -o-linear-gradient(top, #5bc0de, #339bb9); - background-image: linear-gradient(top, #5bc0de, #339bb9); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0); - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - border-color: #339bb9 #339bb9 #22697d; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); -} -.btn { - cursor: pointer; - display: inline-block; - background-color: #e6e6e6; - background-repeat: no-repeat; - background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6)); - background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); - background-image: -moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6); - background-image: -ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); - background-image: -o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); - background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); - padding: 5px 14px 6px; - text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); - color: #333; - font-size: 13px; - line-height: normal; - border: 1px solid #ccc; - border-bottom-color: #bbb; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); - -webkit-transition: 0.1s linear all; - -moz-transition: 0.1s linear all; - -ms-transition: 0.1s linear all; - -o-transition: 0.1s linear all; - transition: 0.1s linear all; -} -.btn:hover { - background-position: 0 -15px; - color: #333; - text-decoration: none; -} -.btn:focus { - outline: 1px dotted #666; -} -.btn.primary { - color: #ffffff; - background-color: #0064cd; - background-repeat: repeat-x; - background-image: -khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd)); - background-image: -moz-linear-gradient(top, #049cdb, #0064cd); - background-image: -ms-linear-gradient(top, #049cdb, #0064cd); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd)); - background-image: -webkit-linear-gradient(top, #049cdb, #0064cd); - background-image: -o-linear-gradient(top, #049cdb, #0064cd); - background-image: linear-gradient(top, #049cdb, #0064cd); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0); - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - border-color: #0064cd #0064cd #003f81; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); -} -.btn.active, .btn:active { - -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05); -} -.btn.disabled { - cursor: default; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - filter: alpha(opacity=65); - -khtml-opacity: 0.65; - -moz-opacity: 0.65; - opacity: 0.65; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} -.btn[disabled] { - cursor: default; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - filter: alpha(opacity=65); - -khtml-opacity: 0.65; - -moz-opacity: 0.65; - opacity: 0.65; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} -.btn.large { - font-size: 15px; - line-height: normal; - padding: 9px 14px 9px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; -} -.btn.small { - padding: 7px 9px 7px; - font-size: 11px; -} -:root .alert-message, :root .btn { - border-radius: 0 \0; -} -button.btn::-moz-focus-inner, input[type=submit].btn::-moz-focus-inner { - padding: 0; - border: 0; -} -.close { - float: right; - color: #000000; - font-size: 20px; - font-weight: bold; - line-height: 13.5px; - text-shadow: 0 1px 0 #ffffff; - filter: alpha(opacity=25); - -khtml-opacity: 0.25; - -moz-opacity: 0.25; - opacity: 0.25; -} -.close:hover { - color: #000000; - text-decoration: none; - filter: alpha(opacity=40); - -khtml-opacity: 0.4; - -moz-opacity: 0.4; - opacity: 0.4; -} -.alert-message { - position: relative; - padding: 7px 15px; - margin-bottom: 18px; - color: #404040; - background-color: #eedc94; - background-repeat: repeat-x; - background-image: -khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94)); - background-image: -moz-linear-gradient(top, #fceec1, #eedc94); - background-image: -ms-linear-gradient(top, #fceec1, #eedc94); - background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94)); - background-image: -webkit-linear-gradient(top, #fceec1, #eedc94); - background-image: -o-linear-gradient(top, #fceec1, #eedc94); - background-image: linear-gradient(top, #fceec1, #eedc94); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0); - text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); - border-color: #eedc94 #eedc94 #e4c652; - border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - border-width: 1px; - border-style: solid; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); - -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25); -} -.alert-message .close { - margin-top: 1px; - *margin-top: 0; -} -.alert-message a { - font-weight: bold; - color: #404040; -} -.alert-message.danger p a, -.alert-message.error p a, -.alert-message.success p a, -.alert-message.info p a { - color: #ffffff; -} -.alert-message h5 { - line-height: 18px; -} -.alert-message p { - margin-bottom: 0; -} -.alert-message div { - margin-top: 5px; - margin-bottom: 2px; - line-height: 28px; -} -.alert-message .btn { - -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); - -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); - box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25); -} -.alert-message.block-message { - background-image: none; - background-color: #fdf5d9; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); - padding: 14px; - border-color: #fceec1; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} -.alert-message.block-message ul, .alert-message.block-message p { - margin-right: 30px; -} -.alert-message.block-message ul { - margin-bottom: 0; -} -.alert-message.block-message li { - color: #404040; -} -.alert-message.block-message .alert-actions { - margin-top: 5px; -} -.alert-message.block-message.error, .alert-message.block-message.success, .alert-message.block-message.info { - color: #404040; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); -} -.alert-message.block-message.error { - background-color: #fddfde; - border-color: #fbc7c6; -} -.alert-message.block-message.success { - background-color: #d1eed1; - border-color: #bfe7bf; -} -.alert-message.block-message.info { - background-color: #ddf4fb; - border-color: #c6edf9; -} -.alert-message.block-message.danger p a, -.alert-message.block-message.error p a, -.alert-message.block-message.success p a, -.alert-message.block-message.info p a { - color: #404040; -} -.pagination { - height: 36px; - margin: 18px 0; -} -.pagination ul { - float: left; - margin: 0; - border: 1px solid #ddd; - border: 1px solid rgba(0, 0, 0, 0.15); - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); -} -.pagination li { - display: inline; -} -.pagination a { - float: left; - padding: 0 14px; - line-height: 34px; - border-right: 1px solid; - border-right-color: #ddd; - border-right-color: rgba(0, 0, 0, 0.15); - *border-right-color: #ddd; - /* IE6-7 */ - - text-decoration: none; -} -.pagination a:hover, .pagination .active a { - background-color: #c7eefe; -} -.pagination .disabled a, .pagination .disabled a:hover { - background-color: transparent; - color: #bfbfbf; -} -.pagination .next a { - border: 0; -} -.well { - background-color: #f5f5f5; - margin-bottom: 20px; - padding: 19px; - min-height: 20px; - border: 1px solid #eee; - border: 1px solid rgba(0, 0, 0, 0.05); - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, 0.15); -} -.modal-backdrop { - background-color: #000000; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 10000; -} -.modal-backdrop.fade { - opacity: 0; -} -.modal-backdrop, .modal-backdrop.fade.in { - filter: alpha(opacity=80); - -khtml-opacity: 0.8; - -moz-opacity: 0.8; - opacity: 0.8; -} -.modal { - position: fixed; - top: 50%; - left: 50%; - z-index: 11000; - width: 560px; - margin: -250px 0 0 -280px; - background-color: #ffffff; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, 0.3); - *border: 1px solid #999; - /* IE6-7 */ - - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - -webkit-background-clip: padding-box; - -moz-background-clip: padding-box; - background-clip: padding-box; -} -.modal .close { - margin-top: 7px; -} -.modal.fade { - -webkit-transition: opacity .3s linear, top .3s ease-out; - -moz-transition: opacity .3s linear, top .3s ease-out; - -ms-transition: opacity .3s linear, top .3s ease-out; - -o-transition: opacity .3s linear, top .3s ease-out; - transition: opacity .3s linear, top .3s ease-out; - top: -25%; -} -.modal.fade.in { - top: 50%; -} -.modal-header { - border-bottom: 1px solid #eee; - padding: 5px 15px; -} -.modal-body { - padding: 15px; -} -.modal-body form { - margin-bottom: 0; -} -.modal-footer { - background-color: #f5f5f5; - padding: 14px 15px 15px; - border-top: 1px solid #ddd; - -webkit-border-radius: 0 0 6px 6px; - -moz-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; - -webkit-box-shadow: inset 0 1px 0 #ffffff; - -moz-box-shadow: inset 0 1px 0 #ffffff; - box-shadow: inset 0 1px 0 #ffffff; - zoom: 1; - margin-bottom: 0; -} -.modal-footer:before, .modal-footer:after { - display: table; - content: ""; - zoom: 1; -} -.modal-footer:after { - clear: both; -} -.modal-footer .btn { - float: right; - margin-left: 5px; -} -.modal .popover, .modal .twipsy { - z-index: 12000; -} -.twipsy { - display: block; - position: absolute; - visibility: visible; - padding: 5px; - font-size: 11px; - z-index: 1000; - filter: alpha(opacity=80); - -khtml-opacity: 0.8; - -moz-opacity: 0.8; - opacity: 0.8; -} -.twipsy.fade.in { - filter: alpha(opacity=80); - -khtml-opacity: 0.8; - -moz-opacity: 0.8; - opacity: 0.8; -} -.twipsy.above .twipsy-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-top: 5px solid #000000; -} -.twipsy.left .twipsy-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-top: 5px solid transparent; - border-bottom: 5px solid transparent; - border-left: 5px solid #000000; -} -.twipsy.below .twipsy-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-bottom: 5px solid #000000; -} -.twipsy.right .twipsy-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-top: 5px solid transparent; - border-bottom: 5px solid transparent; - border-right: 5px solid #000000; -} -.twipsy-inner { - padding: 3px 8px; - background-color: #000000; - color: white; - text-align: center; - max-width: 200px; - text-decoration: none; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -.twipsy-arrow { - position: absolute; - width: 0; - height: 0; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1000; - padding: 5px; - display: none; -} -.popover.above .arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-top: 5px solid #000000; -} -.popover.right .arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-top: 5px solid transparent; - border-bottom: 5px solid transparent; - border-right: 5px solid #000000; -} -.popover.below .arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-bottom: 5px solid #000000; -} -.popover.left .arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-top: 5px solid transparent; - border-bottom: 5px solid transparent; - border-left: 5px solid #000000; -} -.popover .arrow { - position: absolute; - width: 0; - height: 0; -} -.popover .inner { - background: #000000; - background: rgba(0, 0, 0, 0.8); - padding: 3px; - overflow: hidden; - width: 280px; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); - box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); -} -.popover .title { - background-color: #f5f5f5; - padding: 9px 15px; - line-height: 1; - -webkit-border-radius: 3px 3px 0 0; - -moz-border-radius: 3px 3px 0 0; - border-radius: 3px 3px 0 0; - border-bottom: 1px solid #eee; -} -.popover .content { - background-color: #ffffff; - padding: 14px; - -webkit-border-radius: 0 0 3px 3px; - -moz-border-radius: 0 0 3px 3px; - border-radius: 0 0 3px 3px; - -webkit-background-clip: padding-box; - -moz-background-clip: padding-box; - background-clip: padding-box; -} -.popover .content p, .popover .content ul, .popover .content ol { - margin-bottom: 0; -} -.fade { - -webkit-transition: opacity 0.15s linear; - -moz-transition: opacity 0.15s linear; - -ms-transition: opacity 0.15s linear; - -o-transition: opacity 0.15s linear; - transition: opacity 0.15s linear; - opacity: 0; -} -.fade.in { - opacity: 1; -} -.label { - padding: 1px 3px 2px; - font-size: 9.75px; - font-weight: bold; - color: #ffffff; - text-transform: uppercase; - white-space: nowrap; - background-color: #bfbfbf; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.label.important { - background-color: #c43c35; -} -.label.warning { - background-color: #f89406; -} -.label.success { - background-color: #46a546; -} -.label.notice { - background-color: #62cffc; -} -.media-grid { - margin-left: -20px; - margin-bottom: 0; - zoom: 1; -} -.media-grid:before, .media-grid:after { - display: table; - content: ""; - zoom: 1; -} -.media-grid:after { - clear: both; -} -.media-grid li { - display: inline; -} -.media-grid a { - float: left; - padding: 4px; - margin: 0 0 18px 20px; - border: 1px solid #ddd; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); - -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075); -} -.media-grid a img { - display: block; -} -.media-grid a:hover { - border-color: #0069d6; - -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); - -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); - box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25); -} diff --git a/docsite/_themes/bootstrap/theme.conf b/docsite/_themes/bootstrap/theme.conf deleted file mode 100644 index 64f9efd569..0000000000 --- a/docsite/_themes/bootstrap/theme.conf +++ /dev/null @@ -1,5 +0,0 @@ -# Twitter Bootstrap Theme -[theme] -inherit = basic -stylesheet = basic.css -pygments_style = tango diff --git a/docsite/_themes/solar/NEWS.txt b/docsite/_themes/solar/NEWS.txt deleted file mode 100644 index d9743ee67f..0000000000 --- a/docsite/_themes/solar/NEWS.txt +++ /dev/null @@ -1,32 +0,0 @@ -News -==== - -1.3 ---- -* Release date: 2012-11-01. -* Source Code Pro is now used for code samples. -* Reduced font size of pre elements. -* Horizontal rule for header elements. -* HTML pre contents are now wrapped (no scrollbars). -* Changed permalink color from black to a lighter one. - -1.2 ---- -* Release date: 2012-10-03. -* Style additional admonition levels. -* Increase padding for navigation links (minor). -* Add shadow for admonition items (minor). - -1.1 ---- -* Release date: 2012-09-05. -* Add a new background. -* Revert font of headings to Open Sans Light. -* Darker color for h3 - h6. -* Removed dependency on solarized dark pygments style. -* Nice looking scrollbars for pre element. - -1.0 ---- -* Release date: 2012-08-24. -* Initial release. diff --git a/docsite/_themes/solar/README.rst b/docsite/_themes/solar/README.rst deleted file mode 100644 index caeedbd29d..0000000000 --- a/docsite/_themes/solar/README.rst +++ /dev/null @@ -1,28 +0,0 @@ -Solar theme for Python Sphinx -============================= -Solar is an attempt to create a theme for Sphinx based on the `Solarized `_ color scheme. - -Preview -------- -http://vimalkumar.in/sphinx-themes/solar - -Download --------- -Released versions are available from http://github.com/vkvn/sphinx-themes/downloads - -Installation ------------- -#. Extract the archive. -#. Modify ``conf.py`` of an existing Sphinx project or create new project using ``sphinx-quickstart``. -#. Change the ``html_theme`` parameter to ``solar``. -#. Change the ``html_theme_path`` to the location containing the extracted archive. - -License -------- -`GNU General Public License `_. - -Credits -------- -Modified from the default Sphinx theme -- Sphinxdoc - -Background pattern from http://subtlepatterns.com. diff --git a/docsite/_themes/solar/layout.html b/docsite/_themes/solar/layout.html deleted file mode 100644 index 6c57110d41..0000000000 --- a/docsite/_themes/solar/layout.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends "basic/layout.html" %} - -{%- block doctype -%} - -{%- endblock -%} - -{%- block extrahead -%} - - -{%- endblock -%} - -{# put the sidebar before the body #} -{% block sidebar1 %}{{ sidebar() }}{% endblock %} -{% block sidebar2 %}{% endblock %} - -{%- block footer %} - -{%- endblock %} diff --git a/docsite/_themes/solar/static/solar.css b/docsite/_themes/solar/static/solar.css deleted file mode 100644 index 15b5adef71..0000000000 --- a/docsite/_themes/solar/static/solar.css +++ /dev/null @@ -1,344 +0,0 @@ -/* solar.css - * Modified from sphinxdoc.css of the sphinxdoc theme. -*/ - -@import url("basic.css"); - -/* -- page layout ----------------------------------------------------------- */ - -body { - font-family: 'Open Sans', sans-serif; - font-size: 14px; - line-height: 150%; - text-align: center; - color: #002b36; - padding: 0; - margin: 0px 80px 0px 80px; - min-width: 740px; - -moz-box-shadow: 0px 0px 10px #93a1a1; - -webkit-box-shadow: 0px 0px 10px #93a1a1; - box-shadow: 0px 0px 10px #93a1a1; - background: url("subtle_dots.png") repeat; - -} - -div.document { - background-color: #fcfcfc; - text-align: left; - background-repeat: repeat-x; -} - -div.bodywrapper { - margin: 0 240px 0 0; - border-right: 1px dotted #eee8d5; -} - -div.body { - background-color: white; - margin: 0; - padding: 0.5em 20px 20px 20px; -} - -div.related { - font-size: 1em; - background: #002b36; - color: #839496; - padding: 5px 0px; -} - -div.related ul { - height: 2em; - margin: 2px; -} - -div.related ul li { - margin: 0; - padding: 0; - height: 2em; - float: left; -} - -div.related ul li.right { - float: right; - margin-right: 5px; -} - -div.related ul li a { - margin: 0; - padding: 2px 5px; - line-height: 2em; - text-decoration: none; - color: #839496; -} - -div.related ul li a:hover { - background-color: #073642; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - border-radius: 2px; -} - -div.sphinxsidebarwrapper { - padding: 0; -} - -div.sphinxsidebar { - margin: 0; - padding: 0.5em 15px 15px 0; - width: 210px; - float: right; - font-size: 0.9em; - text-align: left; -} - -div.sphinxsidebar h3, div.sphinxsidebar h4 { - margin: 1em 0 0.5em 0; - font-size: 1em; - padding: 0.7em; - background-color: #eeeff1; -} - -div.sphinxsidebar h3 a { - color: #2E3436; -} - -div.sphinxsidebar ul { - padding-left: 1.5em; - margin-top: 7px; - padding: 0; - line-height: 150%; - color: #586e75; -} - -div.sphinxsidebar ul ul { - margin-left: 20px; -} - -div.sphinxsidebar input { - border: 1px solid #eee8d5; -} - -div.footer { - background-color: #93a1a1; - color: #eee; - padding: 3px 8px 3px 0; - clear: both; - font-size: 0.8em; - text-align: right; -} - -div.footer a { - color: #eee; - text-decoration: none; -} - -/* -- body styles ----------------------------------------------------------- */ - -p { - margin: 0.8em 0 0.5em 0; -} - -div.body a, div.sphinxsidebarwrapper a { - color: #268bd2; - text-decoration: none; -} - -div.body a:hover, div.sphinxsidebarwrapper a:hover { - border-bottom: 1px solid #268bd2; -} - -h1, h2, h3, h4, h5, h6 { - font-family: "Open Sans", sans-serif; - font-weight: 300; -} - -h1 { - margin: 0; - padding: 0.7em 0 0.3em 0; - line-height: 1.2em; - color: #002b36; - text-shadow: #eee 0.1em 0.1em 0.1em; -} - -h2 { - margin: 1.3em 0 0.2em 0; - padding: 0 0 10px 0; - color: #073642; - border-bottom: 1px solid #eee; -} - -h3 { - margin: 1em 0 -0.3em 0; - padding-bottom: 5px; -} - -h3, h4, h5, h6 { - color: #073642; - border-bottom: 1px dotted #eee; -} - -div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a { - color: #657B83!important; -} - -h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { - display: none; - margin: 0 0 0 0.3em; - padding: 0 0.2em 0 0.2em; - color: #aaa!important; -} - -h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, -h5:hover a.anchor, h6:hover a.anchor { - display: inline; -} - -h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, -h5 a.anchor:hover, h6 a.anchor:hover { - color: #777; - background-color: #eee; -} - -a.headerlink { - color: #c60f0f!important; - font-size: 1em; - margin-left: 6px; - padding: 0 4px 0 4px; - text-decoration: none!important; -} - -a.headerlink:hover { - background-color: #ccc; - color: white!important; -} - - -cite, code, tt { - font-family: 'Source Code Pro', monospace; - font-size: 0.9em; - letter-spacing: 0.01em; - background-color: #eeeff2; - font-style: normal; -} - -hr { - border: 1px solid #eee; - margin: 2em; -} - -.highlight { - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - border-radius: 2px; -} - -pre { - font-family: 'Source Code Pro', monospace; - font-style: normal; - font-size: 0.9em; - letter-spacing: 0.015em; - line-height: 120%; - padding: 0.7em; - white-space: pre-wrap; /* css-3 */ - white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ -} - -pre a { - color: inherit; - text-decoration: underline; -} - -td.linenos pre { - padding: 0.5em 0; -} - -div.quotebar { - background-color: #f8f8f8; - max-width: 250px; - float: right; - padding: 2px 7px; - border: 1px solid #ccc; -} - -div.topic { - background-color: #f8f8f8; -} - -table { - border-collapse: collapse; - margin: 0 -0.5em 0 -0.5em; -} - -table td, table th { - padding: 0.2em 0.5em 0.2em 0.5em; -} - -div.admonition { - font-size: 0.9em; - margin: 1em 0 1em 0; - border: 1px solid #eee; - background-color: #f7f7f7; - padding: 0; - -moz-box-shadow: 0px 8px 6px -8px #93a1a1; - -webkit-box-shadow: 0px 8px 6px -8px #93a1a1; - box-shadow: 0px 8px 6px -8px #93a1a1; -} - -div.admonition p { - margin: 0.5em 1em 0.5em 1em; - padding: 0.2em; -} - -div.admonition pre { - margin: 0.4em 1em 0.4em 1em; -} - -div.admonition p.admonition-title -{ - margin: 0; - padding: 0.2em 0 0.2em 0.6em; - color: white; - border-bottom: 1px solid #eee8d5; - font-weight: bold; - background-color: #268bd2; -} - -div.warning p.admonition-title, -div.important p.admonition-title { - background-color: #cb4b16; -} - -div.hint p.admonition-title, -div.tip p.admonition-title { - background-color: #859900; -} - -div.caution p.admonition-title, -div.attention p.admonition-title, -div.danger p.admonition-title, -div.error p.admonition-title { - background-color: #dc322f; -} - -div.admonition ul, div.admonition ol { - margin: 0.1em 0.5em 0.5em 3em; - padding: 0; -} - -div.versioninfo { - margin: 1em 0 0 0; - border: 1px solid #eee; - background-color: #DDEAF0; - padding: 8px; - line-height: 1.3em; - font-size: 0.9em; -} - -div.viewcode-block:target { - background-color: #f4debf; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; -} diff --git a/docsite/_themes/solar/static/solarized-dark.css b/docsite/_themes/solar/static/solarized-dark.css deleted file mode 100644 index 6ebb945ce0..0000000000 --- a/docsite/_themes/solar/static/solarized-dark.css +++ /dev/null @@ -1,84 +0,0 @@ -/* solarized dark style for solar theme */ - -/*style pre scrollbar*/ -pre::-webkit-scrollbar, .highlight::-webkit-scrollbar { - height: 0.5em; - background: #073642; -} - -pre::-webkit-scrollbar-thumb { - border-radius: 1em; - background: #93a1a1; -} - -/* pygments style */ -.highlight .hll { background-color: #ffffcc } -.highlight { background: #002B36!important; color: #93A1A1 } -.highlight .c { color: #586E75 } /* Comment */ -.highlight .err { color: #93A1A1 } /* Error */ -.highlight .g { color: #93A1A1 } /* Generic */ -.highlight .k { color: #859900 } /* Keyword */ -.highlight .l { color: #93A1A1 } /* Literal */ -.highlight .n { color: #93A1A1 } /* Name */ -.highlight .o { color: #859900 } /* Operator */ -.highlight .x { color: #CB4B16 } /* Other */ -.highlight .p { color: #93A1A1 } /* Punctuation */ -.highlight .cm { color: #586E75 } /* Comment.Multiline */ -.highlight .cp { color: #859900 } /* Comment.Preproc */ -.highlight .c1 { color: #586E75 } /* Comment.Single */ -.highlight .cs { color: #859900 } /* Comment.Special */ -.highlight .gd { color: #2AA198 } /* Generic.Deleted */ -.highlight .ge { color: #93A1A1; font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #DC322F } /* Generic.Error */ -.highlight .gh { color: #CB4B16 } /* Generic.Heading */ -.highlight .gi { color: #859900 } /* Generic.Inserted */ -.highlight .go { color: #93A1A1 } /* Generic.Output */ -.highlight .gp { color: #93A1A1 } /* Generic.Prompt */ -.highlight .gs { color: #93A1A1; font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #CB4B16 } /* Generic.Subheading */ -.highlight .gt { color: #93A1A1 } /* Generic.Traceback */ -.highlight .kc { color: #CB4B16 } /* Keyword.Constant */ -.highlight .kd { color: #268BD2 } /* Keyword.Declaration */ -.highlight .kn { color: #859900 } /* Keyword.Namespace */ -.highlight .kp { color: #859900 } /* Keyword.Pseudo */ -.highlight .kr { color: #268BD2 } /* Keyword.Reserved */ -.highlight .kt { color: #DC322F } /* Keyword.Type */ -.highlight .ld { color: #93A1A1 } /* Literal.Date */ -.highlight .m { color: #2AA198 } /* Literal.Number */ -.highlight .s { color: #2AA198 } /* Literal.String */ -.highlight .na { color: #93A1A1 } /* Name.Attribute */ -.highlight .nb { color: #B58900 } /* Name.Builtin */ -.highlight .nc { color: #268BD2 } /* Name.Class */ -.highlight .no { color: #CB4B16 } /* Name.Constant */ -.highlight .nd { color: #268BD2 } /* Name.Decorator */ -.highlight .ni { color: #CB4B16 } /* Name.Entity */ -.highlight .ne { color: #CB4B16 } /* Name.Exception */ -.highlight .nf { color: #268BD2 } /* Name.Function */ -.highlight .nl { color: #93A1A1 } /* Name.Label */ -.highlight .nn { color: #93A1A1 } /* Name.Namespace */ -.highlight .nx { color: #93A1A1 } /* Name.Other */ -.highlight .py { color: #93A1A1 } /* Name.Property */ -.highlight .nt { color: #268BD2 } /* Name.Tag */ -.highlight .nv { color: #268BD2 } /* Name.Variable */ -.highlight .ow { color: #859900 } /* Operator.Word */ -.highlight .w { color: #93A1A1 } /* Text.Whitespace */ -.highlight .mf { color: #2AA198 } /* Literal.Number.Float */ -.highlight .mh { color: #2AA198 } /* Literal.Number.Hex */ -.highlight .mi { color: #2AA198 } /* Literal.Number.Integer */ -.highlight .mo { color: #2AA198 } /* Literal.Number.Oct */ -.highlight .sb { color: #586E75 } /* Literal.String.Backtick */ -.highlight .sc { color: #2AA198 } /* Literal.String.Char */ -.highlight .sd { color: #93A1A1 } /* Literal.String.Doc */ -.highlight .s2 { color: #2AA198 } /* Literal.String.Double */ -.highlight .se { color: #CB4B16 } /* Literal.String.Escape */ -.highlight .sh { color: #93A1A1 } /* Literal.String.Heredoc */ -.highlight .si { color: #2AA198 } /* Literal.String.Interpol */ -.highlight .sx { color: #2AA198 } /* Literal.String.Other */ -.highlight .sr { color: #DC322F } /* Literal.String.Regex */ -.highlight .s1 { color: #2AA198 } /* Literal.String.Single */ -.highlight .ss { color: #2AA198 } /* Literal.String.Symbol */ -.highlight .bp { color: #268BD2 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #268BD2 } /* Name.Variable.Class */ -.highlight .vg { color: #268BD2 } /* Name.Variable.Global */ -.highlight .vi { color: #268BD2 } /* Name.Variable.Instance */ -.highlight .il { color: #2AA198 } /* Literal.Number.Integer.Long */ diff --git a/docsite/_themes/solar/static/subtle_dots.png b/docsite/_themes/solar/static/subtle_dots.png deleted file mode 100644 index bb2d6117e5e207e79aa312e26cb3d7ff0dcfdb04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1025 zcmaJ=&ui0A9DiMRn4@EZiMV<2k$DoEUu(N2wAQw1ZDARu>##SMCa-JQk{6TLH5-Ud zgt41~7mwam^d`zwh8GVWJScO2fH(0Vc+iV4UE6uE1|E6eFQ3on`}6Hpmaa~W&yE8C zCdwtHO8@8RnH(DdAoT6$^b9p-NTE)aag%g37lEvS8wi#yZ4FhCW^CPfk5T}H8M9U= zb#+nJvBhcuhV?CnvH?hCd`HvQ5rGY~X4+}y`>Qt$G>tTKEw1valSeJHwC$qh?WLN& zy{=0Jler92zDxxyL^SAI8@4C=X{M_y(|h3N7}$l7^)z#wR9&sWJa!R`vwT$N69P=c zSRuwsG3gQ%d4cByk>eMl0x!oGWL|*1kD<|AqbXOFVlNi8(oBmGN9MRrr^9yUS?sQH zf+R^CFLI(7rHH7vWfRSh+TL7WK|!AGnhr6s4Fg53f!id_&`gg@u$+Oc?e*G38^-yX z!wD=Ol+*{R>i?mZH9&i$ica$Vr?6Msau8QV9&Woj9b9uR2<6Cm7ik2$HH5*;WCgkACNhdHwqUnp*&LpWFjTL{HHPOR z4&`z*87>&?UxV(u==KEH!P-(|u=vQP+wRh}KA+mzqfeq;Ri8=5AfL KCFN;u_0At+f;@fz diff --git a/docsite/_themes/solar/theme.conf b/docsite/_themes/solar/theme.conf deleted file mode 100644 index d8fc2f35d9..0000000000 --- a/docsite/_themes/solar/theme.conf +++ /dev/null @@ -1,4 +0,0 @@ -[theme] -inherit = basic -stylesheet = solar.css -pygments_style = none From 041cea134f012ae3bcf1664f1a2cd8aa695ff6ad Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Tue, 24 Dec 2013 17:29:28 -0500 Subject: [PATCH 025/182] New Sphinx theme has sidebar navigation so don't need TOC on each page. --- docsite/rst/YAMLSyntax.rst | 3 --- docsite/rst/developing_api.rst | 3 --- docsite/rst/developing_inventory.rst | 3 --- docsite/rst/developing_modules.rst | 3 --- docsite/rst/developing_plugins.rst | 3 --- docsite/rst/faq.rst | 3 --- docsite/rst/glossary.rst | 3 --- docsite/rst/guide_aws.rst | 3 --- docsite/rst/guide_rax.rst | 3 --- docsite/rst/guide_rolling_upgrade.rst | 3 --- docsite/rst/guide_vagrant.rst | 3 --- docsite/rst/intro_adhoc.rst | 3 --- docsite/rst/intro_configuration.rst | 3 --- docsite/rst/intro_dynamic_inventory.rst | 3 --- docsite/rst/intro_getting_started.rst | 3 --- docsite/rst/intro_installation.rst | 3 --- docsite/rst/intro_inventory.rst | 3 --- docsite/rst/intro_patterns.rst | 3 --- docsite/rst/playbooks.rst | 3 --- docsite/rst/playbooks_acceleration.rst | 3 --- docsite/rst/playbooks_async.rst | 3 --- docsite/rst/playbooks_best_practices.rst | 3 --- docsite/rst/playbooks_checkmode.rst | 3 --- docsite/rst/playbooks_conditionals.rst | 3 --- docsite/rst/playbooks_delegation.rst | 3 --- docsite/rst/playbooks_environment.rst | 3 --- docsite/rst/playbooks_error_handling.rst | 3 --- docsite/rst/playbooks_lookups.rst | 3 --- docsite/rst/playbooks_loops.rst | 3 --- docsite/rst/playbooks_prompts.rst | 3 --- docsite/rst/playbooks_roles.rst | 3 --- docsite/rst/playbooks_tags.rst | 3 --- docsite/rst/playbooks_variables.rst | 3 --- 33 files changed, 99 deletions(-) diff --git a/docsite/rst/YAMLSyntax.rst b/docsite/rst/YAMLSyntax.rst index 54874b6dde..3230a39f24 100644 --- a/docsite/rst/YAMLSyntax.rst +++ b/docsite/rst/YAMLSyntax.rst @@ -1,9 +1,6 @@ YAML Syntax =========== -.. contents:: - :depth: 2 - This page provides a basic overview of correct YAML syntax, which is how Ansible playbooks (our configuration management language) are expressed. diff --git a/docsite/rst/developing_api.rst b/docsite/rst/developing_api.rst index 95042574b2..da5ee9b1c8 100644 --- a/docsite/rst/developing_api.rst +++ b/docsite/rst/developing_api.rst @@ -13,9 +13,6 @@ as it has a very nice REST API that provides all of these things at a higher lev Ansible is written in its own API so you have a considerable amount of power across the board. This chapter discusses the Python API. -.. contents:: `Table of contents` - :depth: 2 - .. _python_api: Python API diff --git a/docsite/rst/developing_inventory.rst b/docsite/rst/developing_inventory.rst index 4a5af3012c..5fd8678517 100644 --- a/docsite/rst/developing_inventory.rst +++ b/docsite/rst/developing_inventory.rst @@ -1,9 +1,6 @@ Developing Dynamic Inventory Sources ==================================== -.. contents:: `Table of contents` - :depth: 2 - As described in `intro_inventory_dynamic`, ansible can pull inventory information from dynamic sources, including cloud sources. How do we write a new one? diff --git a/docsite/rst/developing_modules.rst b/docsite/rst/developing_modules.rst index 5f9037f61e..1d5e9e4f81 100644 --- a/docsite/rst/developing_modules.rst +++ b/docsite/rst/developing_modules.rst @@ -13,9 +13,6 @@ Should you develop an interesting Ansible module, consider sending a pull reques `github project `_ to see about getting your module included in the core project. -.. contents:: - :depth: 2 - .. _module_dev_tutorial: Tutorial diff --git a/docsite/rst/developing_plugins.rst b/docsite/rst/developing_plugins.rst index 2829874d79..3e967f3136 100644 --- a/docsite/rst/developing_plugins.rst +++ b/docsite/rst/developing_plugins.rst @@ -1,9 +1,6 @@ Developing Plugins ================== -.. contents:: - :depth: 2 - Ansible is pluggable in a lot of other ways separate from inventory scripts and callbacks. Many of these features are there to cover fringe use cases and are infrequently needed, and others are pluggable simply because they are there to implement core features in ansible and were most convenient to be made pluggable. diff --git a/docsite/rst/faq.rst b/docsite/rst/faq.rst index 17653ee082..9b8631d96a 100644 --- a/docsite/rst/faq.rst +++ b/docsite/rst/faq.rst @@ -3,9 +3,6 @@ Frequently Asked Questions Here are some commonly-asked questions and their answers. -.. contents:: - :depth: 2 - .. _users_and_ports: How do I handle different machines needing different user accounts or ports to log in with? diff --git a/docsite/rst/glossary.rst b/docsite/rst/glossary.rst index d64c26633e..8c10086b15 100644 --- a/docsite/rst/glossary.rst +++ b/docsite/rst/glossary.rst @@ -1,9 +1,6 @@ Glossary ======== -.. contents:: - :depth: 2 - The following is a list (and re-explanation) of term definitions used elsewhere in the Ansible documentation. Consult the documentation home page for the full documentation and to see the terms in context, but this should be a good resource diff --git a/docsite/rst/guide_aws.rst b/docsite/rst/guide_aws.rst index 0573528548..47ebdbb23c 100644 --- a/docsite/rst/guide_aws.rst +++ b/docsite/rst/guide_aws.rst @@ -1,9 +1,6 @@ Amazon Web Services Guide ========================= -.. contents:: - :depth: 2 - .. _aws_intro: Introduction diff --git a/docsite/rst/guide_rax.rst b/docsite/rst/guide_rax.rst index 40722c2ef3..762c700981 100644 --- a/docsite/rst/guide_rax.rst +++ b/docsite/rst/guide_rax.rst @@ -1,9 +1,6 @@ Rackspace Cloud Guide ===================== -.. contents:: - :depth: 3 - .. _introduction: Introduction diff --git a/docsite/rst/guide_rolling_upgrade.rst b/docsite/rst/guide_rolling_upgrade.rst index e578006f7b..bdaf1939ab 100644 --- a/docsite/rst/guide_rolling_upgrade.rst +++ b/docsite/rst/guide_rolling_upgrade.rst @@ -1,9 +1,6 @@ Continuous Delivery and Rolling Upgrades ======================================== -.. contents:: - :depth: 2 - .. _lamp_introduction: Introduction diff --git a/docsite/rst/guide_vagrant.rst b/docsite/rst/guide_vagrant.rst index 899735de85..4fb40d569f 100644 --- a/docsite/rst/guide_vagrant.rst +++ b/docsite/rst/guide_vagrant.rst @@ -1,9 +1,6 @@ Using Vagrant and Ansible ========================= -.. contents:: - :depth: 2 - .. _vagrant_intro: Introduction diff --git a/docsite/rst/intro_adhoc.rst b/docsite/rst/intro_adhoc.rst index 25b38a1583..7ed54f2dbb 100644 --- a/docsite/rst/intro_adhoc.rst +++ b/docsite/rst/intro_adhoc.rst @@ -31,9 +31,6 @@ port over directly to the playbook language. If you haven't read :doc:`intro_inventory` already, please look that over a bit first and then we'll get going. -.. contents:: - :depth: 2 - .. _parallelism_and_shell_commands: Parallelism and Shell Commands diff --git a/docsite/rst/intro_configuration.rst b/docsite/rst/intro_configuration.rst index f9eb3c9347..543b11cc48 100644 --- a/docsite/rst/intro_configuration.rst +++ b/docsite/rst/intro_configuration.rst @@ -15,9 +15,6 @@ The mechanism for doing this is the "ansible.cfg" file, which is looked for in t If multiple file locations matching the above exist, the last location on the above list is used. Settings in files are not merged together. -.. contents:: - :depth: 2 - .. _getting_the_latest_configuration: Getting the latest configuration diff --git a/docsite/rst/intro_dynamic_inventory.rst b/docsite/rst/intro_dynamic_inventory.rst index 52de7eb9a5..d1aa4cae5b 100644 --- a/docsite/rst/intro_dynamic_inventory.rst +++ b/docsite/rst/intro_dynamic_inventory.rst @@ -16,9 +16,6 @@ Ansible easily supports all of these options via an external inventory system. For information about writing your own dynamic inventory source, see :doc:`developing_inventory`. -.. contents:: - :depth: 2 - .. _cobbler_example: diff --git a/docsite/rst/intro_getting_started.rst b/docsite/rst/intro_getting_started.rst index f0f7b48cea..fc0c651951 100644 --- a/docsite/rst/intro_getting_started.rst +++ b/docsite/rst/intro_getting_started.rst @@ -1,9 +1,6 @@ Getting Started =============== -.. contents:: - :depth: 2 - .. _gs_about: About diff --git a/docsite/rst/intro_installation.rst b/docsite/rst/intro_installation.rst index 4ef2d23ef9..61ff6a6fe3 100644 --- a/docsite/rst/intro_installation.rst +++ b/docsite/rst/intro_installation.rst @@ -1,9 +1,6 @@ Installation ============ -.. contents:: - :depth: 2 - .. _getting_ansible: Getting Ansible diff --git a/docsite/rst/intro_inventory.rst b/docsite/rst/intro_inventory.rst index 3812ad35b8..5d22464776 100644 --- a/docsite/rst/intro_inventory.rst +++ b/docsite/rst/intro_inventory.rst @@ -12,9 +12,6 @@ Not only is this inventory configurable, but you can also use multiple inventory files at the same time (explained below) and also pull inventory from dynamic or cloud sources, as described in :doc:`intro_dynamic_inventory`. -.. contents:: - :depth: 2 - .. _inventoryformat: Hosts and Groups diff --git a/docsite/rst/intro_patterns.rst b/docsite/rst/intro_patterns.rst index f46a5f5a30..4504ef21ef 100644 --- a/docsite/rst/intro_patterns.rst +++ b/docsite/rst/intro_patterns.rst @@ -1,9 +1,6 @@ Patterns ++++++++ -.. contents:: - :depth: 2 - Patterns in Ansible are how we decide which hosts to manage. This can mean what hosts to communicate with, but in terms of :doc:`playbooks` it actually means what hosts to apply a particular configuration or IT process to. diff --git a/docsite/rst/playbooks.rst b/docsite/rst/playbooks.rst index 6aa5f0cae6..b478ac5a60 100644 --- a/docsite/rst/playbooks.rst +++ b/docsite/rst/playbooks.rst @@ -1,9 +1,6 @@ Intro to Playbooks ================== -.. contents:: - :depth: 2 - .. _about_playbooks: About Playbooks diff --git a/docsite/rst/playbooks_acceleration.rst b/docsite/rst/playbooks_acceleration.rst index 52512c9e1d..2160cf6581 100644 --- a/docsite/rst/playbooks_acceleration.rst +++ b/docsite/rst/playbooks_acceleration.rst @@ -1,9 +1,6 @@ Accelerated Mode ================ -.. contents:: - :depth: 2 - .. versionadded:: 1.3 While OpenSSH using the ControlPersist feature is quite fast and scalable, there is a certain small amount of overhead involved in diff --git a/docsite/rst/playbooks_async.rst b/docsite/rst/playbooks_async.rst index 3a7013564a..d94a6986ba 100644 --- a/docsite/rst/playbooks_async.rst +++ b/docsite/rst/playbooks_async.rst @@ -1,9 +1,6 @@ Asynchronous Actions and Polling ================================ -.. contents:: - :depth: 2 - By default tasks in playbooks block, meaning the connections stay open until the task is done on each node. This may not always be desirable, or you may be running operations that take longer than the SSH timeout. diff --git a/docsite/rst/playbooks_best_practices.rst b/docsite/rst/playbooks_best_practices.rst index 49d14be55b..275a4d3ea5 100644 --- a/docsite/rst/playbooks_best_practices.rst +++ b/docsite/rst/playbooks_best_practices.rst @@ -5,9 +5,6 @@ Here are some tips for making the most of Ansible playbooks. You can find some example playbooks illustrating these best practices in our `ansible-examples repository `_. (NOTE: These may not use all of the features in the latest release, but are still an excellent reference!). -.. contents:: - :depth: 2 - .. _content_organization: Content Organization diff --git a/docsite/rst/playbooks_checkmode.rst b/docsite/rst/playbooks_checkmode.rst index 555f682494..40ee15ff15 100644 --- a/docsite/rst/playbooks_checkmode.rst +++ b/docsite/rst/playbooks_checkmode.rst @@ -1,9 +1,6 @@ Check Mode ("Dry Run") ====================== -.. contents:: - :depth: 2 - .. versionadded:: 1.1 When ansible-playbook is executed with --check it will not make any changes on remote systems. Instead, any module diff --git a/docsite/rst/playbooks_conditionals.rst b/docsite/rst/playbooks_conditionals.rst index 84b6f1b135..ab5f1e08ef 100644 --- a/docsite/rst/playbooks_conditionals.rst +++ b/docsite/rst/playbooks_conditionals.rst @@ -1,9 +1,6 @@ Conditionals ============ -.. contents:: - :depth: 2 - Often the result of a play may depend on the value of a variable, fact (something learned about the remote system), or previous task result. In some cases, the values of variables may depend on other variables. Further, additional groups can be created to manage hosts based on diff --git a/docsite/rst/playbooks_delegation.rst b/docsite/rst/playbooks_delegation.rst index 440228139a..86e67902c7 100644 --- a/docsite/rst/playbooks_delegation.rst +++ b/docsite/rst/playbooks_delegation.rst @@ -1,9 +1,6 @@ Delegation, Rolling Updates, and Local Actions ============================================== -.. contents:: - :depth: 2 - Being designed for multi-tier deployments since the beginning, Ansible is great at doing things on one host on behalf of another, or doing local steps with reference to some remote hosts. This in particular this is very applicable when setting up continuous deployment infrastructure or zero downtime rolling updates, where you might be talking with load balancers or monitoring systems. diff --git a/docsite/rst/playbooks_environment.rst b/docsite/rst/playbooks_environment.rst index 4d02013a3b..971765ab30 100644 --- a/docsite/rst/playbooks_environment.rst +++ b/docsite/rst/playbooks_environment.rst @@ -1,9 +1,6 @@ Setting the Environment (and Working With Proxies) ================================================== -.. contents:: - :depth: 2 - .. versionadded:: 1.1 It is quite possible that you may need to get package updates through a proxy, or even get some package diff --git a/docsite/rst/playbooks_error_handling.rst b/docsite/rst/playbooks_error_handling.rst index d64917b05d..e4c5e413fe 100644 --- a/docsite/rst/playbooks_error_handling.rst +++ b/docsite/rst/playbooks_error_handling.rst @@ -1,9 +1,6 @@ Error Handling In Playbooks =========================== -.. contents:: - :depth: 2 - Ansible normally has defaults that make sure to check the return codes of commands and modules and it fails fast -- forcing an error to be dealt with unless you decide otherwise. diff --git a/docsite/rst/playbooks_lookups.rst b/docsite/rst/playbooks_lookups.rst index a38232f5f9..d14645476e 100644 --- a/docsite/rst/playbooks_lookups.rst +++ b/docsite/rst/playbooks_lookups.rst @@ -1,9 +1,6 @@ Using Lookups ============= -.. contents:: - :depth: 2 - Lookup plugins allow access of data in Ansible from outside sources. This can include the filesystem but also external datastores. These values are then made available using the standard templating system in Ansible, and are typically used to load variables or templates with information from those systems. diff --git a/docsite/rst/playbooks_loops.rst b/docsite/rst/playbooks_loops.rst index cb766e5d7f..9b73c74473 100644 --- a/docsite/rst/playbooks_loops.rst +++ b/docsite/rst/playbooks_loops.rst @@ -1,9 +1,6 @@ Loops ===== -.. contents:: - :depth: 2 - Often you'll want to do many things in one task, such as create a lot of users, install a lot of packages, or repeat a polling step until a certain result is reached. diff --git a/docsite/rst/playbooks_prompts.rst b/docsite/rst/playbooks_prompts.rst index 427420d9d1..30fa51a843 100644 --- a/docsite/rst/playbooks_prompts.rst +++ b/docsite/rst/playbooks_prompts.rst @@ -1,9 +1,6 @@ Prompts ======= -.. contents:: - :depth: 2 - When running a playbook, you may wish to prompt the user for certain input, and can do so with the 'vars_prompt' section. diff --git a/docsite/rst/playbooks_roles.rst b/docsite/rst/playbooks_roles.rst index 2ac055b42e..4af68eb64c 100644 --- a/docsite/rst/playbooks_roles.rst +++ b/docsite/rst/playbooks_roles.rst @@ -1,9 +1,6 @@ Playbook Roles and Include Statements ===================================== -.. contents:: - :depth: 2 - Introduction ```````````` diff --git a/docsite/rst/playbooks_tags.rst b/docsite/rst/playbooks_tags.rst index 600ffa584e..c48ced2090 100644 --- a/docsite/rst/playbooks_tags.rst +++ b/docsite/rst/playbooks_tags.rst @@ -1,9 +1,6 @@ Tags ==== -.. contents:: - :depth: 2 - If you have a large playbook it may become useful to be able to run a specific part of the configuration without running the whole playbook. diff --git a/docsite/rst/playbooks_variables.rst b/docsite/rst/playbooks_variables.rst index 729affe562..8e17d68c83 100644 --- a/docsite/rst/playbooks_variables.rst +++ b/docsite/rst/playbooks_variables.rst @@ -21,9 +21,6 @@ and the "when" conditional can also be used with variables, and to help manage d It's highly recommended that you consult the ansible-examples github repository to see a lot of examples of variables put to use. -.. contents:: - :depth: 2 - .. _valid_variable_names: What Makes A Valid Variable Name From 5b5556020fc0901318cba3c5abee7b1e3f09b315 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Tue, 24 Dec 2013 17:51:16 -0500 Subject: [PATCH 026/182] Reorg of the documentation tree such that the sidebar will be more correct. Some work yet to do on module docs. --- docsite/rst/developing.rst | 16 + docsite/rst/guides.rst | 15 + docsite/rst/index.rst | 146 +-------- docsite/rst/intro.rst | 17 + docsite/rst/playbooks.rst | 400 +---------------------- docsite/rst/playbooks_special_topics.rst | 19 ++ 6 files changed, 93 insertions(+), 520 deletions(-) create mode 100644 docsite/rst/developing.rst create mode 100644 docsite/rst/guides.rst create mode 100644 docsite/rst/intro.rst create mode 100644 docsite/rst/playbooks_special_topics.rst diff --git a/docsite/rst/developing.rst b/docsite/rst/developing.rst new file mode 100644 index 0000000000..cc8b03d30f --- /dev/null +++ b/docsite/rst/developing.rst @@ -0,0 +1,16 @@ +Developer Information +````````````````````` + +Learn how to build modules of your own in any language, and also how to extend Ansible through several kinds of plugins. Explore Ansible's Python API and write Python plugins to integrate with other solutions in your environment. + +.. toctree:: + :maxdepth: 1 + + developing_api + developing_inventory + developing_modules + developing_plugins + +Developers will also likely be interested in the fully-discoverable `REST API `_ that is part of AnsibleWorks AWX. It's great for embedding Ansible in all manner of applications. + + diff --git a/docsite/rst/guides.rst b/docsite/rst/guides.rst new file mode 100644 index 0000000000..05af9b023d --- /dev/null +++ b/docsite/rst/guides.rst @@ -0,0 +1,15 @@ +Detailed Guides +``````````````` + +This section is new and evolving. The idea here is explore particular use cases in greater depth and provide a more "top down" explanation of some basic features. + +.. toctree:: + :maxdepth: 1 + + guide_aws + guide_rax + guide_vagrant + guide_rolling_upgrade + +Pending topics may include: Docker, Jenkins, Google Compute Engine, Linode/Digital Ocean, Continous Deployment, and more. + diff --git a/docsite/rst/index.rst b/docsite/rst/index.rst index e3408e9f02..a50ae25ec4 100644 --- a/docsite/rst/index.rst +++ b/docsite/rst/index.rst @@ -22,98 +22,18 @@ This documentation covers the current released version of Ansible (1.4.3) and al .. _an_introduction: -The Basics -`````````` - -Before we dive into the really fun parts -- playbooks, configuration management, deployment, and orchestration, we'll learn how to get Ansible installed and some basic concepts. We'll go over how to execute ad-hoc commands in parallel across your nodes using /usr/bin/ansible. We'll also see what sort of modules are available in Ansible's core (though you can also write your own, which we'll also show later). - .. toctree:: - :maxdepth: 1 - - intro_installation - intro_getting_started - intro_inventory - intro_dynamic_inventory - intro_patterns - intro_adhoc - intro_configuration - -Modules -``````` - -Ansible modules are resources that are distributed to remote nodes to make them perform particular tasks or match a particular -state. Ansible follows a "batteries included" philosophy, so you have a lot of great modules for all manner of -IT tasks in the core distribution. This means modules are well up-to-date and you don't have to hunt for an implementation -that will work on your platform. You may think of the module library as a toolbox full of useful system management tools, -and playbooks as the instructions for building something using those tools. - -.. toctree:: - :maxdepth: 1 - - modules - -.. _overview: - -Architecture Diagram -```````````````````` - -.. image:: http://www.ansibleworks.com/wp-content/uploads/2013/06/ANSIBLE_DIAGRAM.jpg - :alt: ansible architecture diagram - :width: 788px - :height: 436px - -.. _introduction_to_playbooks: - -Playbooks -````````` - -Playbooks are Ansible's configuration, deployment, and orchestration language. They can describe a policy you want your remote systems to enforce, or a set of steps in a general IT process. - -If Ansible modules are the tools in your workshop, playbooks are your design plans. - -At a basic level, playbooks can be used to manage configurations of and deployments to remote machines. At a more advanced level, they can sequence multi-tier rollouts involving rolling updates, and can delegate actions to other hosts, interacting with monitoring servers and load balancers along the way. - -While there's a lot of information here, there's no need to learn everything at once. You can start small and pick up more features -over time as you need them. - -Playbooks are designed to be human-readable and are developed in a basic text language. There are multiple -ways to organize playbooks and the files they include, and we'll offer up some suggestions on that and making the most out of Ansible. - -It is recommended to look at `Example Playbooks `_ while reading along with the playbook documentation. These illustrate best practices as well as how to put many of the various concepts together. - -.. toctree:: - :maxdepth: 1 + :maxdepth: 2 + intro playbooks - playbooks_roles - playbooks_variables - playbooks_conditionals - playbooks_loops - playbooks_best_practices - -.. _advanced_topics_in_playbooks: - -Special Topics In Playbooks -``````````````````````````` - -Here are some playbook features that not everyone may need to learn, but can be quite useful for particular applications. -Browsing these topics is recommended as you may find some useful tips here, but feel free to learn the basics of Ansible first -and adopt these only if they seem relevant or useful to your environment. - -.. toctree:: - :maxdepth: 1 - - playbooks_acceleration - playbooks_async - playbooks_checkmode - playbooks_delegation - playbooks_environment - playbooks_error_handling - playbooks_lookups - playbooks_prompts - playbooks_tags - -.. _ansibleworks_awx: + playbooks_special_topics + modules + guides + developing + faq + glossary + YAMLSyntax AnsibleWorks AWX ```````````````` @@ -145,23 +65,6 @@ Read the "About" page on the Galaxy site for more information. .. _detailed_guides: -Detailed Guides -``````````````` - -This section is new and evolving. The idea here is explore particular use cases in greater depth and provide a more "top down" explanation of some basic features. - -.. toctree:: - :maxdepth: 1 - - guide_aws - guide_rax - guide_vagrant - guide_rolling_upgrade - -Pending topics may include: Docker, Jenkins, Google Compute Engine, Linode/Digital Ocean, Continous Deployment, and more. - -.. _community_information: - Community Information ````````````````````` @@ -170,35 +73,4 @@ IT automation solutions that work well for them. Should you wish to get more i `Ways to interact `_ -.. _developer_information: - -Developer Information -````````````````````` - -Learn how to build modules of your own in any language, and also how to extend Ansible through several kinds of plugins. Explore Ansible's Python API and write Python plugins to integrate with other solutions in your environment. - -.. toctree:: - :maxdepth: 1 - - developing_api - developing_inventory - developing_modules - developing_plugins - -Developers will also likely be interested in the fully-discoverable `REST API `_ that is part of AnsibleWorks AWX. It's great for embedding Ansible in all manner of applications. - -.. _misc: - -Miscellaneous -````````````` - -Some additional topics you may be interested in: - -.. toctree:: - :maxdepth: 1 - - faq - glossary - YAMLSyntax - diff --git a/docsite/rst/intro.rst b/docsite/rst/intro.rst new file mode 100644 index 0000000000..20518529e5 --- /dev/null +++ b/docsite/rst/intro.rst @@ -0,0 +1,17 @@ +Introduction +============ + +Before we dive into the really fun parts -- playbooks, configuration management, deployment, and orchestration, we'll learn how to get Ansible installed and some basic concepts. We'll go over how to execute ad-hoc commands in parallel across your nodes using /usr/bin/ansible. We'll also see what sort of modules are available in Ansible's core (though you can also write your own, which we'll also show later). + +.. toctree:: + :maxdepth: 1 + + intro_installation + intro_getting_started + intro_inventory + intro_dynamic_inventory + intro_patterns + intro_adhoc + intro_configuration + + diff --git a/docsite/rst/playbooks.rst b/docsite/rst/playbooks.rst index b478ac5a60..9fe8a8a661 100644 --- a/docsite/rst/playbooks.rst +++ b/docsite/rst/playbooks.rst @@ -1,393 +1,27 @@ -Intro to Playbooks -================== +Playbooks +````````` -.. _about_playbooks: +Playbooks are Ansible's configuration, deployment, and orchestration language. They can describe a policy you want your remote systems to enforce, or a set of steps in a general IT process. -About Playbooks -``````````````` +If Ansible modules are the tools in your workshop, playbooks are your design plans. -Playbooks are a completely different way to use ansible than in adhoc task execution mode, and are -particularly powerful. +At a basic level, playbooks can be used to manage configurations of and deployments to remote machines. At a more advanced level, they can sequence multi-tier rollouts involving rolling updates, and can delegate actions to other hosts, interacting with monitoring servers and load balancers along the way. -Simply put, playbooks are the basis for a really simple configuration management and multi-machine deployment system, -unlike any that already exist, and one that is very well suited to deploying complex applications. +While there's a lot of information here, there's no need to learn everything at once. You can start small and pick up more features +over time as you need them. -Playbooks can declare configurations, but they can also orchestrate steps of -any manual ordered process, even as different steps must bounce back and forth -between sets of machines in particular orders. They can launch tasks -synchronously or asynchronously. +Playbooks are designed to be human-readable and are developed in a basic text language. There are multiple +ways to organize playbooks and the files they include, and we'll offer up some suggestions on that and making the most out of Ansible. -While you might run the main /usr/bin/ansible program for ad-hoc -tasks, playbooks are more likely to be kept in source control and used -to push out your configuration or assure the configurations of your -remote systems are in spec. +It is recommended to look at `Example Playbooks `_ while reading along with the playbook documentation. These illustrate best practices as well as how to put many of the various concepts together. -There are also some full sets of playbooks illustrating a lot of these techniques in the -`ansible-examples repository `_. We'd recommend -looking at these in another tab as you go along. +.. toctree:: + :maxdepth: 1 -There are also many jumping off points after you learn playbooks, so hop back to the documentation -index after you're done with this section. - -.. _playbook_language_example: - -Playbook Language Example -````````````````````````` - -Playbooks are expressed in YAML format (see :doc:`YAMLSyntax`) and have a minimum of syntax, which intentionally -tries to not be a programming language or script, but rather a model of a configuration or a process. - -Each playbook is composed of one or more 'plays' in a list. - -The goal of a play is to map a group of hosts to some well defined roles, represented by -things ansible calls tasks. At a basic level, a task is nothing more than a call -to an ansible module, which you should have learned about in earlier chapters. - -By composing a playbook of multiple 'plays', it is possible to -orchestrate multi-machine deployments, running certain steps on all -machines in the webservers group, then certain steps on the database -server group, then more commands back on the webservers group, etc. - -"plays" are more or less a sports analogy. You can have quite a lot of plays that affect your systems -to do different things. It's not as if you were just defining one particular state or model, and you -can run different plays at different times. - -For starters, here's a playbook that contains just one play:: - - --- - - hosts: webservers - vars: - http_port: 80 - max_clients: 200 - remote_user: root - tasks: - - name: ensure apache is at the latest version - yum: pkg=httpd state=latest - - name: write the apache config file - template: src=/srv/httpd.j2 dest=/etc/httpd.conf - notify: - - restart apache - - name: ensure apache is running - service: name=httpd state=started - handlers: - - name: restart apache - service: name=httpd state=restarted - -Below, we'll break down what the various features of the playbook language are. - -.. _playbook_basics: - -Basics -`````` - -.. _playbook_hosts_and_users: - -Hosts and Users -+++++++++++++++ - -For each play in a playbook, you get to choose which machines in your infrastructure -to target and what remote user to complete the steps (called tasks) as. - -The `hosts` line is a list of one or more groups or host patterns, -separated by colons, as described in the :doc:`intro_patterns` -documentation. The `remote_user` is just the name of the user account:: - - --- - - hosts: webservers - remote_user: root - -.. Note:: - - The `remote_user` parameter was formerly called just `user`. It was renamed in Ansible 1.4 to make it more distinguishable from the `user` module (used to create users on remote systems). - -Remote users can also be defined per task:: - - --- - - hosts: webservers - remote_user: root - tasks: - - name: test connection - ping: - remote_user: yourname - -.. Note:: - - The `remote_user` parameter for tasks was added in 1.4. - - -Support for running things from sudo is also available:: - - --- - - hosts: webservers - remote_user: yourname - sudo: yes - -You can also use sudo on a particular task instead of the whole play:: - - --- - - hosts: webservers - remote_user: yourname - tasks: - - service: name=nginx state=started - sudo: yes - - -You can also login as you, and then sudo to different users than root:: - - --- - - hosts: webservers - remote_user: yourname - sudo: yes - sudo_user: postgres - -If you need to specify a password to sudo, run `ansible-playbook` with ``--ask-sudo-pass`` (`-K`). -If you run a sudo playbook and the playbook seems to hang, it's probably stuck at the sudo prompt. -Just `Control-C` to kill it and run it again with `-K`. - -.. important:: - - When using `sudo_user` to a user other than root, the module - arguments are briefly written into a random tempfile in /tmp. - These are deleted immediately after the command is executed. This - only occurs when sudoing from a user like 'bob' to 'timmy', not - when going from 'bob' to 'root', or logging in directly as 'bob' or - 'root'. If this concerns you that this data is briefly readable - (not writable), avoid transferring uncrypted passwords with - `sudo_user` set. In other cases, '/tmp' is not used and this does - not come into play. Ansible also takes care to not log password - parameters. - -.. _tasks_list: - -Tasks list -++++++++++ - -Each play contains a list of tasks. Tasks are executed in order, one -at a time, against all machines matched by the host pattern, -before moving on to the next task. It is important to understand that, within a play, -all hosts are going to get the same task directives. It is the purpose of a play to map -a selection of hosts to tasks. - -When running the playbook, which runs top to bottom, hosts with failed tasks are -taken out of the rotation for the entire playbook. If things fail, simply correct the playbook file and rerun. - -The goal of each task is to execute a module, with very specific arguments. -Variables, as mentioned above, can be used in arguments to modules. - -Modules are 'idempotent', meaning if you run them -again, they will make only the changes they must in order to bring the -system to the desired state. This makes it very safe to rerun -the same playbook multiple times. They won't change things -unless they have to change things. - -The `command` and `shell` modules will typically rerun the same command again, -which is totally ok if the command is something like -'chmod' or 'setsebool', etc. Though there is a 'creates' flag available which can -be used to make these modules also idempotent. - -Every task should have a `name`, which is included in the output from -running the playbook. This is output for humans, so it is -nice to have reasonably good descriptions of each task step. If the name -is not provided though, the string fed to 'action' will be used for -output. - -Tasks can be declared using the legacy "action: module options" format, but -it is recommended that you use the more conventional "module: options" format. -This recommended format is used throughout the documentation, but you may -encounter the older format in some playbooks. - -Here is what a basic task looks like, as with most modules, -the service module takes key=value arguments:: - - tasks: - - name: make sure apache is running - service: name=httpd state=running - -The `command` and `shell` modules are the one modules that just takes a list -of arguments, and don't use the key=value form. This makes -them work just like you would expect. Simple:: - - tasks: - - name: disable selinux - command: /sbin/setenforce 0 - -The command and shell module care about return codes, so if you have a command -whose successful exit code is not zero, you may wish to do this:: - - tasks: - - name: run this command and ignore the result - shell: /usr/bin/somecommand || /bin/true - -Or this:: - - tasks: - - name: run this command and ignore the result - shell: /usr/bin/somecommand - ignore_errors: True - - -If the action line is getting too long for comfort you can break it on -a space and indent any continuation lines:: - - tasks: - - name: Copy ansible inventory file to client - copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts - owner=root group=root mode=0644 - -Variables can be used in action lines. Suppose you defined -a variable called 'vhost' in the 'vars' section, you could do this:: - - tasks: - - name: create a virtual host file for {{ vhost }} - template: src=somefile.j2 dest=/etc/httpd/conf.d/{{ vhost }} - -Those same variables are usable in templates, which we'll get to later. - -Now in a very basic playbook all the tasks will be listed directly in that play, though it will usually -make more sense to break up tasks using the 'include:' directive. We'll show that a bit later. - -.. _action_shorthand: - -Action Shorthand -```````````````` - -.. versionadded:: 0.8 - -Ansible prefers listing modules like this in 0.8 and later:: - - template: src=templates/foo.j2 dest=/etc/foo.conf - -You will notice in earlier versions, this was only available as:: - - action: template src=templates/foo.j2 dest=/etc/foo.conf - -The old form continues to work in newer versions without any plan of deprecation. - -.. _handlers: - -Handlers: Running Operations On Change -`````````````````````````````````````` - -As we've mentioned, modules are written to be 'idempotent' and can relay when -they have made a change on the remote system. Playbooks recognize this and -have a basic event system that can be used to respond to change. - -These 'notify' actions are triggered at the end of each block of tasks in a playbook, and will only be -triggered once even if notified by multiple different tasks. - -For instance, multiple resources may indicate -that apache needs to be restarted because they have changed a config file, -but apache will only be bounced once to avoid unnecessary restarts. - -Here's an example of restarting two services when the contents of a file -change, but only if the file changes:: - - - name: template configuration file - template: src=template.j2 dest=/etc/foo.conf - notify: - - restart memcached - - restart apache - -The things listed in the 'notify' section of a task are called -handlers. - -Handlers are lists of tasks, not really any different from regular -tasks, that are referenced by name. Handlers are what notifiers -notify. If nothing notifies a handler, it will not run. Regardless -of how many things notify a handler, it will run only once, after all -of the tasks complete in a particular play. - -Here's an example handlers section:: - - handlers: - - name: restart memcached - service: name=memcached state=restarted - - name: restart apache - service: name=apache state=restarted - -Handlers are best used to restart services and trigger reboots. You probably -won't need them for much else. - -.. note:: - Notify handlers are always run in the order written. - -Roles are described later on. It's worthwhile to point out that handlers are -automatically processed between 'pre_tasks', 'roles', 'tasks', and 'post_tasks' -sections. If you ever want to flush all the handler commands immediately though, -in 1.2 and later, you can:: - - tasks: - - shell: some tasks go here - - meta: flush_handlers - - shell: some other tasks - -In the above example any queued up handlers would be processed early when the 'meta' -statement was reached. This is a bit of a niche case but can come in handy from -time to time. - -.. _executing_a_playbook: - -Executing A Playbook -```````````````````` - -Now that you've learned playbook syntax, how do you run a playbook? It's simple. -Let's run a playbook using a parallelism level of 10:: - - ansible-playbook playbook.yml -f 10 - -.. _tips_and_tricks: - - -Ansible-Pull -```````````` - -Should you want to invert the architecture of Ansible, so that nodes check in to a central location, instead -of pushing configuration out to them, you can. - -Ansible-pull is a small script that will checkout a repo of configuration instructions from git, and then -run ansible-playbook against that content. - -Assuming you load balance your checkout location, ansible-pull scales essentially infinitely. - -Run 'ansible-pull --help' for details. - -There's also a `clever playbook `_ available to using ansible in push mode to configure ansible-pull via a crontab! - -Tips and Tricks -``````````````` - -Look at the bottom of the playbook execution for a summary of the nodes that were targeted -and how they performed. General failures and fatal "unreachable" communication attempts are -kept separate in the counts. - -If you ever want to see detailed output from successful modules as well as unsuccessful ones, -use the '--verbose' flag. This is available in Ansible 0.5 and later. - -Ansible playbook output is vastly upgraded if the cowsay -package is installed. Try it! - -To see what hosts would be affected by a playbook before you run it, you -can do this:: - - ansible-playbook playbook.yml --list-hosts. - -.. seealso:: - - :doc:`YAMLSyntax` - Learn about YAML syntax - :doc:`playbooks_best_practices` - Various tips about managing playbooks in the real world - :doc:`index` - Hop back to the documentation index for a lot of special topics about playbooks - :doc:`modules` - Learn about available modules - :doc:`developing_modules` - Learn how to extend Ansible by writing your own modules - :doc:`intro_patterns` - Learn about how to select hosts - `Github examples directory `_ - Complete end-to-end playbook examples - `Mailing List `_ - Questions? Help? Ideas? Stop by the list on Google Groups + playbooks_roles + playbooks_variables + playbooks_conditionals + playbooks_loops + playbooks_best_practices diff --git a/docsite/rst/playbooks_special_topics.rst b/docsite/rst/playbooks_special_topics.rst new file mode 100644 index 0000000000..323bb16448 --- /dev/null +++ b/docsite/rst/playbooks_special_topics.rst @@ -0,0 +1,19 @@ +Playbooks: Special Topics +````````````````````````` +Here are some playbook features that not everyone may need to learn, but can be quite useful for particular applications. +Browsing these topics is recommended as you may find some useful tips here, but feel free to learn the basics of Ansible first +and adopt these only if they seem relevant or useful to your environment. + +.. toctree:: + :maxdepth: 1 + + playbooks_acceleration + playbooks_async + playbooks_checkmode + playbooks_delegation + playbooks_environment + playbooks_error_handling + playbooks_lookups + playbooks_prompts + playbooks_tags + From ed39bebaa893dbab1a29c59d750d9f6bd0a4fdfc Mon Sep 17 00:00:00 2001 From: Joshua Lund Date: Tue, 24 Dec 2013 16:05:39 -0700 Subject: [PATCH 027/182] Fixing a small typo in the Galaxy README template --- bin/ansible-galaxy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/ansible-galaxy b/bin/ansible-galaxy index 111ccd01e2..a6845fc86e 100755 --- a/bin/ansible-galaxy +++ b/bin/ansible-galaxy @@ -88,7 +88,7 @@ A brief description of the role goes here. Requirements ------------ -Any pre-requisites that may not be covered by the ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. +Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. Role Variables -------------- From e00262c1b38c4fd416af0e6ad4c16b4e58583d78 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Tue, 24 Dec 2013 18:05:41 -0500 Subject: [PATCH 028/182] Split content into subpages. --- docsite/rst/awx.rst | 11 ++++++++++ docsite/rst/community.rst | 9 +++++++++ docsite/rst/galaxy.rst | 15 ++++++++++++++ docsite/rst/index.rst | 42 +++------------------------------------ docsite/rst/modules.rst | 3 +++ 5 files changed, 41 insertions(+), 39 deletions(-) create mode 100644 docsite/rst/awx.rst create mode 100644 docsite/rst/community.rst create mode 100644 docsite/rst/galaxy.rst diff --git a/docsite/rst/awx.rst b/docsite/rst/awx.rst new file mode 100644 index 0000000000..26ebeb8d3b --- /dev/null +++ b/docsite/rst/awx.rst @@ -0,0 +1,11 @@ +AnsibleWorks AWX +```````````````` + +`AnsibleWorks `_, who also sponsors the Ansible community, also produces 'AWX', which is a web-based solution that makes Ansible even more easy to use for IT teams of all kinds. It's designed to be the hub for all of your automation tasks. + +AWX allows you to control access to who can access what, even allowing sharing of SSH credentials without someone being able to transfer those credentials. Inventory can be graphically managed or synced with a wide variety of cloud sources. It logs all of your jobs, integrates well with LDAP, and has an amazing browsable REST API. Command line tools are available for easy integration +with Jenkins as well. + +Find out more about AWX features and how to download it on the `AWX webpage `_. AWX +is free for usage for up to 10 nodes, and comes bundled with amazing support from AnsibleWorks. As you would expect, AWX is +installed using Ansible playbooks! diff --git a/docsite/rst/community.rst b/docsite/rst/community.rst new file mode 100644 index 0000000000..f91408513e --- /dev/null +++ b/docsite/rst/community.rst @@ -0,0 +1,9 @@ +Community Information +````````````````````` + +Ansible is an open source project designed to bring together developers and administrators of all kinds to collaborate on building +IT automation solutions that work well for them. Should you wish to get more involved -- whether in terms of just asking a question, helping other users, introducing new people to Ansible, or helping with the software or documentation, we welcome your contributions to the project. + +`Ways to interact `_ + + diff --git a/docsite/rst/galaxy.rst b/docsite/rst/galaxy.rst new file mode 100644 index 0000000000..3bc0aa5304 --- /dev/null +++ b/docsite/rst/galaxy.rst @@ -0,0 +1,15 @@ +AnsibleWorks Galaxy +``````````````````` + +.. image:: https://galaxy.ansibleworks.com/static/img/galaxy_logo_small.png + :alt: AnsibleWorks Galaxy Logo + :width: 619px + :height: 109px + +`AnsibleWorks Galaxy `_, is a free site for finding, downloading, rating, and reviewing all kinds of community developed Ansible roles and can be a great way to get a jumpstart on your automation projects. + +You can sign up with social auth, and the download client 'ansible-galaxy' is included in Ansible 1.4.2 and later. + +Read the "About" page on the Galaxy site for more information. + + diff --git a/docsite/rst/index.rst b/docsite/rst/index.rst index a50ae25ec4..68e96d59bb 100644 --- a/docsite/rst/index.rst +++ b/docsite/rst/index.rst @@ -31,46 +31,10 @@ This documentation covers the current released version of Ansible (1.4.3) and al modules guides developing + awx + community + galaxy faq glossary YAMLSyntax -AnsibleWorks AWX -```````````````` - -`AnsibleWorks `_, who also sponsors the Ansible community, also produces 'AWX', which is a web-based solution that makes Ansible even more easy to use for IT teams of all kinds. It's designed to be the hub for all of your automation tasks. - -AWX allows you to control access to who can access what, even allowing sharing of SSH credentials without someone being able to transfer those credentials. Inventory can be graphically managed or synced with a wide variety of cloud sources. It logs all of your jobs, integrates well with LDAP, and has an amazing browsable REST API. Command line tools are available for easy integration -with Jenkins as well. - -Find out more about AWX features and how to download it on the `AWX webpage `_. AWX -is free for usage for up to 10 nodes, and comes bundled with amazing support from AnsibleWorks. As you would expect, AWX is -installed using Ansible playbooks! - -.. _ansibleworks_galaxy: - -AnsibleWorks Galaxy -``````````````````` - -.. image:: https://galaxy.ansibleworks.com/static/img/galaxy_logo_small.png - :alt: AnsibleWorks Galaxy Logo - :width: 619px - :height: 109px - -`AnsibleWorks Galaxy `_, is a free site for finding, downloading, rating, and reviewing all kinds of community developed Ansible roles and can be a great way to get a jumpstart on your automation projects. - -You can sign up with social auth, and the download client 'ansible-galaxy' is included in Ansible 1.4.2 and later. - -Read the "About" page on the Galaxy site for more information. - -.. _detailed_guides: - -Community Information -````````````````````` - -Ansible is an open source project designed to bring together developers and administrators of all kinds to collaborate on building -IT automation solutions that work well for them. Should you wish to get more involved -- whether in terms of just asking a question, helping other users, introducing new people to Ansible, or helping with the software or documentation, we welcome your contributions to the project. - -`Ways to interact `_ - - diff --git a/docsite/rst/modules.rst b/docsite/rst/modules.rst index ba8a7a02ac..7ec0a1fb05 100644 --- a/docsite/rst/modules.rst +++ b/docsite/rst/modules.rst @@ -1,6 +1,9 @@ Ansible Modules =============== +.. toctree:: + :maxdepth: 2 + .. _modules_intro: Introduction From 1a106ac4197922aeecf326ed69e3f9da9d06d973 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Tue, 24 Dec 2013 18:10:19 -0500 Subject: [PATCH 029/182] Add copy of Sphinx RTD theme so we can modify if needed and everyone has a consistent copy. --- docsite/_themes/srtd/__init__.py | 17 + docsite/_themes/srtd/breadcrumbs.html | 11 + docsite/_themes/srtd/footer.html | 30 ++ docsite/_themes/srtd/layout.html | 137 ++++++ docsite/_themes/srtd/layout_old.html | 205 +++++++++ docsite/_themes/srtd/search.html | 50 +++ docsite/_themes/srtd/searchbox.html | 5 + .../_themes/srtd/static/css/badge_only.css | 1 + docsite/_themes/srtd/static/css/theme.css | 1 + .../srtd/static/font/fontawesome_webfont.eot | Bin 0 -> 37405 bytes .../srtd/static/font/fontawesome_webfont.svg | 399 ++++++++++++++++++ .../srtd/static/font/fontawesome_webfont.ttf | Bin 0 -> 79076 bytes .../srtd/static/font/fontawesome_webfont.woff | Bin 0 -> 43572 bytes docsite/_themes/srtd/static/js/theme.js | 16 + docsite/_themes/srtd/theme.conf | 7 + docsite/_themes/srtd/versions.html | 37 ++ docsite/conf.py | 12 +- 17 files changed, 922 insertions(+), 6 deletions(-) create mode 100644 docsite/_themes/srtd/__init__.py create mode 100644 docsite/_themes/srtd/breadcrumbs.html create mode 100644 docsite/_themes/srtd/footer.html create mode 100644 docsite/_themes/srtd/layout.html create mode 100644 docsite/_themes/srtd/layout_old.html create mode 100644 docsite/_themes/srtd/search.html create mode 100644 docsite/_themes/srtd/searchbox.html create mode 100644 docsite/_themes/srtd/static/css/badge_only.css create mode 100644 docsite/_themes/srtd/static/css/theme.css create mode 100644 docsite/_themes/srtd/static/font/fontawesome_webfont.eot create mode 100644 docsite/_themes/srtd/static/font/fontawesome_webfont.svg create mode 100644 docsite/_themes/srtd/static/font/fontawesome_webfont.ttf create mode 100644 docsite/_themes/srtd/static/font/fontawesome_webfont.woff create mode 100644 docsite/_themes/srtd/static/js/theme.js create mode 100644 docsite/_themes/srtd/theme.conf create mode 100644 docsite/_themes/srtd/versions.html diff --git a/docsite/_themes/srtd/__init__.py b/docsite/_themes/srtd/__init__.py new file mode 100644 index 0000000000..1440863d68 --- /dev/null +++ b/docsite/_themes/srtd/__init__.py @@ -0,0 +1,17 @@ +"""Sphinx ReadTheDocs theme. + +From https://github.com/ryan-roemer/sphinx-bootstrap-theme. + +""" +import os + +VERSION = (0, 1, 5) + +__version__ = ".".join(str(v) for v in VERSION) +__version_full__ = __version__ + + +def get_html_theme_path(): + """Return list of HTML theme paths.""" + cur_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + return cur_dir diff --git a/docsite/_themes/srtd/breadcrumbs.html b/docsite/_themes/srtd/breadcrumbs.html new file mode 100644 index 0000000000..1d2e778982 --- /dev/null +++ b/docsite/_themes/srtd/breadcrumbs.html @@ -0,0 +1,11 @@ + +
    + diff --git a/docsite/_themes/srtd/footer.html b/docsite/_themes/srtd/footer.html new file mode 100644 index 0000000000..e42d753ff6 --- /dev/null +++ b/docsite/_themes/srtd/footer.html @@ -0,0 +1,30 @@ +
    + {% if next or prev %} + + {% endif %} + +
    + +

    + {%- if show_copyright %} + {%- if hasdoc('copyright') %} + {% trans path=pathto('copyright'), copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} + {%- else %} + {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} + {%- endif %} + {%- endif %} + + {%- if last_updated %} + {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %} + {%- endif %} +

    + + {% trans %}Sphinx theme provided by Read the Docs{% endtrans %} +
    diff --git a/docsite/_themes/srtd/layout.html b/docsite/_themes/srtd/layout.html new file mode 100644 index 0000000000..ae616f6008 --- /dev/null +++ b/docsite/_themes/srtd/layout.html @@ -0,0 +1,137 @@ +{# TEMPLATE VAR SETTINGS #} +{%- set url_root = pathto('', 1) %} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} + + + + + + + + {% block htmltitle %} + {{ title|striptags|e }}{{ titlesuffix }} + {% endblock %} + + {# FAVICON #} + {% if favicon %} + + {% endif %} + + {# CSS #} + + + {# JS #} + {% if not embedded %} + + + {%- for scriptfile in script_files %} + + {%- endfor %} + + {% if use_opensearch %} + + {% endif %} + + {% endif %} + + {# RTD hosts these file themselves, so just load on non RTD builds #} + {% if not READTHEDOCS %} + + + {% endif %} + + {% for cssfile in css_files %} + + {% endfor %} + + {%- block linktags %} + {%- if hasdoc('about') %} + + {%- endif %} + {%- if hasdoc('genindex') %} + + {%- endif %} + {%- if hasdoc('search') %} + + {%- endif %} + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} + {%- endblock %} + {%- block extrahead %} {% endblock %} + + + + + + + +
    + + {# SIDE NAV, TOGGLES ON MOBILE #} + + +
    + + {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #} + + + + {# PAGE CONTENT #} +
    +
    + {% include "breadcrumbs.html" %} + {% block body %}{% endblock %} + {% include "footer.html" %} +
    +
    + +
    + +
    + {% include "versions.html" %} + + diff --git a/docsite/_themes/srtd/layout_old.html b/docsite/_themes/srtd/layout_old.html new file mode 100644 index 0000000000..deb8df2a1a --- /dev/null +++ b/docsite/_themes/srtd/layout_old.html @@ -0,0 +1,205 @@ +{# + basic/layout.html + ~~~~~~~~~~~~~~~~~ + + Master layout template for Sphinx themes. + + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- block doctype -%} + +{%- endblock %} +{%- set reldelim1 = reldelim1 is not defined and ' »' or reldelim1 %} +{%- set reldelim2 = reldelim2 is not defined and ' |' or reldelim2 %} +{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and + (sidebars != []) %} +{%- set url_root = pathto('', 1) %} +{# XXX necessary? #} +{%- if url_root == '#' %}{% set url_root = '' %}{% endif %} +{%- if not embedded and docstitle %} + {%- set titlesuffix = " — "|safe + docstitle|e %} +{%- else %} + {%- set titlesuffix = "" %} +{%- endif %} + +{%- macro relbar() %} + +{%- endmacro %} + +{%- macro sidebar() %} + {%- if render_sidebar %} +
    +
    + {%- block sidebarlogo %} + {%- if logo %} + + {%- endif %} + {%- endblock %} + {%- if sidebars != None %} + {#- new style sidebar: explicitly include/exclude templates #} + {%- for sidebartemplate in sidebars %} + {%- include sidebartemplate %} + {%- endfor %} + {%- else %} + {#- old style sidebars: using blocks -- should be deprecated #} + {%- block sidebartoc %} + {%- include "localtoc.html" %} + {%- endblock %} + {%- block sidebarrel %} + {%- include "relations.html" %} + {%- endblock %} + {%- block sidebarsourcelink %} + {%- include "sourcelink.html" %} + {%- endblock %} + {%- if customsidebar %} + {%- include customsidebar %} + {%- endif %} + {%- block sidebarsearch %} + {%- include "searchbox.html" %} + {%- endblock %} + {%- endif %} +
    +
    + {%- endif %} +{%- endmacro %} + +{%- macro script() %} + + {%- for scriptfile in script_files %} + + {%- endfor %} +{%- endmacro %} + +{%- macro css() %} + + + {%- for cssfile in css_files %} + + {%- endfor %} +{%- endmacro %} + + + + + {{ metatags }} + {%- block htmltitle %} + {{ title|striptags|e }}{{ titlesuffix }} + {%- endblock %} + {{ css() }} + {%- if not embedded %} + {{ script() }} + {%- if use_opensearch %} + + {%- endif %} + {%- if favicon %} + + {%- endif %} + {%- endif %} +{%- block linktags %} + {%- if hasdoc('about') %} + + {%- endif %} + {%- if hasdoc('genindex') %} + + {%- endif %} + {%- if hasdoc('search') %} + + {%- endif %} + {%- if hasdoc('copyright') %} + + {%- endif %} + + {%- if parents %} + + {%- endif %} + {%- if next %} + + {%- endif %} + {%- if prev %} + + {%- endif %} +{%- endblock %} +{%- block extrahead %} {% endblock %} + + +{%- block header %}{% endblock %} + +{%- block relbar1 %}{{ relbar() }}{% endblock %} + +{%- block content %} + {%- block sidebar1 %} {# possible location for sidebar #} {% endblock %} + +
    + {%- block document %} +
    + {%- if render_sidebar %} +
    + {%- endif %} +
    + {% block body %} {% endblock %} +
    + {%- if render_sidebar %} +
    + {%- endif %} +
    + {%- endblock %} + + {%- block sidebar2 %}{{ sidebar() }}{% endblock %} +
    +
    +{%- endblock %} + +{%- block relbar2 %}{{ relbar() }}{% endblock %} + +{%- block footer %} + +

    asdf asdf asdf asdf 22

    +{%- endblock %} + + + diff --git a/docsite/_themes/srtd/search.html b/docsite/_themes/srtd/search.html new file mode 100644 index 0000000000..d8bbe69014 --- /dev/null +++ b/docsite/_themes/srtd/search.html @@ -0,0 +1,50 @@ +{# + basic/search.html + ~~~~~~~~~~~~~~~~~ + + Template for the search page. + + :copyright: Copyright 2007-2013 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +#} +{%- extends "layout.html" %} +{% set title = _('Search') %} +{% set script_files = script_files + ['_static/searchtools.js'] %} +{% block extrahead %} + + {# this is used when loading the search index using $.ajax fails, + such as on Chrome for documents on localhost #} + + {{ super() }} +{% endblock %} +{% block body %} + + + {% if search_performed %} +

    {{ _('Search Results') }}

    + {% if not search_results %} +

    {{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}

    + {% endif %} + {% endif %} +
    + {% if search_results %} +
      + {% for href, caption, context in search_results %} +
    • + {{ caption }} +

      {{ context|e }}

      +
    • + {% endfor %} +
    + {% endif %} +
    +{% endblock %} diff --git a/docsite/_themes/srtd/searchbox.html b/docsite/_themes/srtd/searchbox.html new file mode 100644 index 0000000000..7668224ee9 --- /dev/null +++ b/docsite/_themes/srtd/searchbox.html @@ -0,0 +1,5 @@ +
    + + + +
    diff --git a/docsite/_themes/srtd/static/css/badge_only.css b/docsite/_themes/srtd/static/css/badge_only.css new file mode 100644 index 0000000000..7fccc414bb --- /dev/null +++ b/docsite/_themes/srtd/static/css/badge_only.css @@ -0,0 +1 @@ +.font-smooth,.icon:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:fontawesome-webfont;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#fontawesome-webfont") format("svg")}.icon:before{display:inline-block;font-family:fontawesome-webfont;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .icon{display:inline-block;text-decoration:inherit}li .icon{display:inline-block}li .icon-large:before,li .icon-large:before{width:1.875em}ul.icons{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.icons li .icon{width:0.8em}ul.icons li .icon-large:before,ul.icons li .icon-large:before{vertical-align:baseline}.icon-book:before{content:"\f02d"}.icon-caret-down:before{content:"\f0d7"}.icon-caret-up:before{content:"\f0d8"}.icon-caret-left:before{content:"\f0d9"}.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .icon{color:#fcfcfc}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} diff --git a/docsite/_themes/srtd/static/css/theme.css b/docsite/_themes/srtd/static/css/theme.css new file mode 100644 index 0000000000..13135230c3 --- /dev/null +++ b/docsite/_themes/srtd/static/css/theme.css @@ -0,0 +1 @@ +*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}blockquote{margin:0}dfn{font-style:italic}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:20px 0;padding:0}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:bold}pre,code,.rst-content tt,kbd,samp{font-family:monospace,serif;_font-family:"courier new",monospace;font-size:1em}pre{white-space:pre}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}ul,ol,dl{margin:0;padding:0;list-style:none;list-style-image:none}li{list-style:none}dd{margin:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;max-width:100%}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:0.2em 0;background:#ccc;color:#000;padding:0.2em 0}.ir{display:block;border:0;text-indent:-999em;overflow:hidden;background-color:transparent;background-repeat:no-repeat;text-align:left;direction:ltr;*line-height:0}.ir br{display:none}.hidden{display:none !important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.relative{position:relative}big,small{font-size:100%}@media print{html,body,section{background:none !important}*{box-shadow:none !important;text-shadow:none !important;filter:none !important;-ms-filter:none !important}a,a:visited{text-decoration:underline}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}.font-smooth,.icon:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-tag-input-group .wy-tag .wy-tag-remove:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before,.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning,.btn,input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"],select,textarea,.wy-tag-input-group,.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a,.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a,.wy-nav-top a{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:fontawesome-webfont;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#fontawesome-webfont") format("svg")}.icon:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-tag-input-group .wy-tag .wy-tag-remove:before,.rst-content .admonition-title:before,.rst-content h1 .headerlink:before,.rst-content h2 .headerlink:before,.rst-content h3 .headerlink:before,.rst-content h4 .headerlink:before,.rst-content h5 .headerlink:before,.rst-content h6 .headerlink:before,.rst-content dl dt .headerlink:before{display:inline-block;font-family:fontawesome-webfont;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .icon,a .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success a .wy-input-context,a .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger a .wy-input-context,a .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning a .wy-input-context,a .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info a .wy-input-context,a .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag a .wy-tag-remove,a .rst-content .admonition-title,.rst-content a .admonition-title,a .rst-content h1 .headerlink,.rst-content h1 a .headerlink,a .rst-content h2 .headerlink,.rst-content h2 a .headerlink,a .rst-content h3 .headerlink,.rst-content h3 a .headerlink,a .rst-content h4 .headerlink,.rst-content h4 a .headerlink,a .rst-content h5 .headerlink,.rst-content h5 a .headerlink,a .rst-content h6 .headerlink,.rst-content h6 a .headerlink,a .rst-content dl dt .headerlink,.rst-content dl dt a .headerlink{display:inline-block;text-decoration:inherit}.icon-large:before{vertical-align:-10%;font-size:1.33333em}.btn .icon,.btn .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success .btn .wy-input-context,.btn .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .btn .wy-input-context,.btn .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .btn .wy-input-context,.btn .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info .btn .wy-input-context,.btn .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag .btn .wy-tag-remove,.btn .rst-content .admonition-title,.rst-content .btn .admonition-title,.btn .rst-content h1 .headerlink,.rst-content h1 .btn .headerlink,.btn .rst-content h2 .headerlink,.rst-content h2 .btn .headerlink,.btn .rst-content h3 .headerlink,.rst-content h3 .btn .headerlink,.btn .rst-content h4 .headerlink,.rst-content h4 .btn .headerlink,.btn .rst-content h5 .headerlink,.rst-content h5 .btn .headerlink,.btn .rst-content h6 .headerlink,.rst-content h6 .btn .headerlink,.btn .rst-content dl dt .headerlink,.rst-content dl dt .btn .headerlink,.nav .icon,.nav .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success .nav .wy-input-context,.nav .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .nav .wy-input-context,.nav .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .nav .wy-input-context,.nav .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info .nav .wy-input-context,.nav .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag .nav .wy-tag-remove,.nav .rst-content .admonition-title,.rst-content .nav .admonition-title,.nav .rst-content h1 .headerlink,.rst-content h1 .nav .headerlink,.nav .rst-content h2 .headerlink,.rst-content h2 .nav .headerlink,.nav .rst-content h3 .headerlink,.rst-content h3 .nav .headerlink,.nav .rst-content h4 .headerlink,.rst-content h4 .nav .headerlink,.nav .rst-content h5 .headerlink,.rst-content h5 .nav .headerlink,.nav .rst-content h6 .headerlink,.rst-content h6 .nav .headerlink,.nav .rst-content dl dt .headerlink,.rst-content dl dt .nav .headerlink{display:inline}.btn .icon.icon-large,.btn .wy-inline-validate.wy-inline-validate-success .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-success .btn .icon-large.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-danger .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-danger .btn .icon-large.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-warning .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-warning .btn .icon-large.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-info .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-info .btn .icon-large.wy-input-context,.btn .wy-tag-input-group .wy-tag .icon-large.wy-tag-remove,.wy-tag-input-group .wy-tag .btn .icon-large.wy-tag-remove,.btn .rst-content .icon-large.admonition-title,.rst-content .btn .icon-large.admonition-title,.btn .rst-content h1 .icon-large.headerlink,.rst-content h1 .btn .icon-large.headerlink,.btn .rst-content h2 .icon-large.headerlink,.rst-content h2 .btn .icon-large.headerlink,.btn .rst-content h3 .icon-large.headerlink,.rst-content h3 .btn .icon-large.headerlink,.btn .rst-content h4 .icon-large.headerlink,.rst-content h4 .btn .icon-large.headerlink,.btn .rst-content h5 .icon-large.headerlink,.rst-content h5 .btn .icon-large.headerlink,.btn .rst-content h6 .icon-large.headerlink,.rst-content h6 .btn .icon-large.headerlink,.btn .rst-content dl dt .icon-large.headerlink,.rst-content dl dt .btn .icon-large.headerlink,.nav .icon.icon-large,.nav .wy-inline-validate.wy-inline-validate-success .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-success .nav .icon-large.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-danger .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-danger .nav .icon-large.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-warning .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-warning .nav .icon-large.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-info .icon-large.wy-input-context,.wy-inline-validate.wy-inline-validate-info .nav .icon-large.wy-input-context,.nav .wy-tag-input-group .wy-tag .icon-large.wy-tag-remove,.wy-tag-input-group .wy-tag .nav .icon-large.wy-tag-remove,.nav .rst-content .icon-large.admonition-title,.rst-content .nav .icon-large.admonition-title,.nav .rst-content h1 .icon-large.headerlink,.rst-content h1 .nav .icon-large.headerlink,.nav .rst-content h2 .icon-large.headerlink,.rst-content h2 .nav .icon-large.headerlink,.nav .rst-content h3 .icon-large.headerlink,.rst-content h3 .nav .icon-large.headerlink,.nav .rst-content h4 .icon-large.headerlink,.rst-content h4 .nav .icon-large.headerlink,.nav .rst-content h5 .icon-large.headerlink,.rst-content h5 .nav .icon-large.headerlink,.nav .rst-content h6 .icon-large.headerlink,.rst-content h6 .nav .icon-large.headerlink,.nav .rst-content dl dt .icon-large.headerlink,.rst-content dl dt .nav .icon-large.headerlink{line-height:0.9em}.btn .icon.icon-spin,.btn .wy-inline-validate.wy-inline-validate-success .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-success .btn .icon-spin.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-danger .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-danger .btn .icon-spin.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-warning .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-warning .btn .icon-spin.wy-input-context,.btn .wy-inline-validate.wy-inline-validate-info .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-info .btn .icon-spin.wy-input-context,.btn .wy-tag-input-group .wy-tag .icon-spin.wy-tag-remove,.wy-tag-input-group .wy-tag .btn .icon-spin.wy-tag-remove,.btn .rst-content .icon-spin.admonition-title,.rst-content .btn .icon-spin.admonition-title,.btn .rst-content h1 .icon-spin.headerlink,.rst-content h1 .btn .icon-spin.headerlink,.btn .rst-content h2 .icon-spin.headerlink,.rst-content h2 .btn .icon-spin.headerlink,.btn .rst-content h3 .icon-spin.headerlink,.rst-content h3 .btn .icon-spin.headerlink,.btn .rst-content h4 .icon-spin.headerlink,.rst-content h4 .btn .icon-spin.headerlink,.btn .rst-content h5 .icon-spin.headerlink,.rst-content h5 .btn .icon-spin.headerlink,.btn .rst-content h6 .icon-spin.headerlink,.rst-content h6 .btn .icon-spin.headerlink,.btn .rst-content dl dt .icon-spin.headerlink,.rst-content dl dt .btn .icon-spin.headerlink,.nav .icon.icon-spin,.nav .wy-inline-validate.wy-inline-validate-success .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-success .nav .icon-spin.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-danger .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-danger .nav .icon-spin.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-warning .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-warning .nav .icon-spin.wy-input-context,.nav .wy-inline-validate.wy-inline-validate-info .icon-spin.wy-input-context,.wy-inline-validate.wy-inline-validate-info .nav .icon-spin.wy-input-context,.nav .wy-tag-input-group .wy-tag .icon-spin.wy-tag-remove,.wy-tag-input-group .wy-tag .nav .icon-spin.wy-tag-remove,.nav .rst-content .icon-spin.admonition-title,.rst-content .nav .icon-spin.admonition-title,.nav .rst-content h1 .icon-spin.headerlink,.rst-content h1 .nav .icon-spin.headerlink,.nav .rst-content h2 .icon-spin.headerlink,.rst-content h2 .nav .icon-spin.headerlink,.nav .rst-content h3 .icon-spin.headerlink,.rst-content h3 .nav .icon-spin.headerlink,.nav .rst-content h4 .icon-spin.headerlink,.rst-content h4 .nav .icon-spin.headerlink,.nav .rst-content h5 .icon-spin.headerlink,.rst-content h5 .nav .icon-spin.headerlink,.nav .rst-content h6 .icon-spin.headerlink,.rst-content h6 .nav .icon-spin.headerlink,.nav .rst-content dl dt .icon-spin.headerlink,.rst-content dl dt .nav .icon-spin.headerlink{display:inline-block}.btn.icon:before,.wy-inline-validate.wy-inline-validate-success .btn.wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .btn.wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .btn.wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .btn.wy-input-context:before,.wy-tag-input-group .wy-tag .btn.wy-tag-remove:before,.rst-content .btn.admonition-title:before,.rst-content h1 .btn.headerlink:before,.rst-content h2 .btn.headerlink:before,.rst-content h3 .btn.headerlink:before,.rst-content h4 .btn.headerlink:before,.rst-content h5 .btn.headerlink:before,.rst-content h6 .btn.headerlink:before,.rst-content dl dt .btn.headerlink:before{opacity:0.5;-webkit-transition:opacity 0.05s ease-in;-moz-transition:opacity 0.05s ease-in;transition:opacity 0.05s ease-in}.btn.icon:hover:before,.wy-inline-validate.wy-inline-validate-success .btn.wy-input-context:hover:before,.wy-inline-validate.wy-inline-validate-danger .btn.wy-input-context:hover:before,.wy-inline-validate.wy-inline-validate-warning .btn.wy-input-context:hover:before,.wy-inline-validate.wy-inline-validate-info .btn.wy-input-context:hover:before,.wy-tag-input-group .wy-tag .btn.wy-tag-remove:hover:before,.rst-content .btn.admonition-title:hover:before,.rst-content h1 .btn.headerlink:hover:before,.rst-content h2 .btn.headerlink:hover:before,.rst-content h3 .btn.headerlink:hover:before,.rst-content h4 .btn.headerlink:hover:before,.rst-content h5 .btn.headerlink:hover:before,.rst-content h6 .btn.headerlink:hover:before,.rst-content dl dt .btn.headerlink:hover:before{opacity:1}.btn-mini .icon:before,.btn-mini .wy-inline-validate.wy-inline-validate-success .wy-input-context:before,.wy-inline-validate.wy-inline-validate-success .btn-mini .wy-input-context:before,.btn-mini .wy-inline-validate.wy-inline-validate-danger .wy-input-context:before,.wy-inline-validate.wy-inline-validate-danger .btn-mini .wy-input-context:before,.btn-mini .wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-warning .btn-mini .wy-input-context:before,.btn-mini .wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .btn-mini .wy-input-context:before,.btn-mini .wy-tag-input-group .wy-tag .wy-tag-remove:before,.wy-tag-input-group .wy-tag .btn-mini .wy-tag-remove:before,.btn-mini .rst-content .admonition-title:before,.rst-content .btn-mini .admonition-title:before,.btn-mini .rst-content h1 .headerlink:before,.rst-content h1 .btn-mini .headerlink:before,.btn-mini .rst-content h2 .headerlink:before,.rst-content h2 .btn-mini .headerlink:before,.btn-mini .rst-content h3 .headerlink:before,.rst-content h3 .btn-mini .headerlink:before,.btn-mini .rst-content h4 .headerlink:before,.rst-content h4 .btn-mini .headerlink:before,.btn-mini .rst-content h5 .headerlink:before,.rst-content h5 .btn-mini .headerlink:before,.btn-mini .rst-content h6 .headerlink:before,.rst-content h6 .btn-mini .headerlink:before,.btn-mini .rst-content dl dt .headerlink:before,.rst-content dl dt .btn-mini .headerlink:before{font-size:14px;vertical-align:-15%}li .icon,li .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success li .wy-input-context,li .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger li .wy-input-context,li .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning li .wy-input-context,li .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info li .wy-input-context,li .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag li .wy-tag-remove,li .rst-content .admonition-title,.rst-content li .admonition-title,li .rst-content h1 .headerlink,.rst-content h1 li .headerlink,li .rst-content h2 .headerlink,.rst-content h2 li .headerlink,li .rst-content h3 .headerlink,.rst-content h3 li .headerlink,li .rst-content h4 .headerlink,.rst-content h4 li .headerlink,li .rst-content h5 .headerlink,.rst-content h5 li .headerlink,li .rst-content h6 .headerlink,.rst-content h6 li .headerlink,li .rst-content dl dt .headerlink,.rst-content dl dt li .headerlink{display:inline-block}li .icon-large:before,li .icon-large:before{width:1.875em}ul.icons{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.icons li .icon,ul.icons li .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success ul.icons li .wy-input-context,ul.icons li .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger ul.icons li .wy-input-context,ul.icons li .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning ul.icons li .wy-input-context,ul.icons li .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info ul.icons li .wy-input-context,ul.icons li .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag ul.icons li .wy-tag-remove,ul.icons li .rst-content .admonition-title,.rst-content ul.icons li .admonition-title,ul.icons li .rst-content h1 .headerlink,.rst-content h1 ul.icons li .headerlink,ul.icons li .rst-content h2 .headerlink,.rst-content h2 ul.icons li .headerlink,ul.icons li .rst-content h3 .headerlink,.rst-content h3 ul.icons li .headerlink,ul.icons li .rst-content h4 .headerlink,.rst-content h4 ul.icons li .headerlink,ul.icons li .rst-content h5 .headerlink,.rst-content h5 ul.icons li .headerlink,ul.icons li .rst-content h6 .headerlink,.rst-content h6 ul.icons li .headerlink,ul.icons li .rst-content dl dt .headerlink,.rst-content dl dt ul.icons li .headerlink{width:0.8em}ul.icons li .icon-large:before,ul.icons li .icon-large:before{vertical-align:baseline}.icon-glass:before{content:"\f000"}.icon-music:before{content:"\f001"}.icon-search:before{content:"\f002"}.icon-envelope-alt:before{content:"\f003"}.icon-heart:before{content:"\f004"}.icon-star:before{content:"\f005"}.icon-star-empty:before{content:"\f006"}.icon-user:before{content:"\f007"}.icon-film:before{content:"\f008"}.icon-th-large:before{content:"\f009"}.icon-th:before{content:"\f00a"}.icon-th-list:before{content:"\f00b"}.icon-ok:before{content:"\f00c"}.icon-remove:before,.wy-tag-input-group .wy-tag .wy-tag-remove:before{content:"\f00d"}.icon-zoom-in:before{content:"\f00e"}.icon-zoom-out:before{content:"\f010"}.icon-power-off:before,.icon-off:before{content:"\f011"}.icon-signal:before{content:"\f012"}.icon-gear:before,.icon-cog:before{content:"\f013"}.icon-trash:before{content:"\f014"}.icon-home:before{content:"\f015"}.icon-file-alt:before{content:"\f016"}.icon-time:before{content:"\f017"}.icon-road:before{content:"\f018"}.icon-download-alt:before{content:"\f019"}.icon-download:before{content:"\f01a"}.icon-upload:before{content:"\f01b"}.icon-inbox:before{content:"\f01c"}.icon-play-circle:before{content:"\f01d"}.icon-rotate-right:before,.icon-repeat:before{content:"\f01e"}.icon-refresh:before{content:"\f021"}.icon-list-alt:before{content:"\f022"}.icon-lock:before{content:"\f023"}.icon-flag:before{content:"\f024"}.icon-headphones:before{content:"\f025"}.icon-volume-off:before{content:"\f026"}.icon-volume-down:before{content:"\f027"}.icon-volume-up:before{content:"\f028"}.icon-qrcode:before{content:"\f029"}.icon-barcode:before{content:"\f02a"}.icon-tag:before{content:"\f02b"}.icon-tags:before{content:"\f02c"}.icon-book:before{content:"\f02d"}.icon-bookmark:before{content:"\f02e"}.icon-print:before{content:"\f02f"}.icon-camera:before{content:"\f030"}.icon-font:before{content:"\f031"}.icon-bold:before{content:"\f032"}.icon-italic:before{content:"\f033"}.icon-text-height:before{content:"\f034"}.icon-text-width:before{content:"\f035"}.icon-align-left:before{content:"\f036"}.icon-align-center:before{content:"\f037"}.icon-align-right:before{content:"\f038"}.icon-align-justify:before{content:"\f039"}.icon-list:before{content:"\f03a"}.icon-indent-left:before{content:"\f03b"}.icon-indent-right:before{content:"\f03c"}.icon-facetime-video:before{content:"\f03d"}.icon-picture:before{content:"\f03e"}.icon-pencil:before{content:"\f040"}.icon-map-marker:before{content:"\f041"}.icon-adjust:before{content:"\f042"}.icon-tint:before{content:"\f043"}.icon-edit:before{content:"\f044"}.icon-share:before{content:"\f045"}.icon-check:before{content:"\f046"}.icon-move:before{content:"\f047"}.icon-step-backward:before{content:"\f048"}.icon-fast-backward:before{content:"\f049"}.icon-backward:before{content:"\f04a"}.icon-play:before{content:"\f04b"}.icon-pause:before{content:"\f04c"}.icon-stop:before{content:"\f04d"}.icon-forward:before{content:"\f04e"}.icon-fast-forward:before{content:"\f050"}.icon-step-forward:before{content:"\f051"}.icon-eject:before{content:"\f052"}.icon-chevron-left:before{content:"\f053"}.icon-chevron-right:before{content:"\f054"}.icon-plus-sign:before{content:"\f055"}.icon-minus-sign:before{content:"\f056"}.icon-remove-sign:before,.wy-inline-validate.wy-inline-validate-danger .wy-input-context:before{content:"\f057"}.icon-ok-sign:before{content:"\f058"}.icon-question-sign:before{content:"\f059"}.icon-info-sign:before{content:"\f05a"}.icon-screenshot:before{content:"\f05b"}.icon-remove-circle:before{content:"\f05c"}.icon-ok-circle:before{content:"\f05d"}.icon-ban-circle:before{content:"\f05e"}.icon-arrow-left:before{content:"\f060"}.icon-arrow-right:before{content:"\f061"}.icon-arrow-up:before{content:"\f062"}.icon-arrow-down:before{content:"\f063"}.icon-mail-forward:before,.icon-share-alt:before{content:"\f064"}.icon-resize-full:before{content:"\f065"}.icon-resize-small:before{content:"\f066"}.icon-plus:before{content:"\f067"}.icon-minus:before{content:"\f068"}.icon-asterisk:before{content:"\f069"}.icon-exclamation-sign:before,.wy-inline-validate.wy-inline-validate-warning .wy-input-context:before,.wy-inline-validate.wy-inline-validate-info .wy-input-context:before,.rst-content .admonition-title:before{content:"\f06a"}.icon-gift:before{content:"\f06b"}.icon-leaf:before{content:"\f06c"}.icon-fire:before{content:"\f06d"}.icon-eye-open:before{content:"\f06e"}.icon-eye-close:before{content:"\f070"}.icon-warning-sign:before{content:"\f071"}.icon-plane:before{content:"\f072"}.icon-calendar:before{content:"\f073"}.icon-random:before{content:"\f074"}.icon-comment:before{content:"\f075"}.icon-magnet:before{content:"\f076"}.icon-chevron-up:before{content:"\f077"}.icon-chevron-down:before{content:"\f078"}.icon-retweet:before{content:"\f079"}.icon-shopping-cart:before{content:"\f07a"}.icon-folder-close:before{content:"\f07b"}.icon-folder-open:before{content:"\f07c"}.icon-resize-vertical:before{content:"\f07d"}.icon-resize-horizontal:before{content:"\f07e"}.icon-bar-chart:before{content:"\f080"}.icon-twitter-sign:before{content:"\f081"}.icon-facebook-sign:before{content:"\f082"}.icon-camera-retro:before{content:"\f083"}.icon-key:before{content:"\f084"}.icon-gears:before,.icon-cogs:before{content:"\f085"}.icon-comments:before{content:"\f086"}.icon-thumbs-up-alt:before{content:"\f087"}.icon-thumbs-down-alt:before{content:"\f088"}.icon-star-half:before{content:"\f089"}.icon-heart-empty:before{content:"\f08a"}.icon-signout:before{content:"\f08b"}.icon-linkedin-sign:before{content:"\f08c"}.icon-pushpin:before{content:"\f08d"}.icon-external-link:before{content:"\f08e"}.icon-signin:before{content:"\f090"}.icon-trophy:before{content:"\f091"}.icon-github-sign:before{content:"\f092"}.icon-upload-alt:before{content:"\f093"}.icon-lemon:before{content:"\f094"}.icon-phone:before{content:"\f095"}.icon-unchecked:before,.icon-check-empty:before{content:"\f096"}.icon-bookmark-empty:before{content:"\f097"}.icon-phone-sign:before{content:"\f098"}.icon-twitter:before{content:"\f099"}.icon-facebook:before{content:"\f09a"}.icon-github:before{content:"\f09b"}.icon-unlock:before{content:"\f09c"}.icon-credit-card:before{content:"\f09d"}.icon-rss:before{content:"\f09e"}.icon-hdd:before{content:"\f0a0"}.icon-bullhorn:before{content:"\f0a1"}.icon-bell:before{content:"\f0a2"}.icon-certificate:before{content:"\f0a3"}.icon-hand-right:before{content:"\f0a4"}.icon-hand-left:before{content:"\f0a5"}.icon-hand-up:before{content:"\f0a6"}.icon-hand-down:before{content:"\f0a7"}.icon-circle-arrow-left:before{content:"\f0a8"}.icon-circle-arrow-right:before{content:"\f0a9"}.icon-circle-arrow-up:before{content:"\f0aa"}.icon-circle-arrow-down:before{content:"\f0ab"}.icon-globe:before{content:"\f0ac"}.icon-wrench:before{content:"\f0ad"}.icon-tasks:before{content:"\f0ae"}.icon-filter:before{content:"\f0b0"}.icon-briefcase:before{content:"\f0b1"}.icon-fullscreen:before{content:"\f0b2"}.icon-group:before{content:"\f0c0"}.icon-link:before{content:"\f0c1"}.icon-cloud:before{content:"\f0c2"}.icon-beaker:before{content:"\f0c3"}.icon-cut:before{content:"\f0c4"}.icon-copy:before{content:"\f0c5"}.icon-paperclip:before,.icon-paper-clip:before{content:"\f0c6"}.icon-save:before{content:"\f0c7"}.icon-sign-blank:before{content:"\f0c8"}.icon-reorder:before{content:"\f0c9"}.icon-list-ul:before{content:"\f0ca"}.icon-list-ol:before{content:"\f0cb"}.icon-strikethrough:before{content:"\f0cc"}.icon-underline:before{content:"\f0cd"}.icon-table:before{content:"\f0ce"}.icon-magic:before{content:"\f0d0"}.icon-truck:before{content:"\f0d1"}.icon-pinterest:before{content:"\f0d2"}.icon-pinterest-sign:before{content:"\f0d3"}.icon-google-plus-sign:before{content:"\f0d4"}.icon-google-plus:before{content:"\f0d5"}.icon-money:before{content:"\f0d6"}.icon-caret-down:before{content:"\f0d7"}.icon-caret-up:before{content:"\f0d8"}.icon-caret-left:before{content:"\f0d9"}.icon-caret-right:before{content:"\f0da"}.icon-columns:before{content:"\f0db"}.icon-sort:before{content:"\f0dc"}.icon-sort-down:before{content:"\f0dd"}.icon-sort-up:before{content:"\f0de"}.icon-envelope:before{content:"\f0e0"}.icon-linkedin:before{content:"\f0e1"}.icon-rotate-left:before,.icon-undo:before{content:"\f0e2"}.icon-legal:before{content:"\f0e3"}.icon-dashboard:before{content:"\f0e4"}.icon-comment-alt:before{content:"\f0e5"}.icon-comments-alt:before{content:"\f0e6"}.icon-bolt:before{content:"\f0e7"}.icon-sitemap:before{content:"\f0e8"}.icon-umbrella:before{content:"\f0e9"}.icon-paste:before{content:"\f0ea"}.icon-lightbulb:before{content:"\f0eb"}.icon-exchange:before{content:"\f0ec"}.icon-cloud-download:before{content:"\f0ed"}.icon-cloud-upload:before{content:"\f0ee"}.icon-user-md:before{content:"\f0f0"}.icon-stethoscope:before{content:"\f0f1"}.icon-suitcase:before{content:"\f0f2"}.icon-bell-alt:before{content:"\f0f3"}.icon-coffee:before{content:"\f0f4"}.icon-food:before{content:"\f0f5"}.icon-file-text-alt:before{content:"\f0f6"}.icon-building:before{content:"\f0f7"}.icon-hospital:before{content:"\f0f8"}.icon-ambulance:before{content:"\f0f9"}.icon-medkit:before{content:"\f0fa"}.icon-fighter-jet:before{content:"\f0fb"}.icon-beer:before{content:"\f0fc"}.icon-h-sign:before{content:"\f0fd"}.icon-plus-sign-alt:before{content:"\f0fe"}.icon-double-angle-left:before{content:"\f100"}.icon-double-angle-right:before{content:"\f101"}.icon-double-angle-up:before{content:"\f102"}.icon-double-angle-down:before{content:"\f103"}.icon-angle-left:before{content:"\f104"}.icon-angle-right:before{content:"\f105"}.icon-angle-up:before{content:"\f106"}.icon-angle-down:before{content:"\f107"}.icon-desktop:before{content:"\f108"}.icon-laptop:before{content:"\f109"}.icon-tablet:before{content:"\f10a"}.icon-mobile-phone:before{content:"\f10b"}.icon-circle-blank:before{content:"\f10c"}.icon-quote-left:before{content:"\f10d"}.icon-quote-right:before{content:"\f10e"}.icon-spinner:before{content:"\f110"}.icon-circle:before{content:"\f111"}.icon-mail-reply:before,.icon-reply:before{content:"\f112"}.icon-github-alt:before{content:"\f113"}.icon-folder-close-alt:before{content:"\f114"}.icon-folder-open-alt:before{content:"\f115"}.icon-expand-alt:before{content:"\f116"}.icon-collapse-alt:before{content:"\f117"}.icon-smile:before{content:"\f118"}.icon-frown:before{content:"\f119"}.icon-meh:before{content:"\f11a"}.icon-gamepad:before{content:"\f11b"}.icon-keyboard:before{content:"\f11c"}.icon-flag-alt:before{content:"\f11d"}.icon-flag-checkered:before{content:"\f11e"}.icon-terminal:before{content:"\f120"}.icon-code:before{content:"\f121"}.icon-reply-all:before{content:"\f122"}.icon-mail-reply-all:before{content:"\f122"}.icon-star-half-full:before,.icon-star-half-empty:before{content:"\f123"}.icon-location-arrow:before{content:"\f124"}.icon-crop:before{content:"\f125"}.icon-code-fork:before{content:"\f126"}.icon-unlink:before{content:"\f127"}.icon-question:before{content:"\f128"}.icon-info:before{content:"\f129"}.icon-exclamation:before{content:"\f12a"}.icon-superscript:before{content:"\f12b"}.icon-subscript:before{content:"\f12c"}.icon-eraser:before{content:"\f12d"}.icon-puzzle-piece:before{content:"\f12e"}.icon-microphone:before{content:"\f130"}.icon-microphone-off:before{content:"\f131"}.icon-shield:before{content:"\f132"}.icon-calendar-empty:before{content:"\f133"}.icon-fire-extinguisher:before{content:"\f134"}.icon-rocket:before{content:"\f135"}.icon-maxcdn:before{content:"\f136"}.icon-chevron-sign-left:before{content:"\f137"}.icon-chevron-sign-right:before{content:"\f138"}.icon-chevron-sign-up:before{content:"\f139"}.icon-chevron-sign-down:before{content:"\f13a"}.icon-html5:before{content:"\f13b"}.icon-css3:before{content:"\f13c"}.icon-anchor:before{content:"\f13d"}.icon-unlock-alt:before{content:"\f13e"}.icon-bullseye:before{content:"\f140"}.icon-ellipsis-horizontal:before{content:"\f141"}.icon-ellipsis-vertical:before{content:"\f142"}.icon-rss-sign:before{content:"\f143"}.icon-play-sign:before{content:"\f144"}.icon-ticket:before{content:"\f145"}.icon-minus-sign-alt:before{content:"\f146"}.icon-check-minus:before{content:"\f147"}.icon-level-up:before{content:"\f148"}.icon-level-down:before{content:"\f149"}.icon-check-sign:before,.wy-inline-validate.wy-inline-validate-success .wy-input-context:before{content:"\f14a"}.icon-edit-sign:before{content:"\f14b"}.icon-external-link-sign:before{content:"\f14c"}.icon-share-sign:before{content:"\f14d"}.icon-compass:before{content:"\f14e"}.icon-collapse:before{content:"\f150"}.icon-collapse-top:before{content:"\f151"}.icon-expand:before{content:"\f152"}.icon-euro:before,.icon-eur:before{content:"\f153"}.icon-gbp:before{content:"\f154"}.icon-dollar:before,.icon-usd:before{content:"\f155"}.icon-rupee:before,.icon-inr:before{content:"\f156"}.icon-yen:before,.icon-jpy:before{content:"\f157"}.icon-renminbi:before,.icon-cny:before{content:"\f158"}.icon-won:before,.icon-krw:before{content:"\f159"}.icon-bitcoin:before,.icon-btc:before{content:"\f15a"}.icon-file:before{content:"\f15b"}.icon-file-text:before{content:"\f15c"}.icon-sort-by-alphabet:before{content:"\f15d"}.icon-sort-by-alphabet-alt:before{content:"\f15e"}.icon-sort-by-attributes:before{content:"\f160"}.icon-sort-by-attributes-alt:before{content:"\f161"}.icon-sort-by-order:before{content:"\f162"}.icon-sort-by-order-alt:before{content:"\f163"}.icon-thumbs-up:before{content:"\f164"}.icon-thumbs-down:before{content:"\f165"}.icon-youtube-sign:before{content:"\f166"}.icon-youtube:before{content:"\f167"}.icon-xing:before{content:"\f168"}.icon-xing-sign:before{content:"\f169"}.icon-youtube-play:before{content:"\f16a"}.icon-dropbox:before{content:"\f16b"}.icon-stackexchange:before{content:"\f16c"}.icon-instagram:before{content:"\f16d"}.icon-flickr:before{content:"\f16e"}.icon-adn:before{content:"\f170"}.icon-bitbucket:before{content:"\f171"}.icon-bitbucket-sign:before{content:"\f172"}.icon-tumblr:before{content:"\f173"}.icon-tumblr-sign:before{content:"\f174"}.icon-long-arrow-down:before{content:"\f175"}.icon-long-arrow-up:before{content:"\f176"}.icon-long-arrow-left:before{content:"\f177"}.icon-long-arrow-right:before{content:"\f178"}.icon-apple:before{content:"\f179"}.icon-windows:before{content:"\f17a"}.icon-android:before{content:"\f17b"}.icon-linux:before{content:"\f17c"}.icon-dribbble:before{content:"\f17d"}.icon-skype:before{content:"\f17e"}.icon-foursquare:before{content:"\f180"}.icon-trello:before{content:"\f181"}.icon-female:before{content:"\f182"}.icon-male:before{content:"\f183"}.icon-gittip:before{content:"\f184"}.icon-sun:before{content:"\f185"}.icon-moon:before{content:"\f186"}.icon-archive:before{content:"\f187"}.icon-bug:before{content:"\f188"}.icon-vk:before{content:"\f189"}.icon-weibo:before{content:"\f18a"}.icon-renren:before{content:"\f18b"}.wy-alert,.rst-content .note,.rst-content .attention,.rst-content .caution,.rst-content .danger,.rst-content .error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .warning{padding:24px;line-height:24px;margin-bottom:24px;border-left:solid 3px transparent}.wy-alert strong,.rst-content .note strong,.rst-content .attention strong,.rst-content .caution strong,.rst-content .danger strong,.rst-content .error strong,.rst-content .hint strong,.rst-content .important strong,.rst-content .tip strong,.rst-content .warning strong,.wy-alert a,.rst-content .note a,.rst-content .attention a,.rst-content .caution a,.rst-content .danger a,.rst-content .error a,.rst-content .hint a,.rst-content .important a,.rst-content .tip a,.rst-content .warning a{color:#fff}.wy-alert.wy-alert-danger,.rst-content .wy-alert-danger.note,.rst-content .wy-alert-danger.attention,.rst-content .wy-alert-danger.caution,.rst-content .danger,.rst-content .error,.rst-content .wy-alert-danger.hint,.rst-content .wy-alert-danger.important,.rst-content .wy-alert-danger.tip,.rst-content .wy-alert-danger.warning{background:#e74c3c;color:#fff;border-color:#d62c1a}.wy-alert.wy-alert-warning,.rst-content .wy-alert-warning.note,.rst-content .attention,.rst-content .caution,.rst-content .wy-alert-warning.danger,.rst-content .wy-alert-warning.error,.rst-content .wy-alert-warning.hint,.rst-content .wy-alert-warning.important,.rst-content .wy-alert-warning.tip,.rst-content .warning{background:#e67e22;color:#fff;border-color:#bf6516}.wy-alert.wy-alert-info,.rst-content .note,.rst-content .wy-alert-info.attention,.rst-content .wy-alert-info.caution,.rst-content .wy-alert-info.danger,.rst-content .wy-alert-info.error,.rst-content .hint,.rst-content .important,.rst-content .tip,.rst-content .wy-alert-info.warning{background:#2980b9;color:#fff;border-color:#20638f}.wy-alert.wy-alert-success,.rst-content .wy-alert-success.note,.rst-content .wy-alert-success.attention,.rst-content .wy-alert-success.caution,.rst-content .wy-alert-success.danger,.rst-content .wy-alert-success.error,.rst-content .wy-alert-success.hint,.rst-content .wy-alert-success.important,.rst-content .wy-alert-success.tip,.rst-content .wy-alert-success.warning{background:#27ae60;color:#fff;border-color:#1e8449}.wy-alert.wy-alert-neutral,.rst-content .wy-alert-neutral.note,.rst-content .wy-alert-neutral.attention,.rst-content .wy-alert-neutral.caution,.rst-content .wy-alert-neutral.danger,.rst-content .wy-alert-neutral.error,.rst-content .wy-alert-neutral.hint,.rst-content .wy-alert-neutral.important,.rst-content .wy-alert-neutral.tip,.rst-content .wy-alert-neutral.warning{background:#f3f6f6;border-color:#e1e4e5}.wy-alert.wy-alert-neutral strong,.rst-content .wy-alert-neutral.note strong,.rst-content .wy-alert-neutral.attention strong,.rst-content .wy-alert-neutral.caution strong,.rst-content .wy-alert-neutral.danger strong,.rst-content .wy-alert-neutral.error strong,.rst-content .wy-alert-neutral.hint strong,.rst-content .wy-alert-neutral.important strong,.rst-content .wy-alert-neutral.tip strong,.rst-content .wy-alert-neutral.warning strong{color:#404040}.wy-alert.wy-alert-neutral a,.rst-content .wy-alert-neutral.note a,.rst-content .wy-alert-neutral.attention a,.rst-content .wy-alert-neutral.caution a,.rst-content .wy-alert-neutral.danger a,.rst-content .wy-alert-neutral.error a,.rst-content .wy-alert-neutral.hint a,.rst-content .wy-alert-neutral.important a,.rst-content .wy-alert-neutral.tip a,.rst-content .wy-alert-neutral.warning a{color:#2980b9}.wy-tray-container{position:fixed;top:-50px;left:0;width:100%;-webkit-transition:top 0.2s ease-in;-moz-transition:top 0.2s ease-in;transition:top 0.2s ease-in}.wy-tray-container.on{top:0}.wy-tray-container li{display:none;width:100%;background:#343131;padding:12px 24px;color:#fff;margin-bottom:6px;text-align:center;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1),0px -1px 2px -1px rgba(255,255,255,0.5) inset}.wy-tray-container li.wy-tray-item-success{background:#27ae60}.wy-tray-container li.wy-tray-item-info{background:#2980b9}.wy-tray-container li.wy-tray-item-warning{background:#e67e22}.wy-tray-container li.wy-tray-item-danger{background:#e74c3c}.btn{display:inline-block;*display:inline;zoom:1;line-height:normal;white-space:nowrap;vertical-align:baseline;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;font-size:100%;padding:6px 12px;color:#fff;border:1px solid rgba(0,0,0,0.1);border-bottom:solid 3px rgba(0,0,0,0.1);background-color:#27ae60;text-decoration:none;font-weight:500;box-shadow:0px 1px 2px -1px rgba(255,255,255,0.5) inset;-webkit-transition:all 0.1s linear;-moz-transition:all 0.1s linear;transition:all 0.1s linear;outline-none:false}.btn-hover{background:#2e8ece;color:#fff}.btn:hover{background:#2cc36b;color:#fff}.btn:focus{background:#2cc36b;color:#fff;outline:0}.btn:active{border-top:solid 3px rgba(0,0,0,0.1);border-bottom:solid 1px rgba(0,0,0,0.1);box-shadow:0px 1px 2px -1px rgba(0,0,0,0.5) inset}.btn[disabled]{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn-disabled:hover,.btn-disabled:focus,.btn-disabled:active{background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=40);opacity:0.4;cursor:not-allowed;box-shadow:none}.btn::-moz-focus-inner{padding:0;border:0}.btn-small{font-size:80%}.btn-info{background-color:#2980b9 !important}.btn-info:hover{background-color:#2e8ece !important}.btn-neutral{background-color:#f3f6f6 !important;color:#404040 !important}.btn-neutral:hover{background-color:#e5ebeb !important;color:#404040}.btn-danger{background-color:#e74c3c !important}.btn-danger:hover{background-color:#ea6153 !important}.btn-warning{background-color:#e67e22 !important}.btn-warning:hover{background-color:#e98b39 !important}.btn-invert{background-color:#343131}.btn-invert:hover{background-color:#413d3d !important}.btn-link{background-color:transparent !important;color:#2980b9;border-color:transparent}.btn-link:hover{background-color:transparent !important;color:#409ad5;border-color:transparent}.btn-link:active{background-color:transparent !important;border-color:transparent;border-top:solid 1px transparent;border-bottom:solid 3px transparent}.wy-btn-group .btn,.wy-control .btn{vertical-align:middle}.wy-btn-group{margin-bottom:24px;*zoom:1}.wy-btn-group:before,.wy-btn-group:after{display:table;content:""}.wy-btn-group:after{clear:both}.wy-dropdown{position:relative;display:inline-block}.wy-dropdown:hover .wy-dropdown-menu{display:block}.wy-dropdown .caret:after{font-family:fontawesome-webfont;content:"\f0d7";font-size:70%}.wy-dropdown-menu{position:absolute;top:100%;left:0;display:none;float:left;min-width:100%;background:#fcfcfc;z-index:100;border:solid 1px #cfd7dd;box-shadow:0 5px 5px 0 rgba(0,0,0,0.1);padding:12px}.wy-dropdown-menu>dd>a{display:block;clear:both;color:#404040;white-space:nowrap;font-size:90%;padding:0 12px}.wy-dropdown-menu>dd>a:hover{background:#2980b9;color:#fff}.wy-dropdown-menu>dd.divider{border-top:solid 1px #cfd7dd;margin:6px 0}.wy-dropdown-menu>dd.search{padding-bottom:12px}.wy-dropdown-menu>dd.search input[type="search"]{width:100%}.wy-dropdown-menu>dd.call-to-action{background:#e3e3e3;text-transform:uppercase;font-weight:500;font-size:80%}.wy-dropdown-menu>dd.call-to-action:hover{background:#e3e3e3}.wy-dropdown-menu>dd.call-to-action .btn{color:#fff}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu{background:#fcfcfc;margin-top:2px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a{padding:6px 12px}.wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover{background:#2980b9;color:#fff}.wy-dropdown.wy-dropdown-left .wy-dropdown-menu{right:0;text-align:right}.wy-dropdown-arrow:before{content:" ";border-bottom:5px solid #f5f5f5;border-left:5px solid transparent;border-right:5px solid transparent;position:absolute;display:block;top:-4px;left:50%;margin-left:-3px}.wy-dropdown-arrow.wy-dropdown-arrow-left:before{left:11px}.wy-form-stacked select{display:block}.wy-form-aligned input,.wy-form-aligned textarea,.wy-form-aligned select,.wy-form-aligned .wy-help-inline,.wy-form-aligned label{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-form-aligned .wy-control-group>label{display:inline-block;vertical-align:middle;width:10em;margin:0.5em 1em 0 0;float:left}.wy-form-aligned .wy-control{float:left}.wy-form-aligned .wy-control label{display:block}.wy-form-aligned .wy-control select{margin-top:0.5em}fieldset{border:0;margin:0;padding:0}legend{display:block;width:100%;border:0;padding:0;white-space:normal;margin-bottom:24px;font-size:150%;*margin-left:-7px}label{display:block;margin:0 0 0.3125em 0;color:#999;font-size:90%}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button{-webkit-appearance:button;cursor:pointer;*overflow:visible}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}button[disabled]{cursor:default}input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible}input[type="text"],input[type="password"],input[type="email"],input[type="url"],input[type="date"],input[type="month"],input[type="time"],input[type="datetime"],input[type="datetime-local"],input[type="week"],input[type="number"],input[type="search"],input[type="tel"],input[type="color"]{-webkit-appearance:none;padding:6px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;border-radius:0;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}input[type="datetime-local"]{padding:0.34375em 0.625em}input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0;margin-right:0.3125em;*height:13px;*width:13px}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}input[type="text"]:focus,input[type="password"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{outline:0;outline:thin dotted \9;border-color:#2980b9}input.no-focus:focus{border-color:#ccc !important}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:1px auto #129fea}input[type="text"][disabled],input[type="password"][disabled],input[type="email"][disabled],input[type="url"][disabled],input[type="date"][disabled],input[type="month"][disabled],input[type="time"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="week"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="color"][disabled]{cursor:not-allowed;background-color:#f3f6f6;color:#cad2d3}input:focus:invalid,textarea:focus:invalid,select:focus:invalid{color:#e74c3c;border:1px solid #e74c3c}input:focus:invalid:focus,textarea:focus:invalid:focus,select:focus:invalid:focus{border-color:#e9322d}input[type="file"]:focus:invalid:focus,input[type="radio"]:focus:invalid:focus,input[type="checkbox"]:focus:invalid:focus{outline-color:#e9322d}input.wy-input-large{padding:12px;font-size:100%}textarea{overflow:auto;vertical-align:top;width:100%}select,textarea{padding:0.5em 0.625em;display:inline-block;border:1px solid #ccc;font-size:0.8em;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}select{border:1px solid #ccc;background-color:#fff}select[multiple]{height:auto}select:focus,textarea:focus{outline:0}select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#fff;color:#cad2d3;border-color:transparent}.wy-checkbox,.wy-radio{margin:0.5em 0;color:#404040 !important;display:block}.wy-form-message-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle}.wy-input-prefix,.wy-input-suffix{white-space:nowrap}.wy-input-prefix .wy-input-context,.wy-input-suffix .wy-input-context{padding:6px;display:inline-block;font-size:80%;background-color:#f3f6f6;border:solid 1px #ccc;color:#999}.wy-input-suffix .wy-input-context{border-left:0}.wy-input-prefix .wy-input-context{border-right:0}.wy-inline-validate{white-space:nowrap}.wy-inline-validate .wy-input-context{padding:0.5em 0.625em;display:inline-block;font-size:80%}.wy-inline-validate.wy-inline-validate-success .wy-input-context{color:#27ae60}.wy-inline-validate.wy-inline-validate-danger .wy-input-context{color:#e74c3c}.wy-inline-validate.wy-inline-validate-warning .wy-input-context{color:#e67e22}.wy-inline-validate.wy-inline-validate-info .wy-input-context{color:#2980b9}.wy-control-group{margin-bottom:24px;*zoom:1}.wy-control-group:before,.wy-control-group:after{display:table;content:""}.wy-control-group:after{clear:both}.wy-control-group.wy-control-group-error .wy-form-message,.wy-control-group.wy-control-group-error label{color:#e74c3c}.wy-control-group.wy-control-group-error input[type="text"],.wy-control-group.wy-control-group-error input[type="password"],.wy-control-group.wy-control-group-error input[type="email"],.wy-control-group.wy-control-group-error input[type="url"],.wy-control-group.wy-control-group-error input[type="date"],.wy-control-group.wy-control-group-error input[type="month"],.wy-control-group.wy-control-group-error input[type="time"],.wy-control-group.wy-control-group-error input[type="datetime"],.wy-control-group.wy-control-group-error input[type="datetime-local"],.wy-control-group.wy-control-group-error input[type="week"],.wy-control-group.wy-control-group-error input[type="number"],.wy-control-group.wy-control-group-error input[type="search"],.wy-control-group.wy-control-group-error input[type="tel"],.wy-control-group.wy-control-group-error input[type="color"]{border:solid 2px #e74c3c}.wy-control-group.wy-control-group-error textarea{border:solid 2px #e74c3c}.wy-control-group.fluid-input input[type="text"],.wy-control-group.fluid-input input[type="password"],.wy-control-group.fluid-input input[type="email"],.wy-control-group.fluid-input input[type="url"],.wy-control-group.fluid-input input[type="date"],.wy-control-group.fluid-input input[type="month"],.wy-control-group.fluid-input input[type="time"],.wy-control-group.fluid-input input[type="datetime"],.wy-control-group.fluid-input input[type="datetime-local"],.wy-control-group.fluid-input input[type="week"],.wy-control-group.fluid-input input[type="number"],.wy-control-group.fluid-input input[type="search"],.wy-control-group.fluid-input input[type="tel"],.wy-control-group.fluid-input input[type="color"]{width:100%}.wy-form-message-inline{display:inline-block;padding-left:0.3em;color:#666;vertical-align:middle;font-size:90%}.wy-form-message{display:block;color:#ccc;font-size:70%;margin-top:0.3125em;font-style:italic}.wy-tag-input-group{padding:4px 4px 0px 4px;display:inline-block;border:1px solid #ccc;font-size:80%;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;box-shadow:inset 0 1px 3px #ddd;-webkit-transition:border 0.3s linear;-moz-transition:border 0.3s linear;transition:border 0.3s linear}.wy-tag-input-group .wy-tag{display:inline-block;background-color:rgba(0,0,0,0.1);padding:0.5em 0.625em;border-radius:2px;position:relative;margin-bottom:4px}.wy-tag-input-group .wy-tag .wy-tag-remove{color:#ccc;margin-left:5px}.wy-tag-input-group .wy-tag .wy-tag-remove:hover{color:#e74c3c}.wy-tag-input-group label{margin-left:5px;display:inline-block;margin-bottom:0}.wy-tag-input-group input{border:none;font-size:100%;margin-bottom:4px;box-shadow:none}.wy-form-upload{border:solid 1px #ccc;border-bottom:solid 3px #ccc;background-color:#fff;padding:24px;display:inline-block;text-align:center;cursor:pointer;color:#404040;-webkit-transition:border-color 0.1s ease-in;-moz-transition:border-color 0.1s ease-in;transition:border-color 0.1s ease-in;*zoom:1}.wy-form-upload:before,.wy-form-upload:after{display:table;content:""}.wy-form-upload:after{clear:both}@media screen and (max-width: 480px){.wy-form-upload{width:100%}}.wy-form-upload .image-drop{display:none}.wy-form-upload .image-desktop{display:none}.wy-form-upload .image-loading{display:none}.wy-form-upload .wy-form-upload-icon{display:block;font-size:32px;color:#b3b3b3}.wy-form-upload .image-drop .wy-form-upload-icon{color:#27ae60}.wy-form-upload p{font-size:90%}.wy-form-upload .wy-form-upload-image{float:left;margin-right:24px}@media screen and (max-width: 480px){.wy-form-upload .wy-form-upload-image{width:100%;margin-bottom:24px}}.wy-form-upload img{max-width:125px;max-height:125px;opacity:0.9;-webkit-transition:opacity 0.1s ease-in;-moz-transition:opacity 0.1s ease-in;transition:opacity 0.1s ease-in}.wy-form-upload .wy-form-upload-content{float:left}@media screen and (max-width: 480px){.wy-form-upload .wy-form-upload-content{width:100%}}.wy-form-upload:hover{border-color:#b3b3b3;color:#404040}.wy-form-upload:hover .image-desktop{display:block}.wy-form-upload:hover .image-drag{display:none}.wy-form-upload:hover img{opacity:1}.wy-form-upload:active{border-top:solid 3px #ccc;border-bottom:solid 1px #ccc}.wy-form-upload.wy-form-upload-big{width:100%;text-align:center;padding:72px}.wy-form-upload.wy-form-upload-big .wy-form-upload-content{float:none}.wy-form-upload.wy-form-upload-file p{margin-bottom:0}.wy-form-upload.wy-form-upload-file .wy-form-upload-icon{display:inline-block;font-size:inherit}.wy-form-upload.wy-form-upload-drop{background-color:#ddf7e8}.wy-form-upload.wy-form-upload-drop .image-drop{display:block}.wy-form-upload.wy-form-upload-drop .image-desktop{display:none}.wy-form-upload.wy-form-upload-drop .image-drag{display:none}.wy-form-upload.wy-form-upload-loading .image-drag{display:none}.wy-form-upload.wy-form-upload-loading .image-desktop{display:none}.wy-form-upload.wy-form-upload-loading .image-loading{display:block}.wy-form-upload.wy-form-upload-loading .wy-input-prefix{display:none}.wy-form-upload.wy-form-upload-loading p{margin-bottom:0}.rotate-90{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.rotate-180{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.rotate-270{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.mirror{-webkit-transform:scaleX(-1);-moz-transform:scaleX(-1);-ms-transform:scaleX(-1);-o-transform:scaleX(-1);transform:scaleX(-1)}.mirror.rotate-90{-webkit-transform:scaleX(-1) rotate(90deg);-moz-transform:scaleX(-1) rotate(90deg);-ms-transform:scaleX(-1) rotate(90deg);-o-transform:scaleX(-1) rotate(90deg);transform:scaleX(-1) rotate(90deg)}.mirror.rotate-180{-webkit-transform:scaleX(-1) rotate(180deg);-moz-transform:scaleX(-1) rotate(180deg);-ms-transform:scaleX(-1) rotate(180deg);-o-transform:scaleX(-1) rotate(180deg);transform:scaleX(-1) rotate(180deg)}.mirror.rotate-270{-webkit-transform:scaleX(-1) rotate(270deg);-moz-transform:scaleX(-1) rotate(270deg);-ms-transform:scaleX(-1) rotate(270deg);-o-transform:scaleX(-1) rotate(270deg);transform:scaleX(-1) rotate(270deg)}.wy-form-gallery-manage{margin-left:-12px;margin-right:-12px}.wy-form-gallery-manage li{float:left;padding:12px;width:20%;cursor:pointer}@media screen and (max-width: 768px){.wy-form-gallery-manage li{width:25%}}@media screen and (max-width: 480px){.wy-form-gallery-manage li{width:50%}}.wy-form-gallery-manage li:active{cursor:move}.wy-form-gallery-manage li>a{padding:12px;background-color:#fff;border:solid 1px #e1e4e5;border-bottom:solid 3px #e1e4e5;display:inline-block;-webkit-transition:all 0.1s ease-in;-moz-transition:all 0.1s ease-in;transition:all 0.1s ease-in}.wy-form-gallery-manage li>a:active{border:solid 1px #ccc;border-top:solid 3px #ccc}.wy-form-gallery-manage img{width:100%;-webkit-transition:all 0.05s ease-in;-moz-transition:all 0.05s ease-in;transition:all 0.05s ease-in}li.wy-form-gallery-edit{position:relative;color:#fff;padding:24px;width:100%;display:block;background-color:#343131;border-radius:4px}li.wy-form-gallery-edit .arrow{position:absolute;display:block;top:-50px;left:50%;margin-left:-25px;z-index:500;height:0;width:0;border-color:transparent;border-style:solid;border-width:25px;border-bottom-color:#343131}@media only screen and (max-width: 480px){.wy-form button[type="submit"]{margin:0.7em 0 0}.wy-form input[type="text"],.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0.3em;display:block}.wy-form label{margin-bottom:0.3em;display:block}.wy-form input[type="password"],.wy-form input[type="email"],.wy-form input[type="url"],.wy-form input[type="date"],.wy-form input[type="month"],.wy-form input[type="time"],.wy-form input[type="datetime"],.wy-form input[type="datetime-local"],.wy-form input[type="week"],.wy-form input[type="number"],.wy-form input[type="search"],.wy-form input[type="tel"],.wy-form input[type="color"]{margin-bottom:0}.wy-form-aligned .wy-control-group label{margin-bottom:0.3em;text-align:left;display:block;width:100%}.wy-form-aligned .wy-controls{margin:1.5em 0 0 0}.wy-form .wy-help-inline,.wy-form-message-inline,.wy-form-message{display:block;font-size:80%;padding:0.2em 0 0.8em}}@media screen and (max-width: 768px){.tablet-hide{display:none}}@media screen and (max-width: 480px){.mobile-hide{display:none}}.float-left{float:left}.float-right{float:right}.full-width{width:100%}.wy-grid-one-col{*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;max-width:1066px;margin-top:1.618em}.wy-grid-one-col:before,.wy-grid-one-col:after{display:table;content:""}.wy-grid-one-col:after{clear:both}.wy-grid-one-col section{display:block;float:left;margin-right:2.35765%;width:100%;background:#fcfcfc;padding:1.618em;margin-right:0}.wy-grid-one-col section:last-child{margin-right:0}.wy-grid-index-card{*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;max-width:460px;margin-top:1.618em;background:#fcfcfc;padding:1.618em}.wy-grid-index-card:before,.wy-grid-index-card:after{display:table;content:""}.wy-grid-index-card:after{clear:both}.wy-grid-index-card header,.wy-grid-index-card section,.wy-grid-index-card aside{display:block;float:left;margin-right:2.35765%;width:100%}.wy-grid-index-card header:last-child,.wy-grid-index-card section:last-child,.wy-grid-index-card aside:last-child{margin-right:0}.wy-grid-index-card.twocol{max-width:768px}.wy-grid-index-card.twocol section{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-grid-index-card.twocol section:last-child{margin-right:0}.wy-grid-index-card.twocol aside{display:block;float:left;margin-right:2.35765%;width:48.82117%}.wy-grid-index-card.twocol aside:last-child{margin-right:0}.wy-grid-search-filter{*zoom:1;max-width:68em;margin-left:auto;margin-right:auto;margin-bottom:24px}.wy-grid-search-filter:before,.wy-grid-search-filter:after{display:table;content:""}.wy-grid-search-filter:after{clear:both}.wy-grid-search-filter .wy-grid-search-filter-input{display:block;float:left;margin-right:2.35765%;width:74.41059%}.wy-grid-search-filter .wy-grid-search-filter-input:last-child{margin-right:0}.wy-grid-search-filter .wy-grid-search-filter-btn{display:block;float:left;margin-right:2.35765%;width:23.23176%}.wy-grid-search-filter .wy-grid-search-filter-btn:last-child{margin-right:0}.wy-table,.rst-content table.docutils,.rst-content table.field-list{border-collapse:collapse;border-spacing:0;empty-cells:show;margin-bottom:24px}.wy-table caption,.rst-content table.docutils caption,.rst-content table.field-list caption{color:#000;font:italic 85%/1 arial,sans-serif;padding:1em 0;text-align:center}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td,.wy-table th,.rst-content table.docutils th,.rst-content table.field-list th{font-size:90%;margin:0;overflow:visible;padding:8px 16px}.wy-table td:first-child,.rst-content table.docutils td:first-child,.rst-content table.field-list td:first-child,.wy-table th:first-child,.rst-content table.docutils th:first-child,.rst-content table.field-list th:first-child{border-left-width:0}.wy-table thead,.rst-content table.docutils thead,.rst-content table.field-list thead{color:#000;text-align:left;vertical-align:bottom;white-space:nowrap}.wy-table thead th,.rst-content table.docutils thead th,.rst-content table.field-list thead th{font-weight:bold;border-bottom:solid 2px #e1e4e5}.wy-table td,.rst-content table.docutils td,.rst-content table.field-list td{background-color:transparent;vertical-align:middle}.wy-table td p,.rst-content table.docutils td p,.rst-content table.field-list td p{line-height:18px;margin-bottom:0}.wy-table .wy-table-cell-min,.rst-content table.docutils .wy-table-cell-min,.rst-content table.field-list .wy-table-cell-min{width:1%;padding-right:0}.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox],.wy-table .wy-table-cell-min input[type=checkbox],.rst-content table.docutils .wy-table-cell-min input[type=checkbox],.rst-content table.field-list .wy-table-cell-min input[type=checkbox]{margin:0}.wy-table-secondary{color:gray;font-size:90%}.wy-table-tertiary{color:gray;font-size:80%}.wy-table-odd td,.wy-table-striped tr:nth-child(2n-1) td,.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:#f3f6f6}.wy-table-backed{background-color:#f3f6f6}.wy-table-bordered-all,.rst-content table.docutils{border:1px solid #e1e4e5}.wy-table-bordered-all td,.rst-content table.docutils td{border-bottom:1px solid #e1e4e5;border-left:1px solid #e1e4e5}.wy-table-bordered-all tbody>tr:last-child td,.rst-content table.docutils tbody>tr:last-child td{border-bottom-width:0}.wy-table-bordered{border:1px solid #e1e4e5}.wy-table-bordered-rows td{border-bottom:1px solid #e1e4e5}.wy-table-bordered-rows tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-horizontal td,.wy-table-horizontal th{border-width:0 0 1px 0;border-bottom:1px solid #e1e4e5}.wy-table-horizontal tbody>tr:last-child td{border-bottom-width:0}.wy-table-responsive{margin-bottom:24px;max-width:100%;overflow:auto}.wy-table-responsive table{margin-bottom:0 !important}.wy-table-responsive table td,.wy-table-responsive table th{white-space:nowrap}html{height:100%;overflow-x:hidden}body{font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;font-weight:normal;color:#404040;min-height:100%;overflow-x:hidden;background:#edf0f2}a{color:#2980b9;text-decoration:none}a:hover{color:#3091d1}.link-danger{color:#e74c3c}.link-danger:hover{color:#d62c1a}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}h1,h2,h3,h4,h5,h6,legend{margin-top:0;font-weight:700;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif}p{line-height:24px;margin:0;font-size:16px;margin-bottom:24px}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}small{font-size:80%}code,.rst-content tt{white-space:nowrap;max-width:100%;background:#fff;border:solid 1px #e1e4e5;font-size:75%;padding:0 5px;font-family:"Incosolata","Consolata","Monaco",monospace;color:#e74c3c;overflow-x:auto}code.code-large,.rst-content tt.code-large{font-size:90%}.full-width{width:100%}.wy-plain-list-disc,.rst-content .section ul,.rst-content .toctree-wrapper ul{list-style:disc;line-height:24px;margin-bottom:24px}.wy-plain-list-disc li,.rst-content .section ul li,.rst-content .toctree-wrapper ul li{list-style:disc;margin-left:24px}.wy-plain-list-disc li ul,.rst-content .section ul li ul,.rst-content .toctree-wrapper ul li ul{margin-bottom:0}.wy-plain-list-disc li li,.rst-content .section ul li li,.rst-content .toctree-wrapper ul li li{list-style:circle}.wy-plain-list-disc li li li,.rst-content .section ul li li li,.rst-content .toctree-wrapper ul li li li{list-style:square}.wy-plain-list-decimal,.rst-content .section ol,.rst-content ol.arabic{list-style:decimal;line-height:24px;margin-bottom:24px}.wy-plain-list-decimal li,.rst-content .section ol li,.rst-content ol.arabic li{list-style:decimal;margin-left:24px}.wy-type-large{font-size:120%}.wy-type-normal{font-size:100%}.wy-type-small{font-size:100%}.wy-type-strike{text-decoration:line-through}.wy-text-warning{color:#e67e22 !important}a.wy-text-warning:hover{color:#eb9950 !important}.wy-text-info{color:#2980b9 !important}a.wy-text-info:hover{color:#409ad5 !important}.wy-text-success{color:#27ae60 !important}a.wy-text-success:hover{color:#36d278 !important}.wy-text-danger{color:#e74c3c !important}a.wy-text-danger:hover{color:#ed7669 !important}.wy-text-neutral{color:#404040 !important}a.wy-text-neutral:hover{color:#595959 !important}.codeblock-example{border:1px solid #e1e4e5;border-bottom:none;padding:24px;padding-top:48px;font-weight:500;background:#fff;position:relative}.codeblock-example:after{content:"Example";position:absolute;top:0px;left:0px;background:#9b59b6;color:#fff;padding:6px 12px}.codeblock-example.prettyprint-example-only{border:1px solid #e1e4e5;margin-bottom:24px}.codeblock,div[class^='highlight']{border:1px solid #e1e4e5;padding:0px;overflow-x:auto;background:#fff;margin:1px 0 24px 0}.codeblock div[class^='highlight'],div[class^='highlight'] div[class^='highlight']{border:none;background:none;margin:0}.linenodiv pre{border-right:solid 1px #e6e9ea;margin:0;padding:12px 12px;font-family:"Incosolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;color:#d9d9d9}div[class^='highlight'] pre{white-space:pre;margin:0;padding:12px 12px;font-family:"Incosolata","Consolata","Monaco",monospace;font-size:12px;line-height:1.5;display:block;overflow:auto;color:#404040}pre.literal-block{@extends .codeblock;}@media print{.codeblock,div[class^='highlight'],div[class^='highlight'] pre{white-space:pre-wrap}}.hll{background-color:#f8f8f8;border:1px solid #ccc;padding:1.5px 5px}.c{color:#998;font-style:italic}.err{color:#a61717;background-color:#e3d2d2}.k{font-weight:bold}.o{font-weight:bold}.cm{color:#998;font-style:italic}.cp{color:#999;font-weight:bold}.c1{color:#998;font-style:italic}.cs{color:#999;font-weight:bold;font-style:italic}.gd{color:#000;background-color:#fdd}.gd .x{color:#000;background-color:#faa}.ge{font-style:italic}.gr{color:#a00}.gh{color:#999}.gi{color:#000;background-color:#dfd}.gi .x{color:#000;background-color:#afa}.go{color:#888}.gp{color:#555}.gs{font-weight:bold}.gu{color:purple;font-weight:bold}.gt{color:#a00}.kc{font-weight:bold}.kd{font-weight:bold}.kn{font-weight:bold}.kp{font-weight:bold}.kr{font-weight:bold}.kt{color:#458;font-weight:bold}.m{color:#099}.s{color:#d14}.n{color:#333}.na{color:teal}.nb{color:#0086b3}.nc{color:#458;font-weight:bold}.no{color:teal}.ni{color:purple}.ne{color:#900;font-weight:bold}.nf{color:#900;font-weight:bold}.nn{color:#555}.nt{color:navy}.nv{color:teal}.ow{font-weight:bold}.w{color:#bbb}.mf{color:#099}.mh{color:#099}.mi{color:#099}.mo{color:#099}.sb{color:#d14}.sc{color:#d14}.sd{color:#d14}.s2{color:#d14}.se{color:#d14}.sh{color:#d14}.si{color:#d14}.sx{color:#d14}.sr{color:#009926}.s1{color:#d14}.ss{color:#990073}.bp{color:#999}.vc{color:teal}.vg{color:teal}.vi{color:teal}.il{color:#099}.gc{color:#999;background-color:#eaf2f5}.wy-breadcrumbs li{display:inline-block}.wy-breadcrumbs li.wy-breadcrumbs-aside{float:right}.wy-breadcrumbs li a{display:inline-block;padding:5px}.wy-breadcrumbs li a:first-child{padding-left:0}.wy-breadcrumbs-extra{margin-bottom:0;color:#b3b3b3;font-size:80%;display:inline-block}@media screen and (max-width: 480px){.wy-breadcrumbs-extra{display:none}.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}@media print{.wy-breadcrumbs li.wy-breadcrumbs-aside{display:none}}.wy-affix{position:fixed;top:1.618em}.wy-menu a:hover{text-decoration:none}.wy-menu-horiz{*zoom:1}.wy-menu-horiz:before,.wy-menu-horiz:after{display:table;content:""}.wy-menu-horiz:after{clear:both}.wy-menu-horiz ul,.wy-menu-horiz li{display:inline-block}.wy-menu-horiz li:hover{background:rgba(255,255,255,0.1)}.wy-menu-horiz li.divide-left{border-left:solid 1px #404040}.wy-menu-horiz li.divide-right{border-right:solid 1px #404040}.wy-menu-horiz a{height:32px;display:inline-block;line-height:32px;padding:0 16px}.wy-menu-vertical header{height:32px;display:inline-block;line-height:32px;padding:0 1.618em;display:block;font-weight:bold;text-transform:uppercase;font-size:80%;color:#2980b9;white-space:nowrap}.wy-menu-vertical ul{margin-bottom:0}.wy-menu-vertical li.divide-top{border-top:solid 1px #404040}.wy-menu-vertical li.divide-bottom{border-bottom:solid 1px #404040}.wy-menu-vertical li.current{background:#e3e3e3}.wy-menu-vertical li.current a{color:gray;border-right:solid 1px #c9c9c9;padding:0.4045em 2.427em}.wy-menu-vertical li.current a:hover{background:#d6d6d6}.wy-menu-vertical li.on a,.wy-menu-vertical li.current>a{color:#404040;padding:0.4045em 1.618em;font-weight:bold;position:relative;background:#fcfcfc;border:none;border-bottom:solid 1px #c9c9c9;border-top:solid 1px #c9c9c9;padding-left:1.618em -4px}.wy-menu-vertical li.on a:hover,.wy-menu-vertical li.current>a:hover{background:#fcfcfc}.wy-menu-vertical li.tocktree-l2.current>a{background:#c9c9c9}.wy-menu-vertical li.current ul{display:block}.wy-menu-vertical li ul{margin-bottom:0;display:none}.wy-menu-vertical li ul li a{margin-bottom:0;color:#b3b3b3;font-weight:normal}.wy-menu-vertical a{display:inline-block;line-height:18px;padding:0.4045em 1.618em;display:block;position:relative;font-size:90%;color:#b3b3b3}.wy-menu-vertical a:hover{background-color:#4e4a4a;cursor:pointer}.wy-menu-vertical a:active{background-color:#2980b9;cursor:pointer;color:#fff}.wy-side-nav-search{z-index:200;background-color:#2980b9;text-align:center;padding:0.809em;display:block;color:#fcfcfc;margin-bottom:0.809em}.wy-side-nav-search input[type=text]{width:100%;border-radius:50px;padding:6px 12px;border-color:#2472a4}.wy-side-nav-search img{display:block;margin:auto auto 0.809em auto;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-side-nav-search>a,.wy-side-nav-search .wy-dropdown>a{color:#fcfcfc;font-size:100%;font-weight:bold;display:inline-block;padding:4px 6px;margin-bottom:0.809em}.wy-side-nav-search>a:hover,.wy-side-nav-search .wy-dropdown>a:hover{background:rgba(255,255,255,0.1)}.wy-nav .wy-menu-vertical header{color:#2980b9}.wy-nav .wy-menu-vertical a{color:#b3b3b3}.wy-nav .wy-menu-vertical a:hover{background-color:#2980b9;color:#fff}[data-menu-wrap]{-webkit-transition:all 0.2s ease-in;-moz-transition:all 0.2s ease-in;transition:all 0.2s ease-in;position:absolute;opacity:1;width:100%;opacity:0}[data-menu-wrap].move-center{left:0;right:auto;opacity:1}[data-menu-wrap].move-left{right:auto;left:-100%;opacity:0}[data-menu-wrap].move-right{right:-100%;left:auto;opacity:0}.wy-body-for-nav{background:left repeat-y #fcfcfc;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC);background-size:300px 1px}.wy-grid-for-nav{position:absolute;width:100%;height:100%}.wy-nav-side{position:absolute;top:0;left:0;width:300px;overflow:hidden;min-height:100%;background:#343131;z-index:200}.wy-nav-top{display:none;background:#2980b9;color:#fff;padding:0.4045em 0.809em;position:relative;line-height:50px;text-align:center;font-size:100%;*zoom:1}.wy-nav-top:before,.wy-nav-top:after{display:table;content:""}.wy-nav-top:after{clear:both}.wy-nav-top a{color:#fff;font-weight:bold}.wy-nav-top img{margin-right:12px;height:45px;width:45px;background-color:#2980b9;padding:5px;border-radius:100%}.wy-nav-top i{font-size:30px;float:left;cursor:pointer}.wy-nav-content-wrap{margin-left:300px;background:#fcfcfc;min-height:100%}.wy-nav-content{padding:1.618em 3.236em;height:100%;max-width:800px;margin:auto}.wy-body-mask{position:fixed;width:100%;height:100%;background:rgba(0,0,0,0.2);display:none;z-index:499}.wy-body-mask.on{display:block}footer{color:#999}footer p{margin-bottom:12px}.rst-footer-buttons{*zoom:1}.rst-footer-buttons:before,.rst-footer-buttons:after{display:table;content:""}.rst-footer-buttons:after{clear:both}#search-results .search li{margin-bottom:24px;border-bottom:solid 1px #e1e4e5;padding-bottom:24px}#search-results .search li:first-child{border-top:solid 1px #e1e4e5;padding-top:24px}#search-results .search li a{font-size:120%;margin-bottom:12px;display:inline-block}#search-results .context{color:gray;font-size:90%}@media screen and (max-width: 768px){.wy-body-for-nav{background:#fcfcfc}.wy-nav-top{display:block}.wy-nav-side{left:-300px}.wy-nav-side.shift{width:85%;left:0}.wy-nav-content-wrap{margin-left:0}.wy-nav-content-wrap .wy-nav-content{padding:1.618em}.wy-nav-content-wrap.shift{position:fixed;min-width:100%;left:85%;top:0;height:100%;overflow:hidden}}@media screen and (min-width: 1400px){.wy-nav-content-wrap{background:rgba(0,0,0,0.05)}.wy-nav-content{margin:0;background:#fcfcfc}}@media print{.wy-nav-side{display:none}.wy-nav-content-wrap{margin-left:0}}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .icon,.rst-versions .rst-current-version .wy-inline-validate.wy-inline-validate-success .wy-input-context,.wy-inline-validate.wy-inline-validate-success .rst-versions .rst-current-version .wy-input-context,.rst-versions .rst-current-version .wy-inline-validate.wy-inline-validate-danger .wy-input-context,.wy-inline-validate.wy-inline-validate-danger .rst-versions .rst-current-version .wy-input-context,.rst-versions .rst-current-version .wy-inline-validate.wy-inline-validate-warning .wy-input-context,.wy-inline-validate.wy-inline-validate-warning .rst-versions .rst-current-version .wy-input-context,.rst-versions .rst-current-version .wy-inline-validate.wy-inline-validate-info .wy-input-context,.wy-inline-validate.wy-inline-validate-info .rst-versions .rst-current-version .wy-input-context,.rst-versions .rst-current-version .wy-tag-input-group .wy-tag .wy-tag-remove,.wy-tag-input-group .wy-tag .rst-versions .rst-current-version .wy-tag-remove,.rst-versions .rst-current-version .rst-content .admonition-title,.rst-content .rst-versions .rst-current-version .admonition-title,.rst-versions .rst-current-version .rst-content h1 .headerlink,.rst-content h1 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h2 .headerlink,.rst-content h2 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h3 .headerlink,.rst-content h3 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h4 .headerlink,.rst-content h4 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h5 .headerlink,.rst-content h5 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content h6 .headerlink,.rst-content h6 .rst-versions .rst-current-version .headerlink,.rst-versions .rst-current-version .rst-content dl dt .headerlink,.rst-content dl dt .rst-versions .rst-current-version .headerlink{color:#fcfcfc}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}}.rst-content img{max-width:100%;height:auto !important}.rst-content .section>img{margin-bottom:24px}.rst-content a.reference.external:after{font-family:fontawesome-webfont;content:" \f08e ";color:#b3b3b3;vertical-align:super;font-size:60%}.rst-content blockquote{margin-left:24px;line-height:24px;margin-bottom:24px}.rst-content .note .last,.rst-content .note p.first,.rst-content .attention .last,.rst-content .attention p.first,.rst-content .caution .last,.rst-content .caution p.first,.rst-content .danger .last,.rst-content .danger p.first,.rst-content .error .last,.rst-content .error p.first,.rst-content .hint .last,.rst-content .hint p.first,.rst-content .important .last,.rst-content .important p.first,.rst-content .tip .last,.rst-content .tip p.first,.rst-content .warning .last,.rst-content .warning p.first{margin-bottom:0}.rst-content .admonition-title{font-weight:bold}.rst-content .admonition-title:before{margin-right:4px}.rst-content .admonition table{border-color:rgba(0,0,0,0.1)}.rst-content .admonition table td,.rst-content .admonition table th{background:transparent !important;border-color:rgba(0,0,0,0.1) !important}.rst-content .section ol.loweralpha,.rst-content .section ol.loweralpha li{list-style:lower-alpha}.rst-content .section ol.upperalpha,.rst-content .section ol.upperalpha li{list-style:upper-alpha}.rst-content .section ol p,.rst-content .section ul p{margin-bottom:12px}.rst-content .line-block{margin-left:24px}.rst-content .topic-title{font-weight:bold;margin-bottom:12px}.rst-content .toc-backref{color:#404040}.rst-content .align-right{float:right;margin:0px 0px 24px 24px}.rst-content .align-left{float:left;margin:0px 24px 24px 0px}.rst-content h1 .headerlink,.rst-content h2 .headerlink,.rst-content h3 .headerlink,.rst-content h4 .headerlink,.rst-content h5 .headerlink,.rst-content h6 .headerlink,.rst-content dl dt .headerlink{display:none;visibility:hidden;font-size:14px}.rst-content h1 .headerlink:after,.rst-content h2 .headerlink:after,.rst-content h3 .headerlink:after,.rst-content h4 .headerlink:after,.rst-content h5 .headerlink:after,.rst-content h6 .headerlink:after,.rst-content dl dt .headerlink:after{visibility:visible;content:"\f0c1";font-family:fontawesome-webfont;display:inline-block}.rst-content h1:hover .headerlink,.rst-content h2:hover .headerlink,.rst-content h3:hover .headerlink,.rst-content h4:hover .headerlink,.rst-content h5:hover .headerlink,.rst-content h6:hover .headerlink,.rst-content dl dt:hover .headerlink{display:inline-block}.rst-content .sidebar{float:right;width:40%;display:block;margin:0 0 24px 24px;padding:24px;background:#f3f6f6;border:solid 1px #e1e4e5}.rst-content .sidebar p,.rst-content .sidebar ul,.rst-content .sidebar dl{font-size:90%}.rst-content .sidebar .last{margin-bottom:0}.rst-content .sidebar .sidebar-title{display:block;font-family:"Roboto Slab","ff-tisa-web-pro","Georgia",Arial,sans-serif;font-weight:bold;background:#e1e4e5;padding:6px 12px;margin:-24px;margin-bottom:24px;font-size:100%}.rst-content .highlighted{background:#f1c40f;display:inline-block;font-weight:bold;padding:0 6px}.rst-content .footnote-reference,.rst-content .citation-reference{vertical-align:super;font-size:90%}.rst-content table.docutils.citation,.rst-content table.docutils.footnote{background:none;border:none;color:#999}.rst-content table.docutils.citation td,.rst-content table.docutils.citation tr,.rst-content table.docutils.footnote td,.rst-content table.docutils.footnote tr{border:none;background-color:transparent !important;white-space:normal}.rst-content table.docutils.citation td.label,.rst-content table.docutils.footnote td.label{padding-left:0;padding-right:0;vertical-align:top}.rst-content table.field-list{border:none}.rst-content table.field-list td{border:none}.rst-content table.field-list .field-name{padding-right:10px;text-align:left}.rst-content table.field-list .field-body{text-align:left;padding-left:0}.rst-content tt{color:#000}.rst-content tt big,.rst-content tt em{font-size:100% !important;line-height:normal}.rst-content tt .xref,a .rst-content tt{font-weight:bold}.rst-content dl{margin-bottom:24px}.rst-content dl dt{font-weight:bold}.rst-content dl p,.rst-content dl table,.rst-content dl ul,.rst-content dl ol{margin-bottom:12px !important}.rst-content dl dd{margin:0 0 12px 24px}.rst-content dl:not(.docutils){margin-bottom:24px}.rst-content dl:not(.docutils) dt{display:inline-block;margin:6px 0;font-size:90%;line-height:normal;background:#e7f2fa;color:#2980b9;border-top:solid 3px #6ab0de;padding:6px;position:relative}.rst-content dl:not(.docutils) dt:before{color:#6ab0de}.rst-content dl:not(.docutils) dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dl dt{margin-bottom:6px;border:none;border-left:solid 3px #ccc;background:#f0f0f0;color:gray}.rst-content dl:not(.docutils) dl dt .headerlink{color:#404040;font-size:100% !important}.rst-content dl:not(.docutils) dt:first-child{margin-top:0}.rst-content dl:not(.docutils) tt{font-weight:bold}.rst-content dl:not(.docutils) tt.descname,.rst-content dl:not(.docutils) tt.descclassname{background-color:transparent;border:none;padding:0;font-size:100% !important}.rst-content dl:not(.docutils) tt.descname{font-weight:bold}.rst-content dl:not(.docutils) .viewcode-link{display:inline-block;color:#27ae60;font-size:80%;padding-left:24px}.rst-content dl:not(.docutils) .optional{display:inline-block;padding:0 4px;color:#000;font-weight:bold}.rst-content dl:not(.docutils) .property{display:inline-block;padding-right:8px}@media screen and (max-width: 480px){.rst-content .sidebar{width:100%}}span[id*='MathJax-Span']{color:#404040}.admonition.note span[id*='MathJax-Span']{color:#fff}.admonition.warning span[id*='MathJax-Span']{color:#fff} diff --git a/docsite/_themes/srtd/static/font/fontawesome_webfont.eot b/docsite/_themes/srtd/static/font/fontawesome_webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..0662cb96bfb78cb2603df4bc9995314bd6806312 GIT binary patch literal 37405 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�-A&iY%khEsGVMaq&$BJA^QAs8x>7-g_ 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 k3oQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docsite/_themes/srtd/static/font/fontawesome_webfont.ttf b/docsite/_themes/srtd/static/font/fontawesome_webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d3659246915cacb0c9204271f1f9fc5f77049eac GIT binary patch literal 79076 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 literal 0 HcmV?d00001 diff --git a/docsite/_themes/srtd/static/js/theme.js b/docsite/_themes/srtd/static/js/theme.js new file mode 100644 index 0000000000..58e514c0c3 --- /dev/null +++ b/docsite/_themes/srtd/static/js/theme.js @@ -0,0 +1,16 @@ +$( document ).ready(function() { + // Shift nav in mobile when clicking the menu. + $("[data-toggle='wy-nav-top']").click(function() { + $("[data-toggle='wy-nav-shift']").toggleClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); + }); + // Close menu when you click a link. + $(".wy-menu-vertical .current ul li a").click(function() { + $("[data-toggle='wy-nav-shift']").removeClass("shift"); + $("[data-toggle='rst-versions']").toggleClass("shift"); + }); + $("[data-toggle='rst-current-version']").click(function() { + $("[data-toggle='rst-versions']").toggleClass("shift-up"); + }); + $("table.docutils:not(.field-list").wrap("

    "); +}); diff --git a/docsite/_themes/srtd/theme.conf b/docsite/_themes/srtd/theme.conf new file mode 100644 index 0000000000..d70cf98f37 --- /dev/null +++ b/docsite/_themes/srtd/theme.conf @@ -0,0 +1,7 @@ +[theme] +inherit = basic +stylesheet = css/theme.css + +[options] +typekit_id = hiw1hhg +analytics_id = diff --git a/docsite/_themes/srtd/versions.html b/docsite/_themes/srtd/versions.html new file mode 100644 index 0000000000..93319be892 --- /dev/null +++ b/docsite/_themes/srtd/versions.html @@ -0,0 +1,37 @@ +{% if READTHEDOCS %} +{# Add rst-badge after rst-versions for small badge style. #} +
    + + Read the Docs + v: {{ current_version }} + + +
    +
    +
    Versions
    + {% for slug, url in versions %} +
    {{ slug }}
    + {% endfor %} +
    +
    +
    Downloads
    + {% for type, url in downloads %} +
    {{ type }}
    + {% endfor %} +
    +
    +
    On Read the Docs
    +
    + Project Home +
    +
    + Builds +
    +
    +
    + Free document hosting provided by Read the Docs. + +
    +
    +{% endif %} + diff --git a/docsite/conf.py b/docsite/conf.py index 5615ff135f..fae3a156f2 100644 --- a/docsite/conf.py +++ b/docsite/conf.py @@ -17,16 +17,16 @@ import sys import os # pip install sphinx_rtd_theme -import sphinx_rtd_theme -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +#import sphinx_rtd_theme +#html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. #sys.path.append(os.path.abspath('some/directory')) # -#sys.path.insert(0, os.path.join('ansible', 'lib')) -#sys.path.append(os.path.abspath('_themes')) +sys.path.insert(0, os.path.join('ansible', 'lib')) +sys.path.append(os.path.abspath('_themes')) VERSION='0.01' AUTHOR='AnsibleWorks' @@ -104,8 +104,8 @@ pygments_style = 'sphinx' # Options for HTML output # ----------------------- -#html_theme_path = ['_themes'] -html_theme = 'sphinx_rtd_theme' +html_theme_path = ['_themes'] +html_theme = 'srtd' html_short_title = 'Ansible Documentation' # The style sheet to use for HTML and HTML Help pages. A file of that name From 34f65f8e8422d626a6f7cf66212712f52cf5402b Mon Sep 17 00:00:00 2001 From: Mikhail Sobolev Date: Wed, 25 Dec 2013 01:19:41 +0200 Subject: [PATCH 030/182] fix the link formatting --- docsite/rst/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docsite/rst/faq.rst b/docsite/rst/faq.rst index 9b8631d96a..cf2a2cfe38 100644 --- a/docsite/rst/faq.rst +++ b/docsite/rst/faq.rst @@ -196,7 +196,7 @@ Is there a web interface / REST API / etc? ++++++++++++++++++++++++++++++++++++++++++ Yes! AnsibleWorks makes a great product that makes Ansible even more powerful -and easy to use: `AnsibleWorks AWX ` +and easy to use: `AnsibleWorks AWX `_. .. _docs_contributions: From d1d20b7065f2faa467c1826e864f4c670a6ab7f7 Mon Sep 17 00:00:00 2001 From: Mikhail Sobolev Date: Wed, 25 Dec 2013 01:20:00 +0200 Subject: [PATCH 031/182] add a link to the resources section --- docsite/rst/faq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docsite/rst/faq.rst b/docsite/rst/faq.rst index cf2a2cfe38..90ae12ac8b 100644 --- a/docsite/rst/faq.rst +++ b/docsite/rst/faq.rst @@ -212,7 +212,7 @@ I don't see my question here We're happy to help. -See the "Resources" section of the documentation home page for a link to the IRC and Google Group. +See the `Resources `_ section of the documentation home page for a link to the IRC and Google Group. .. seealso:: From 99daaa5bb50d4d3f4f1d91c100b6dab00830851f Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Tue, 24 Dec 2013 18:51:46 -0500 Subject: [PATCH 032/182] Re-add the playbook introduction which was replaced earlier. --- docsite/rst/modules.rst | 2 +- docsite/rst/playbooks.rst | 1 + docsite/rst/playbooks_intro.rst | 394 ++++++++++++++++++++++++++++++++ 3 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 docsite/rst/playbooks_intro.rst diff --git a/docsite/rst/modules.rst b/docsite/rst/modules.rst index 7ec0a1fb05..ac922c965c 100644 --- a/docsite/rst/modules.rst +++ b/docsite/rst/modules.rst @@ -2,7 +2,7 @@ Ansible Modules =============== .. toctree:: - :maxdepth: 2 + :maxdepth: 4 .. _modules_intro: diff --git a/docsite/rst/playbooks.rst b/docsite/rst/playbooks.rst index 9fe8a8a661..20af4e02fc 100644 --- a/docsite/rst/playbooks.rst +++ b/docsite/rst/playbooks.rst @@ -18,6 +18,7 @@ It is recommended to look at `Example Playbooks `_. We'd recommend +looking at these in another tab as you go along. + +There are also many jumping off points after you learn playbooks, so hop back to the documentation +index after you're done with this section. + +.. _playbook_language_example: + +Playbook Language Example +````````````````````````` + +Playbooks are expressed in YAML format (see :doc:`YAMLSyntax`) and have a minimum of syntax, which intentionally +tries to not be a programming language or script, but rather a model of a configuration or a process. + +Each playbook is composed of one or more 'plays' in a list. + +The goal of a play is to map a group of hosts to some well defined roles, represented by +things ansible calls tasks. At a basic level, a task is nothing more than a call +to an ansible module, which you should have learned about in earlier chapters. + +By composing a playbook of multiple 'plays', it is possible to +orchestrate multi-machine deployments, running certain steps on all +machines in the webservers group, then certain steps on the database +server group, then more commands back on the webservers group, etc. + +"plays" are more or less a sports analogy. You can have quite a lot of plays that affect your systems +to do different things. It's not as if you were just defining one particular state or model, and you +can run different plays at different times. + +For starters, here's a playbook that contains just one play:: + + --- + - hosts: webservers + vars: + http_port: 80 + max_clients: 200 + remote_user: root + tasks: + - name: ensure apache is at the latest version + yum: pkg=httpd state=latest + - name: write the apache config file + template: src=/srv/httpd.j2 dest=/etc/httpd.conf + notify: + - restart apache + - name: ensure apache is running + service: name=httpd state=started + handlers: + - name: restart apache + service: name=httpd state=restarted + +Below, we'll break down what the various features of the playbook language are. + +.. _playbook_basics: + +Basics +`````` + +.. _playbook_hosts_and_users: + +Hosts and Users ++++++++++++++++ + +For each play in a playbook, you get to choose which machines in your infrastructure +to target and what remote user to complete the steps (called tasks) as. + +The `hosts` line is a list of one or more groups or host patterns, +separated by colons, as described in the :doc:`intro_patterns` +documentation. The `remote_user` is just the name of the user account:: + + --- + - hosts: webservers + remote_user: root + +.. Note:: + + The `remote_user` parameter was formerly called just `user`. It was renamed in Ansible 1.4 to make it more distinguishable from the `user` module (used to create users on remote systems). + +Remote users can also be defined per task:: + + --- + - hosts: webservers + remote_user: root + tasks: + - name: test connection + ping: + remote_user: yourname + +.. Note:: + + The `remote_user` parameter for tasks was added in 1.4. + + +Support for running things from sudo is also available:: + + --- + - hosts: webservers + remote_user: yourname + sudo: yes + +You can also use sudo on a particular task instead of the whole play:: + + --- + - hosts: webservers + remote_user: yourname + tasks: + - service: name=nginx state=started + sudo: yes + + +You can also login as you, and then sudo to different users than root:: + + --- + - hosts: webservers + remote_user: yourname + sudo: yes + sudo_user: postgres + +If you need to specify a password to sudo, run `ansible-playbook` with ``--ask-sudo-pass`` (`-K`). +If you run a sudo playbook and the playbook seems to hang, it's probably stuck at the sudo prompt. +Just `Control-C` to kill it and run it again with `-K`. + +.. important:: + + When using `sudo_user` to a user other than root, the module + arguments are briefly written into a random tempfile in /tmp. + These are deleted immediately after the command is executed. This + only occurs when sudoing from a user like 'bob' to 'timmy', not + when going from 'bob' to 'root', or logging in directly as 'bob' or + 'root'. If this concerns you that this data is briefly readable + (not writable), avoid transferring uncrypted passwords with + `sudo_user` set. In other cases, '/tmp' is not used and this does + not come into play. Ansible also takes care to not log password + parameters. + +.. _tasks_list: + +Tasks list +++++++++++ + +Each play contains a list of tasks. Tasks are executed in order, one +at a time, against all machines matched by the host pattern, +before moving on to the next task. It is important to understand that, within a play, +all hosts are going to get the same task directives. It is the purpose of a play to map +a selection of hosts to tasks. + +When running the playbook, which runs top to bottom, hosts with failed tasks are +taken out of the rotation for the entire playbook. If things fail, simply correct the playbook file and rerun. + +The goal of each task is to execute a module, with very specific arguments. +Variables, as mentioned above, can be used in arguments to modules. + +Modules are 'idempotent', meaning if you run them +again, they will make only the changes they must in order to bring the +system to the desired state. This makes it very safe to rerun +the same playbook multiple times. They won't change things +unless they have to change things. + +The `command` and `shell` modules will typically rerun the same command again, +which is totally ok if the command is something like +'chmod' or 'setsebool', etc. Though there is a 'creates' flag available which can +be used to make these modules also idempotent. + +Every task should have a `name`, which is included in the output from +running the playbook. This is output for humans, so it is +nice to have reasonably good descriptions of each task step. If the name +is not provided though, the string fed to 'action' will be used for +output. + +Tasks can be declared using the legacy "action: module options" format, but +it is recommended that you use the more conventional "module: options" format. +This recommended format is used throughout the documentation, but you may +encounter the older format in some playbooks. + +Here is what a basic task looks like, as with most modules, +the service module takes key=value arguments:: + + tasks: + - name: make sure apache is running + service: name=httpd state=running + +The `command` and `shell` modules are the one modules that just takes a list +of arguments, and don't use the key=value form. This makes +them work just like you would expect. Simple:: + + tasks: + - name: disable selinux + command: /sbin/setenforce 0 + +The command and shell module care about return codes, so if you have a command +whose successful exit code is not zero, you may wish to do this:: + + tasks: + - name: run this command and ignore the result + shell: /usr/bin/somecommand || /bin/true + +Or this:: + + tasks: + - name: run this command and ignore the result + shell: /usr/bin/somecommand + ignore_errors: True + + +If the action line is getting too long for comfort you can break it on +a space and indent any continuation lines:: + + tasks: + - name: Copy ansible inventory file to client + copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts + owner=root group=root mode=0644 + +Variables can be used in action lines. Suppose you defined +a variable called 'vhost' in the 'vars' section, you could do this:: + + tasks: + - name: create a virtual host file for {{ vhost }} + template: src=somefile.j2 dest=/etc/httpd/conf.d/{{ vhost }} + +Those same variables are usable in templates, which we'll get to later. + +Now in a very basic playbook all the tasks will be listed directly in that play, though it will usually +make more sense to break up tasks using the 'include:' directive. We'll show that a bit later. + +.. _action_shorthand: + +Action Shorthand +```````````````` + +.. versionadded:: 0.8 + +Ansible prefers listing modules like this in 0.8 and later:: + + template: src=templates/foo.j2 dest=/etc/foo.conf + +You will notice in earlier versions, this was only available as:: + + action: template src=templates/foo.j2 dest=/etc/foo.conf + +The old form continues to work in newer versions without any plan of deprecation. + +.. _handlers: + +Handlers: Running Operations On Change +`````````````````````````````````````` + +As we've mentioned, modules are written to be 'idempotent' and can relay when +they have made a change on the remote system. Playbooks recognize this and +have a basic event system that can be used to respond to change. + +These 'notify' actions are triggered at the end of each block of tasks in a playbook, and will only be +triggered once even if notified by multiple different tasks. + +For instance, multiple resources may indicate +that apache needs to be restarted because they have changed a config file, +but apache will only be bounced once to avoid unnecessary restarts. + +Here's an example of restarting two services when the contents of a file +change, but only if the file changes:: + + - name: template configuration file + template: src=template.j2 dest=/etc/foo.conf + notify: + - restart memcached + - restart apache + +The things listed in the 'notify' section of a task are called +handlers. + +Handlers are lists of tasks, not really any different from regular +tasks, that are referenced by name. Handlers are what notifiers +notify. If nothing notifies a handler, it will not run. Regardless +of how many things notify a handler, it will run only once, after all +of the tasks complete in a particular play. + +Here's an example handlers section:: + + handlers: + - name: restart memcached + service: name=memcached state=restarted + - name: restart apache + service: name=apache state=restarted + +Handlers are best used to restart services and trigger reboots. You probably +won't need them for much else. + +.. note:: + Notify handlers are always run in the order written. + +Roles are described later on. It's worthwhile to point out that handlers are +automatically processed between 'pre_tasks', 'roles', 'tasks', and 'post_tasks' +sections. If you ever want to flush all the handler commands immediately though, +in 1.2 and later, you can:: + + tasks: + - shell: some tasks go here + - meta: flush_handlers + - shell: some other tasks + +In the above example any queued up handlers would be processed early when the 'meta' +statement was reached. This is a bit of a niche case but can come in handy from +time to time. + +.. _executing_a_playbook: + +Executing A Playbook +```````````````````` + +Now that you've learned playbook syntax, how do you run a playbook? It's simple. +Let's run a playbook using a parallelism level of 10:: + + ansible-playbook playbook.yml -f 10 + +.. _tips_and_tricks: + + +Ansible-Pull +```````````` + +Should you want to invert the architecture of Ansible, so that nodes check in to a central location, instead +of pushing configuration out to them, you can. + +Ansible-pull is a small script that will checkout a repo of configuration instructions from git, and then +run ansible-playbook against that content. + +Assuming you load balance your checkout location, ansible-pull scales essentially infinitely. + +Run 'ansible-pull --help' for details. + +There's also a `clever playbook `_ available to using ansible in push mode to configure ansible-pull via a crontab! + +Tips and Tricks +``````````````` + +Look at the bottom of the playbook execution for a summary of the nodes that were targeted +and how they performed. General failures and fatal "unreachable" communication attempts are +kept separate in the counts. + +If you ever want to see detailed output from successful modules as well as unsuccessful ones, +use the '--verbose' flag. This is available in Ansible 0.5 and later. + +Ansible playbook output is vastly upgraded if the cowsay +package is installed. Try it! + +To see what hosts would be affected by a playbook before you run it, you +can do this:: + + ansible-playbook playbook.yml --list-hosts. + +.. seealso:: + + :doc:`YAMLSyntax` + Learn about YAML syntax + :doc:`playbooks_best_practices` + Various tips about managing playbooks in the real world + :doc:`index` + Hop back to the documentation index for a lot of special topics about playbooks + :doc:`modules` + Learn about available modules + :doc:`developing_modules` + Learn how to extend Ansible by writing your own modules + :doc:`intro_patterns` + Learn about how to select hosts + `Github examples directory `_ + Complete end-to-end playbook examples + `Mailing List `_ + Questions? Help? Ideas? Stop by the list on Google Groups + + + From b85743e228bb5875682033c3cd0e896e4b5c190d Mon Sep 17 00:00:00 2001 From: Stoned Elipot Date: Wed, 25 Dec 2013 12:12:11 +0100 Subject: [PATCH 033/182] shell module: document removes option and sync with command module doc --- library/commands/shell | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/library/commands/shell b/library/commands/shell index 5b3969a1fb..03299b967c 100644 --- a/library/commands/shell +++ b/library/commands/shell @@ -7,20 +7,26 @@ DOCUMENTATION = ''' module: shell short_description: Execute commands in nodes. description: - - The shell module takes the command name followed by a list of arguments, - space delimited. It is almost exactly like the M(command) module but runs + - The M(shell) module takes the command name followed by a list of space-delimited arguments. + It is almost exactly like the M(command) module but runs the command through a shell (C(/bin/sh)) on the remote node. version_added: "0.2" options: - (free form): + free_form: description: - - The command module takes a free form command to run - required: null + - The shell module takes a free form command to run + required: true default: null creates: description: - - a filename, when it already exists, this step will NOT be run - required: false + - a filename, when it already exists, this step will B(not) be run. + required: no + default: null + removes: + description: + - a filename, when it does not exist, this step will B(not) be run. + version_added: "0.8" + required: no default: null chdir: description: From dc6a26123be77359cff8cb972ccf2d9c50bd9c7c Mon Sep 17 00:00:00 2001 From: Levi Gross Date: Wed, 25 Dec 2013 11:04:47 -0500 Subject: [PATCH 034/182] Fixed syntax error and removed unused imports --- lib/ansible/runner/lookup_plugins/etcd.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/ansible/runner/lookup_plugins/etcd.py b/lib/ansible/runner/lookup_plugins/etcd.py index 04508cdfff..07adec8029 100644 --- a/lib/ansible/runner/lookup_plugins/etcd.py +++ b/lib/ansible/runner/lookup_plugins/etcd.py @@ -15,8 +15,7 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . -from ansible import utils, errors, constants -import os +from ansible import utils import urllib2 try: import json @@ -27,7 +26,7 @@ except ImportError: ANSIBLE_ETCD_URL = 'http://127.0.0.1:4001' class etcd(): - def __init__(self, url=ANSIBLE_ETCD_URL) + def __init__(self, url=ANSIBLE_ETCD_URL): self.url = url self.baseurl = '%s/v1/keys' % (self.url) From 10009b0d3fb4321df105b7a10b0a46e465e230fe Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 11:44:01 -0500 Subject: [PATCH 035/182] Starting to refactor module formatter script. Rename "jpfunc" to something more explanatory --- docsite/Makefile | 2 +- docsite/rst/modules.rst | 3 +-- hacking/module_formatter.py | 17 +++++++++-------- hacking/templates/html.j2 | 2 +- hacking/templates/latex.j2 | 6 +++--- hacking/templates/man.j2 | 8 ++++---- hacking/templates/markdown.j2 | 8 ++++---- hacking/templates/rst.j2 | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docsite/Makefile b/docsite/Makefile index 56b84fbc88..39b3cc647e 100644 --- a/docsite/Makefile +++ b/docsite/Makefile @@ -27,6 +27,6 @@ clean: .PHONEY: docs clean modules: $(FORMATTER) ../hacking/templates/rst.j2 - PYTHONPATH=../lib $(FORMATTER) -t rst --template-dir=../hacking/templates --module-dir=../library -o rst/modules/ --includes-file=rst/modules/_list.rst + PYTHONPATH=../lib $(FORMATTER) -t rst --template-dir=../hacking/templates --module-dir=../library -o rst/modules/ --includes-file=rst/modules/_categories.rst diff --git a/docsite/rst/modules.rst b/docsite/rst/modules.rst index ac922c965c..3348d2b804 100644 --- a/docsite/rst/modules.rst +++ b/docsite/rst/modules.rst @@ -54,8 +54,7 @@ ansible-doc as well as the man command:: Let's see what's available in the Ansible module library, out of the box: - -.. include:: modules/_list.rst +.. include:: modules/_categories.rst .. _ansible_doc: diff --git a/hacking/module_formatter.py b/hacking/module_formatter.py index 35fde1b1f6..7066589236 100755 --- a/hacking/module_formatter.py +++ b/hacking/module_formatter.py @@ -264,25 +264,25 @@ def main(): env.globals['xline'] = rst_xline if options.type == 'latex': - env.filters['jpfunc'] = latex_ify + env.filters['convert_symbols_to_format'] = latex_ify template = env.get_template('latex.j2') outputname = "%s.tex" includecmt = "" includefmt = "%s\n" if options.type == 'html': - env.filters['jpfunc'] = html_ify + env.filters['convert_symbols_to_format'] = html_ify template = env.get_template('html.j2') outputname = "%s.html" includecmt = "" includefmt = "" if options.type == 'man': - env.filters['jpfunc'] = man_ify + env.filters['convert_symbols_to_format'] = man_ify template = env.get_template('man.j2') outputname = "ansible.%s.3" includecmt = "" includefmt = "" if options.type == 'rst': - env.filters['jpfunc'] = rst_ify + env.filters['convert_symbols_to_format'] = rst_ify env.filters['html_ify'] = html_ify env.filters['fmt'] = rst_fmt env.filters['xline'] = rst_xline @@ -291,16 +291,16 @@ def main(): includecmt = ".. Generated by module_formatter\n" includefmt = ".. include:: modules/%s.rst\n" if options.type == 'json': - env.filters['jpfunc'] = json_ify + env.filters['convert_symbols_to_format'] = json_ify outputname = "%s.json" includecmt = "" includefmt = "" if options.type == 'js': - env.filters['jpfunc'] = js_ify + env.filters['convert_symbols_to_format'] = js_ify template = env.get_template('js.j2') outputname = "%s.js" if options.type == 'markdown': - env.filters['jpfunc'] = markdown_ify + env.filters['convert_symbols_to_format'] = markdown_ify env.filters['html_ify'] = html_ify template = env.get_template('markdown.j2') outputname = "%s.md" @@ -320,7 +320,8 @@ def main(): category_names.sort() for category in category_names: - module_map = categories[category] + + module_map = categories[category] category = category.replace("_"," ") category = category.title() diff --git a/hacking/templates/html.j2 b/hacking/templates/html.j2 index b86e47378b..f80018bb6d 100644 --- a/hacking/templates/html.j2 +++ b/hacking/templates/html.j2 @@ -3,5 +3,5 @@

    @{module}@

    {% for desc in description -%} - @{ desc | jpfunc }@ + @{ desc | convert_symbols_to_format }@ {% endfor %} diff --git a/hacking/templates/latex.j2 b/hacking/templates/latex.j2 index d02ffa8677..5905477d60 100644 --- a/hacking/templates/latex.j2 +++ b/hacking/templates/latex.j2 @@ -8,7 +8,7 @@ %: -- module header \mods{@{module}@}{@{docuri}@}{ {% for desc in description -%} - @{ desc | jpfunc }@ + @{ desc | convert_symbols_to_format }@ {% endfor -%} {% if version_added is defined -%} (\I{* new in version @{ version_added }@}) @@ -30,7 +30,7 @@ {# -------- option description ----------#} {% for desc in v.description %} - @{ desc | jpfunc }@ + @{ desc | convert_symbols_to_format }@ {% endfor %} {% if v['choices'] %} \B{Choices}:\, @@ -51,7 +51,7 @@ {% if notes %} {% for note in notes %} - \I{@{ note | jpfunc }@} + \I{@{ note | convert_symbols_to_format }@} {% endfor %} {% endif %} ----------------------------- #} diff --git a/hacking/templates/man.j2 b/hacking/templates/man.j2 index 6cf51960ae..c85b14a2b1 100644 --- a/hacking/templates/man.j2 +++ b/hacking/templates/man.j2 @@ -6,7 +6,7 @@ .SH DESCRIPTION {% for desc in description %} .PP -@{ desc | jpfunc }@ +@{ desc | convert_symbols_to_format }@ {% endfor %} .\" ------ OPTIONS .\" @@ -17,7 +17,7 @@ {% set v = options[k] %} .IP @{ k }@ -{% for desc in v.description %}@{ desc | jpfunc }@{% endfor %} +{% for desc in v.description %}@{ desc | convert_symbols_to_format }@{% endfor %} {% if v.get('choices') %} .IR Choices : @@ -37,7 +37,7 @@ .SH NOTES {% for note in notes %} .PP -@{ note | jpfunc }@ +@{ note | convert_symbols_to_format }@ {% endfor %} {% endif %} .\" @@ -48,7 +48,7 @@ {% for e in examples %} .PP {% if e['description'] %} -@{ e['description'] | jpfunc }@ +@{ e['description'] | convert_symbols_to_format }@ {% endif %} .nf diff --git a/hacking/templates/markdown.j2 b/hacking/templates/markdown.j2 index 6f9bb0b3d8..a734ece3ec 100644 --- a/hacking/templates/markdown.j2 +++ b/hacking/templates/markdown.j2 @@ -1,4 +1,4 @@ -## @{ module | jpfunc }@ +## @{ module | convert_symbols_to_format }@ {# ------------------------------------------ # @@ -11,7 +11,7 @@ New in version @{ version_added }@. {% endif %} {% for desc in description -%} -@{ desc | jpfunc }@ +@{ desc | convert_symbols_to_format }@ {% endfor %} @@ -42,7 +42,7 @@ New in version @{ version_added }@. {% for example in examples %} {% if example['description'] %} -* @{ example['description'] | jpfunc }@ +* @{ example['description'] | convert_symbols_to_format }@ {% endif %} ``` @@ -58,7 +58,7 @@ New in version @{ version_added }@. {% if notes %} #### Notes {% for note in notes %} -@{ note | jpfunc }@ +@{ note | convert_symbols_to_format }@ {% endfor %} {% endif %} diff --git a/hacking/templates/rst.j2 b/hacking/templates/rst.j2 index b4afcd1cab..556ea1e36c 100644 --- a/hacking/templates/rst.j2 +++ b/hacking/templates/rst.j2 @@ -15,7 +15,7 @@ {% endif %} {% for desc in description -%} -@{ desc | jpfunc }@ +@{ desc | convert_symbols_to_format }@ {% endfor %} {% if options -%} From 31d0060de825d80424099ae05b128d6abfc22f05 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 11:45:27 -0500 Subject: [PATCH 036/182] Standardized on yml suffix, so fix this example content filename. --- examples/{DOCUMENTATION.yaml => DOCUMENTATION.yml} | 0 hacking/module_formatter.py | 8 +++++--- 2 files changed, 5 insertions(+), 3 deletions(-) rename examples/{DOCUMENTATION.yaml => DOCUMENTATION.yml} (100%) diff --git a/examples/DOCUMENTATION.yaml b/examples/DOCUMENTATION.yml similarity index 100% rename from examples/DOCUMENTATION.yaml rename to examples/DOCUMENTATION.yml diff --git a/hacking/module_formatter.py b/hacking/module_formatter.py index 7066589236..24dc3bdce1 100755 --- a/hacking/module_formatter.py +++ b/hacking/module_formatter.py @@ -41,10 +41,12 @@ TO_OLD_TO_BE_NOTABLE = 1.0 # Get parent directory of the directory this script lives in MODULEDIR=os.path.abspath(os.path.join( os.path.dirname(os.path.realpath(__file__)), os.pardir, 'library' - )) +)) + +# The name of the DOCUMENTATION template EXAMPLE_YAML=os.path.abspath(os.path.join( - os.path.dirname(os.path.realpath(__file__)), os.pardir, 'examples', 'DOCUMENTATION.yaml' - )) + os.path.dirname(os.path.realpath(__file__)), os.pardir, 'examples', 'DOCUMENTATION.yml' +)) # There is a better way of doing this! # TODO: somebody add U(text, http://foo.bar/) as described by Tim in #991 From fe2d00d9d338e43071f7382fa7d414cb30706839 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 12:35:41 -0500 Subject: [PATCH 037/182] WIP on refactoring the module formatter code that we use to build the doc site with. --- Makefile | 10 +- hacking/module_formatter.py | 466 ++++++++++++---------------------- hacking/templates/html.j2 | 7 - hacking/templates/js.j2 | 5 - hacking/templates/latex.j2 | 76 ------ hacking/templates/markdown.j2 | 64 ----- 6 files changed, 170 insertions(+), 458 deletions(-) delete mode 100644 hacking/templates/html.j2 delete mode 100644 hacking/templates/js.j2 delete mode 100644 hacking/templates/latex.j2 delete mode 100644 hacking/templates/markdown.j2 diff --git a/Makefile b/Makefile index c617b1eec9..990d9cdc0b 100644 --- a/Makefile +++ b/Makefile @@ -64,9 +64,6 @@ all: clean python tests: PYTHONPATH=./lib ANSIBLE_LIBRARY=./library $(NOSETESTS) -d -v -# To force a rebuild of the docs run 'touch VERSION && make docs' -docs: $(MANPAGES) modulepages - authors: sh hacking/authors.sh @@ -172,11 +169,6 @@ deb: debian # for arch or gentoo, read instructions in the appropriate 'packaging' subdirectory directory -modulepages: - PYTHONPATH=./lib $(PYTHON) hacking/module_formatter.py -A $(VERSION) -t man -o docs/man/man3/ --module-dir=library --template-dir=hacking/templates # --verbose - -# because this requires Sphinx it is not run as part of every build, those building the RPM and so on can ignore this - -webdocs: +webdocs: $(MANPAGES) (cd docsite/; make docs) diff --git a/hacking/module_formatter.py b/hacking/module_formatter.py index 24dc3bdce1..f6ab8d596a 100755 --- a/hacking/module_formatter.py +++ b/hacking/module_formatter.py @@ -24,16 +24,20 @@ import yaml import codecs import json import ast -from jinja2 import Environment, FileSystemLoader import re import optparse import time import datetime import subprocess import cgi +from jinja2 import Environment, FileSystemLoader + import ansible.utils import ansible.utils.module_docs as module_docs +##################################################################################### +# constants and paths + # if a module is added in a version of Ansible older than this, don't print the version added information # in the module documentation because everyone is assumed to be running something newer than this already. TO_OLD_TO_BE_NOTABLE = 1.0 @@ -48,65 +52,16 @@ EXAMPLE_YAML=os.path.abspath(os.path.join( os.path.dirname(os.path.realpath(__file__)), os.pardir, 'examples', 'DOCUMENTATION.yml' )) -# There is a better way of doing this! -# TODO: somebody add U(text, http://foo.bar/) as described by Tim in #991 - _ITALIC = re.compile(r"I\(([^)]+)\)") _BOLD = re.compile(r"B\(([^)]+)\)") _MODULE = re.compile(r"M\(([^)]+)\)") _URL = re.compile(r"U\(([^)]+)\)") _CONST = re.compile(r"C\(([^)]+)\)") -def latex_ify(text): - - t = _ITALIC.sub("\\I{" + r"\1" + "}", text) - t = _BOLD.sub("\\B{" + r"\1" + "}", t) - t = _MODULE.sub("\\M{" + r"\1" + "}", t) - t = _URL.sub("\\url{" + r"\1" + "}", t) - t = _CONST.sub("\\C{" + r"\1" + "}", t) - - return t - -def html_ify(text): - - #print "DEBUG: text=%s" % text - - t = cgi.escape(text) - t = _ITALIC.sub("" + r"\1" + "", t) - t = _BOLD.sub("" + r"\1" + "", t) - t = _MODULE.sub("" + r"\1" + "", t) - t = _URL.sub("" + r"\1" + "", t) - t = _CONST.sub("" + r"\1" + "", t) - - return t - -def json_ify(text): - - t = _ITALIC.sub("" + r"\1" + "", text) - t = _BOLD.sub("" + r"\1" + "", t) - t = _MODULE.sub("" + r"\1" + "", t) - t = _URL.sub("" + r"\1" + "", t) - t = _CONST.sub("" + r"\1" + "", t) - - return t - - -def js_ify(text): - - return text - - -def man_ify(text): - - t = _ITALIC.sub(r'\\fI' + r"\1" + r"\\fR", text) - t = _BOLD.sub(r'\\fB' + r"\1" + r"\\fR", t) - t = _MODULE.sub(r'\\fI' + r"\1" + r"\\fR", t) - t = _URL.sub(r'\\fI' + r"\1" + r"\\fR", t) - t = _CONST.sub(r'\\fC' + r"\1" + r"\\fR", t) - - return t +##################################################################################### def rst_ify(text): + ''' convert symbols like I(this is in italics) to valid restructured text ''' t = _ITALIC.sub(r'*' + r"\1" + r"*", text) t = _BOLD.sub(r'**' + r"\1" + r"**", t) @@ -116,31 +71,40 @@ def rst_ify(text): return t -_MARKDOWN = re.compile(r"[*_`]") +##################################################################################### -def markdown_ify(text): +def html_ify(text): + ''' convert symbols like I(this is in italics) to valid HTML ''' t = cgi.escape(text) - t = _MARKDOWN.sub(r"\\\g<0>", t) - t = _ITALIC.sub("_" + r"\1" + "_", t) - t = _BOLD.sub("**" + r"\1" + "**", t) - t = _MODULE.sub("*" + r"\1" + "*", t) - t = _URL.sub("[" + r"\1" + "](" + r"\1" + ")", t) - t = _CONST.sub("`" + r"\1" + "`", t) + t = _ITALIC.sub("" + r"\1" + "", t) + t = _BOLD.sub("" + r"\1" + "", t) + t = _MODULE.sub("" + r"\1" + "", t) + t = _URL.sub("" + r"\1" + "", t) + t = _CONST.sub("" + r"\1" + "", t) return t -# Helper for Jinja2 (format() doesn't work here...) + +##################################################################################### + def rst_fmt(text, fmt): + ''' helper for Jinja2 to do format strings ''' + return fmt % (text) +##################################################################################### + def rst_xline(width, char="="): + ''' return a restructured text line of a given length ''' + return char * width -def load_examples_section(text): - return text.split('***BREAK***') +##################################################################################### def return_data(text, options, outputname, module): + ''' dumps module output to a file or the screen, as requested ''' + if options.output_dir is not None: f = open(os.path.join(options.output_dir, outputname % module), 'w') f.write(text.encode('utf-8')) @@ -148,15 +112,29 @@ def return_data(text, options, outputname, module): else: print text +##################################################################################### + def boilerplate(): + ''' prints the boilerplate for module docs ''' + if not os.path.exists(EXAMPLE_YAML): print >>sys.stderr, "Missing example boiler plate: %s" % EXAMPLE_YAML print "DOCUMENTATION = '''" print file(EXAMPLE_YAML).read() print "'''" print "" + print "" + print "EXAMPLES = '''" + print "# example of doing ___ from a playbook" + print "your_module: some_arg=1 other_arg=2" + print "'''" + print "" + +##################################################################################### def list_modules(module_dir): + ''' returns a hash of categories, each category being a hash of module names to file paths ''' + categories = {} files = glob.glob("%s/*" % module_dir) for d in files: @@ -171,150 +149,157 @@ def list_modules(module_dir): categories[category][module] = f return categories -def main(): +##################################################################################### + +def generate_parser(): + ''' generate an optparse parser ''' p = optparse.OptionParser( version='%prog 1.0', usage='usage: %prog [options] arg1 arg2', - description='Convert Ansible module DOCUMENTATION strings to other formats', + description='Generate module documentation from metadata', ) - p.add_option("-A", "--ansible-version", - action="store", - dest="ansible_version", - default="unknown", - help="Ansible version number") - p.add_option("-M", "--module-dir", - action="store", - dest="module_dir", - default=MODULEDIR, - help="Ansible modules/ directory") - p.add_option("-T", "--template-dir", - action="store", - dest="template_dir", - default="hacking/templates", - help="directory containing Jinja2 templates") - p.add_option("-t", "--type", - action='store', - dest='type', - choices=['html', 'latex', 'man', 'rst', 'json', 'markdown', 'js'], - default='latex', - help="Output type") - p.add_option("-m", "--module", - action='append', - default=[], - dest='module_list', - help="Add modules to process in module_dir") - p.add_option("-v", "--verbose", - action='store_true', - default=False, - help="Verbose") - p.add_option("-o", "--output-dir", - action="store", - dest="output_dir", - default=None, - help="Output directory for module files") - p.add_option("-I", "--includes-file", - action="store", - dest="includes_file", - default=None, - help="Create a file containing list of processed modules") - p.add_option("-G", "--generate", - action="store_true", - dest="do_boilerplate", - default=False, - help="generate boilerplate DOCUMENTATION to stdout") + p.add_option("-A", "--ansible-version", action="store", dest="ansible_version", default="unknown", help="Ansible version number") + p.add_option("-M", "--module-dir", action="store", dest="module_dir", default=MODULEDIR, help="Ansible library path") + p.add_option("-T", "--template-dir", action="store", dest="template_dir", default="hacking/templates", help="directory containing Jinja2 templates") + p.add_option("-t", "--type", action='store', dest='type', choices=['html', 'latex', 'man', 'rst', 'json', 'markdown', 'js'], default='latex', help="Document type") + p.add_option("-v", "--verbose", action='store_true', default=False, help="Verbose") + p.add_option("-o", "--output-dir", action="store", dest="output_dir", default=None, help="Output directory for module files") + p.add_option("-I", "--includes-file", action="store", dest="includes_file", default=None, help="Create a file containing list of processed modules") + p.add_option("-G", "--generate", action="store_true", dest="do_boilerplate", default=False, help="generate boilerplate docs to stdout") p.add_option('-V', action='version', help='Show version number and exit') + return p - (options, args) = p.parse_args() +##################################################################################### -# print "M: %s" % options.module_dir -# print "t: %s" % options.type -# print "m: %s" % options.module_list -# print "v: %s" % options.verbose +def jinja2_environment(template_dir, typ): - if options.do_boilerplate: - boilerplate() - - print "" - print "EXAMPLES = '''" - print "# example of doing ___ from a playbook" - print "your_module: some_arg=1 other_arg=2" - print "'''" - print "" - - sys.exit(0) - - if not options.module_dir: - print "Need module_dir" - sys.exit(1) - if not os.path.exists(options.module_dir): - print >>sys.stderr, "Module directory does not exist: %s" % options.module_dir - sys.exit(1) - - - if not options.template_dir: - print "Need template_dir" - sys.exit(1) - - env = Environment(loader=FileSystemLoader(options.template_dir), + env = Environment(loader=FileSystemLoader(template_dir), variable_start_string="@{", variable_end_string="}@", trim_blocks=True, ) - env.globals['xline'] = rst_xline - if options.type == 'latex': - env.filters['convert_symbols_to_format'] = latex_ify - template = env.get_template('latex.j2') - outputname = "%s.tex" - includecmt = "" - includefmt = "%s\n" - if options.type == 'html': - env.filters['convert_symbols_to_format'] = html_ify - template = env.get_template('html.j2') - outputname = "%s.html" - includecmt = "" - includefmt = "" - if options.type == 'man': - env.filters['convert_symbols_to_format'] = man_ify - template = env.get_template('man.j2') - outputname = "ansible.%s.3" - includecmt = "" - includefmt = "" - if options.type == 'rst': + if typ == 'rst': env.filters['convert_symbols_to_format'] = rst_ify env.filters['html_ify'] = html_ify env.filters['fmt'] = rst_fmt env.filters['xline'] = rst_xline template = env.get_template('rst.j2') outputname = "%s.rst" - includecmt = ".. Generated by module_formatter\n" - includefmt = ".. include:: modules/%s.rst\n" - if options.type == 'json': - env.filters['convert_symbols_to_format'] = json_ify - outputname = "%s.json" - includecmt = "" - includefmt = "" - if options.type == 'js': - env.filters['convert_symbols_to_format'] = js_ify - template = env.get_template('js.j2') - outputname = "%s.js" - if options.type == 'markdown': - env.filters['convert_symbols_to_format'] = markdown_ify - env.filters['html_ify'] = html_ify - template = env.get_template('markdown.j2') - outputname = "%s.md" - includecmt = "" - includefmt = "" + else: + raise Exception("unknown module format type: %s" % typ) - if options.includes_file is not None and includefmt != "": - incfile = open(options.includes_file, "w") - incfile.write(includecmt) + return env, template, outputname - # Temporary variable required to genrate aggregated content in 'js' format. - js_data = [] +##################################################################################### + +def process_module(module, options, env, template, outputname, module_map): + + print "rendering: %s" % module + + fname = module_map[module] + + # ignore files with extensions + if os.path.basename(fname).find(".") != -1: + return + + # use ansible core library to parse out doc metadata YAML and plaintext examples + doc, examples = ansible.utils.module_docs.get_docstring(fname, verbose=options.verbose) + + # crash if module is missing documentation and not explicitly hidden from docs index + if doc is None and module not in ansible.utils.module_docs.BLACKLIST_MODULES: + sys.stderr.write("*** ERROR: CORE MODULE MISSING DOCUMENTATION: %s, %s ***\n" % (fname, module)) + sys.exit(1) + if doc is None: + return + + all_keys = [] + + if not 'version_added' in doc: + sys.stderr.write("*** ERROR: missing version_added in: %s ***\n" % module) + sys.exit(1) + + added = 0 + if doc['version_added'] == 'historical': + del doc['version_added'] + else: + added = doc['version_added'] + + # don't show version added information if it's too old to be called out + if added: + added_tokens = str(added).split(".") + added = added_tokens[0] + "." + added_tokens[1] + added_float = float(added) + if added and added_float < TO_OLD_TO_BE_NOTABLE: + del doc['version_added'] + + for (k,v) in doc['options'].iteritems(): + all_keys.append(k) + all_keys = sorted(all_keys) + doc['option_keys'] = all_keys + + doc['filename'] = fname + doc['docuri'] = doc['module'].replace('_', '-') + doc['now_date'] = datetime.date.today().strftime('%Y-%m-%d') + doc['ansible_version'] = options.ansible_version + doc['plainexamples'] = examples #plain text + + # here is where we build the table of contents... + + text = template.render(doc) + return_data(text, options, outputname, module) + +##################################################################################### + +def process_category(category, categories, options, env, template, outputname): + + module_map = categories[category] + + # TODO: start a new category file + + category = category.replace("_"," ") + category = category.title() + + modules = module_map.keys() + modules.sort() + + for module in modules: + process_module(module, options, env, template, outputname, module_map) + + # TODO: end a new category file + +##################################################################################### + +def validate_options(options): + ''' validate option parser options ''' + + if options.do_boilerplate: + boilerplate() + sys.exit(0) + + if not options.module_dir: + print >>sys.stderr, "--module-dir is required" + sys.exit(1) + if not os.path.exists(options.module_dir): + print >>sys.stderr, "--module-dir does not exist: %s" % options.module_dir + sys.exit(1) + if not options.template_dir: + print "--template-dir must be specified" + sys.exit(1) + +##################################################################################### + +def main(): + + p = generate_parser() + + (options, args) = p.parse_args() + validate_options(options) + + env, template, outputname = jinja2_environment(options.template_dir, options.type) categories = list_modules(options.module_dir) last_category = None @@ -322,120 +307,7 @@ def main(): category_names.sort() for category in category_names: - - module_map = categories[category] - - category = category.replace("_"," ") - category = category.title() - - modules = module_map.keys() - modules.sort() - - for module in modules: - - print "rendering: %s" % module - - fname = module_map[module] - - if len(options.module_list): - if not module in options.module_list: - continue - - # fname = os.path.join(options.module_dir, module) - - extra = os.path.join("inc", "%s.tex" % module) - - # probably could just throw out everything with extensions - if fname.endswith(".swp") or fname.endswith(".orig") or fname.endswith(".rej"): - continue - - # print " processing module source ---> %s" % fname - - if options.type == 'js': - if fname.endswith(".json"): - f = open(fname) - j = json.load(f) - f.close() - js_data.append(j) - continue - - doc, examples = ansible.utils.module_docs.get_docstring(fname, verbose=options.verbose) - - if doc is None and module not in ansible.utils.module_docs.BLACKLIST_MODULES: - print " while processing module source ---> %s" % fname - sys.stderr.write("*** ERROR: CORE MODULE MISSING DOCUMENTATION: %s ***\n" % module) - #sys.exit(1) - - if not doc is None: - - all_keys = [] - - if not 'version_added' in doc: - sys.stderr.write("*** ERROR: missing version_added in: %s ***\n" % module) - sys.exit(1) - - added = 0 - if doc['version_added'] == 'historical': - del doc['version_added'] - else: - added = doc['version_added'] - - # don't show version added information if it's too old to be called out - if added: - added_tokens = str(added).split(".") - added = added_tokens[0] + "." + added_tokens[1] - added_float = float(added) - if added and added_float < TO_OLD_TO_BE_NOTABLE: - del doc['version_added'] - - for (k,v) in doc['options'].iteritems(): - all_keys.append(k) - all_keys = sorted(all_keys) - doc['option_keys'] = all_keys - - doc['filename'] = fname - doc['docuri'] = doc['module'].replace('_', '-') - doc['now_date'] = datetime.date.today().strftime('%Y-%m-%d') - doc['ansible_version'] = options.ansible_version - doc['plainexamples'] = examples #plain text - - # BOOKMARK: here is where we build the table of contents... - - if options.includes_file is not None and includefmt != "": - - if last_category != category: - incfile.write("\n\n") - incfile.write(category) - incfile.write("\n") - incfile.write('`' * len(category)) - incfile.write("\n\n") - last_category = category - - incfile.write(includefmt % module) - - if options.verbose: - print json.dumps(doc, indent=4) - - - if options.type == 'latex': - if os.path.exists(extra): - f = open(extra) - extradata = f.read() - f.close() - doc['extradata'] = extradata - - if options.type == 'json': - text = json.dumps(doc, indent=2) - else: - text = template.render(doc) - - return_data(text, options, outputname, module) - - if options.type == 'js': - docs = {} - docs['json'] = json.dumps(js_data, indent=2) - text = template.render(docs) - return_data(text, options, outputname, 'modules') + process_category(category, categories, options, env, template, outputname) if __name__ == '__main__': main() diff --git a/hacking/templates/html.j2 b/hacking/templates/html.j2 deleted file mode 100644 index f80018bb6d..0000000000 --- a/hacking/templates/html.j2 +++ /dev/null @@ -1,7 +0,0 @@ - - -

    @{module}@

    - - {% for desc in description -%} - @{ desc | convert_symbols_to_format }@ - {% endfor %} diff --git a/hacking/templates/js.j2 b/hacking/templates/js.j2 deleted file mode 100644 index fa2d4f7f53..0000000000 --- a/hacking/templates/js.j2 +++ /dev/null @@ -1,5 +0,0 @@ -function AnsibleModules($scope) { - $scope.modules = @{ json }@; - - $scope.orderProp = "module"; -} diff --git a/hacking/templates/latex.j2 b/hacking/templates/latex.j2 deleted file mode 100644 index 5905477d60..0000000000 --- a/hacking/templates/latex.j2 +++ /dev/null @@ -1,76 +0,0 @@ -{# ------------------------------------------------------------------- - template for module_formatter.py for LaTeX output (Ansible Booklet) - by Jan-Piet Mens. - Note: nodes & code examples are omitted on purpose. - -------------------------------------------------------------------- #} -%--- @{ module | upper }@ ---- from @{ filename }@ --- - -%: -- module header -\mods{@{module}@}{@{docuri}@}{ - {% for desc in description -%} - @{ desc | convert_symbols_to_format }@ - {% endfor -%} - {% if version_added is defined -%} - (\I{* new in version @{ version_added }@}) - {% endif -%} - } - -%: -- module options - - - -{% if options %} -\begin{xlist}{abcdefghijklmno} - {% for (opt,v) in options.iteritems() %} - {% if v['required'] %} - \item[\man\,\C{@{ opt }@}] - {% else %} - \item[\opt\,\C{@{ opt }@}] - {% endif %} - - {# -------- option description ----------#} - {% for desc in v.description %} - @{ desc | convert_symbols_to_format }@ - {% endfor %} - {% if v['choices'] %} - \B{Choices}:\, - {% for choice in v['choices'] %}\C{@{ choice }@}{% if not loop.last %},{% else %}.{% endif %} - {% endfor %} - {% endif %} - {% if v['default'] %} - (default \C{@{ v['default'] }@}) - {% endif %} - {% if v['version_added'] is defined %} - (\I{* version @{ v['version_added'] }@}) - {% endif %} - {% endfor %} -\end{xlist} -{% endif %} - -{# --------------------------------------- -{% if notes %} - - {% for note in notes %} - \I{@{ note | convert_symbols_to_format }@} - {% endfor %} -{% endif %} - ----------------------------- #} - -{#------------------------------------------- - -{% if examples is defined -%} - {% for e in examples %} - \begin{extymeta} -@{ e['code'] }@ - \end{extymeta} - {% endfor %} -{% endif %} ------------------------------------ #} - -{% if extradata is defined %} -%--- BEGIN-EXTRADATA -\begin{extymeta} -@{ extradata }@ -\end{extymeta} -%----- END-EXTRADATA -{% endif %} diff --git a/hacking/templates/markdown.j2 b/hacking/templates/markdown.j2 deleted file mode 100644 index a734ece3ec..0000000000 --- a/hacking/templates/markdown.j2 +++ /dev/null @@ -1,64 +0,0 @@ -## @{ module | convert_symbols_to_format }@ - -{# ------------------------------------------ - # - # This is Github-flavored Markdown - # - --------------------------------------------#} - -{% if version_added is defined -%} -New in version @{ version_added }@. -{% endif %} - -{% for desc in description -%} -@{ desc | convert_symbols_to_format }@ - -{% endfor %} - -{% if options -%} - - - - - - - - -{% for (k,v) in options.iteritems() %} - - - - - - - -{% endfor %} -
    parameterrequireddefaultchoicescomments
    @{ k }@{% if v.get('required', False) %}yes{% else %}no{% endif %}{% if v['default'] %}@{ v['default'] }@{% endif %}
      {% for choice in v.get('choices',[]) -%}
    • @{ choice }@
    • {% endfor -%}
    {% for desc in v.description -%}@{ desc | html_ify }@{% endfor -%}{% if v['version_added'] %} (added in Ansible @{v['version_added']}@){% endif %}
    -{% endif %} - -{% if examples or plainexamples %} -#### Examples -{% endif %} - -{% for example in examples %} -{% if example['description'] %} -* @{ example['description'] | convert_symbols_to_format }@ -{% endif %} - -``` -@{ example['code'] }@ -``` -{% endfor %} -{% if plainexamples -%} -``` -@{ plainexamples }@ -``` -{% endif %} - -{% if notes %} -#### Notes -{% for note in notes %} -@{ note | convert_symbols_to_format }@ -{% endfor %} -{% endif %} - From 35ec9f81ae567dadc0947d962b95a0f61904a938 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 13:23:58 -0500 Subject: [PATCH 038/182] Further modifications to the module formatter to adjust to the new theme, and some misc docs text corrections. --- .gitignore | 4 +- docsite/Makefile | 2 +- docsite/rst/developing_modules.rst | 18 ++++----- docsite/rst/index.rst | 3 +- docsite/rst/modules.rst | 61 ++++++------------------------ hacking/module_formatter.py | 38 ++++++++++++++++--- 6 files changed, 58 insertions(+), 68 deletions(-) diff --git a/.gitignore b/.gitignore index 3d33ca7a3a..52039a3033 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,9 @@ docs/man/man3/* *.sublime-project *.sublime-workspace # docsite stuff... -docsite/rst/modules +docsite/rst/modules_by_category.rst +docsite/rst/list_of_*.rst +docsite/rst/*_module.rst docsite/*.html docsite/_static/*.gif docsite/_static/*.png diff --git a/docsite/Makefile b/docsite/Makefile index 39b3cc647e..f31fd3d58f 100644 --- a/docsite/Makefile +++ b/docsite/Makefile @@ -27,6 +27,6 @@ clean: .PHONEY: docs clean modules: $(FORMATTER) ../hacking/templates/rst.j2 - PYTHONPATH=../lib $(FORMATTER) -t rst --template-dir=../hacking/templates --module-dir=../library -o rst/modules/ --includes-file=rst/modules/_categories.rst + PYTHONPATH=../lib $(FORMATTER) -t rst --template-dir=../hacking/templates --module-dir=../library -o rst/ diff --git a/docsite/rst/developing_modules.rst b/docsite/rst/developing_modules.rst index 1d5e9e4f81..342dcba23b 100644 --- a/docsite/rst/developing_modules.rst +++ b/docsite/rst/developing_modules.rst @@ -226,8 +226,7 @@ The 'group' and 'user' modules are reasonably non-trivial and showcase what this Key parts include always ending the module file with:: - # include magic from lib/ansible/module_common.py - #<> + from ansible.module_utils.basic import * main() And instantiating the module class like:: @@ -372,10 +371,7 @@ syntax highlighting before you include it in your Python file. Example +++++++ -To print a basic documentation string, run ``./hacking/module_formatter.py -G``. - -You can copy it into your module and use it as a starting point -when writing your own docs. +See an example documentation string in the checkout under examples/DOCUMENTATION.yml Include it in your module file like this:: @@ -389,8 +385,9 @@ Include it in your module file like this:: # ... snip ... ''' -The ``description``, and ``notes`` -support formatting in some of the output formats (e.g. ``rst``, ``man``). +The ``description``, and ``notes`` fields +support formatting with some special macros. + These formatting functions are ``U()``, ``M()``, ``I()``, and ``C()`` for URL, module, italic, and constant-width respectively. It is suggested to use ``C()`` for file and option names, and ``I()`` when referencing @@ -405,9 +402,8 @@ like this:: - action: modulename opt1=arg1 opt2=arg2 ''' -The ``module_formatter.py`` script and ``ansible-doc(1)`` append the -``EXAMPLES`` blob after any existing (deprecated) ``examples`` you may have in the -YAML ``DOCUMENTATION`` string. +The EXAMPLES section, just like the documentation section, is required in +all module pull requests for new modules. .. _module_dev_testing: diff --git a/docsite/rst/index.rst b/docsite/rst/index.rst index 68e96d59bb..864cd33796 100644 --- a/docsite/rst/index.rst +++ b/docsite/rst/index.rst @@ -23,12 +23,13 @@ This documentation covers the current released version of Ansible (1.4.3) and al .. _an_introduction: .. toctree:: - :maxdepth: 2 + :maxdepth: 1 intro playbooks playbooks_special_topics modules + modules_by_category guides developing awx diff --git a/docsite/rst/modules.rst b/docsite/rst/modules.rst index 3348d2b804..3818800a12 100644 --- a/docsite/rst/modules.rst +++ b/docsite/rst/modules.rst @@ -1,5 +1,5 @@ -Ansible Modules -=============== +About Modules +============= .. toctree:: :maxdepth: 4 @@ -9,11 +9,10 @@ Ansible Modules Introduction ```````````` - Ansible ships with a number of modules (called the 'module library') that can be executed directly on remote hosts or through :doc:`Playbooks `. -Users can also write their own modules. These modules can control system -resources, like services, packages, or files (anything really), or + +Users can also write their own modules. These modules can control system resources, like services, packages, or files (anything really), or handle executing system commands. Let's review how we execute three different modules from the command line:: @@ -23,64 +22,28 @@ Let's review how we execute three different modules from the command line:: ansible webservers -m command -a "/sbin/reboot -t now" Each module supports taking arguments. Nearly all modules take ``key=value`` -arguments, space delimited. Some modules take no arguments, and the -command/shell modules simply take the string of the command you want to run. +arguments, space delimited. Some modules take no arguments, and the command/shell modules simply +take the string of the command you want to run. From playbooks, Ansible modules are executed in a very similar way:: - name: reboot the servers action: command /sbin/reboot -t now -Version 0.8 and higher support the following shorter syntax:: +Which can be abbreviated to: - name: reboot the servers command: /sbin/reboot -t now -All modules technically return JSON format data, though if you are using the -command line or playbooks, you don't really need to know much about -that. If you're writing your own module, you care, and this means you do -not have to write modules in any particular language -- you get to choose. +All modules technically return JSON format data, though if you are using the command line or playbooks, you don't really need to know much about +that. If you're writing your own module, you care, and this means you do not have to write modules in any particular language -- you get to choose. Modules are `idempotent`, meaning they will seek to avoid changes to the system unless a change needs to be made. When using Ansible -playbooks, these modules can trigger 'change events' in the form of notifying 'handlers' -to run additional tasks. +playbooks, these modules can trigger 'change events' in the form of notifying 'handlers' to run additional tasks. -Documentation for each module can be accessed from the command line with the -ansible-doc as well as the man command:: +Documentation for each module can be accessed from the command line with the ansible-doc tool:: - ansible-doc command - - man ansible.template - -Let's see what's available in the Ansible module library, out of the box: - -.. include:: modules/_categories.rst - -.. _ansible_doc: - -Reading Module Documentation Locally -```````````````````````````````````` - -ansible-doc is a friendly command line tool that allows you to access module documentation locally. -It comes with Ansible. - -To list documentation for a particular module:: - - ansible-doc yum | less - -To list all modules available:: - - ansible-doc --list | less - -To access modules outside of the stock module path (such as custom modules that live in your playbook directory), -use the '--module-path' option to specify the directory where the module lives. - -.. _writing_modules: - -Writing your own modules -```````````````````````` - -See :doc:`developing_modules`. + ansible-doc yum .. seealso:: diff --git a/hacking/module_formatter.py b/hacking/module_formatter.py index f6ab8d596a..787bf4e2d2 100755 --- a/hacking/module_formatter.py +++ b/hacking/module_formatter.py @@ -102,7 +102,7 @@ def rst_xline(width, char="="): ##################################################################################### -def return_data(text, options, outputname, module): +def write_data(text, options, outputname, module): ''' dumps module output to a file or the screen, as requested ''' if options.output_dir is not None: @@ -188,7 +188,7 @@ def jinja2_environment(template_dir, typ): env.filters['fmt'] = rst_fmt env.filters['xline'] = rst_xline template = env.get_template('rst.j2') - outputname = "%s.rst" + outputname = "%s_module.rst" else: raise Exception("unknown module format type: %s" % typ) @@ -214,7 +214,7 @@ def process_module(module, options, env, template, outputname, module_map): sys.stderr.write("*** ERROR: CORE MODULE MISSING DOCUMENTATION: %s, %s ***\n" % (fname, module)) sys.exit(1) if doc is None: - return + return "SKIPPED" all_keys = [] @@ -250,7 +250,7 @@ def process_module(module, options, env, template, outputname, module_map): # here is where we build the table of contents... text = template.render(doc) - return_data(text, options, outputname, module) + write_data(text, options, outputname, module) ##################################################################################### @@ -258,6 +258,10 @@ def process_category(category, categories, options, env, template, outputname): module_map = categories[category] + category_file_path = os.path.join(options.output_dir, "list_of_%s_modules.rst" % category) + category_file = open(category_file_path, "w") + print "*** recording category %s in %s ***" % (category, category_file_path) + # TODO: start a new category file category = category.replace("_"," ") @@ -266,8 +270,22 @@ def process_category(category, categories, options, env, template, outputname): modules = module_map.keys() modules.sort() + category_header = "%s Modules" % (category.title()) + underscores = "`" * len(category_header) + + category_file.write(category_header) + category_file.write("\n") + category_file.write(underscores) + category_file.write("\n") + category_file.write(".. toctree::\n") + for module in modules: - process_module(module, options, env, template, outputname, module_map) + result = process_module(module, options, env, template, outputname, module_map) + if result != "SKIPPED": + category_file.write(" %s_module\n" % module) + + + category_file.close() # TODO: end a new category file @@ -305,9 +323,19 @@ def main(): last_category = None category_names = categories.keys() category_names.sort() + + category_list_path = os.path.join(options.output_dir, "modules_by_category.rst") + category_list_file = open(category_list_path, "w") + category_list_file.write("Module Index\n") + category_list_file.write("============\n") + category_list_file.write("\n\n") + category_list_file.write(".. toctree::\n") for category in category_names: + category_list_file.write(" list_of_%s_modules\n" % category) process_category(category, categories, options, env, template, outputname) + category_list_file.close() + if __name__ == '__main__': main() From de8520da21f291b5da2a9fa5eb833429911fd34d Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 13:38:40 -0500 Subject: [PATCH 039/182] Add analytics to new docs theme. --- docsite/_themes/srtd/layout.html | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docsite/_themes/srtd/layout.html b/docsite/_themes/srtd/layout.html index ae616f6008..6c922d0db3 100644 --- a/docsite/_themes/srtd/layout.html +++ b/docsite/_themes/srtd/layout.html @@ -13,6 +13,20 @@ + + + + + {% block htmltitle %} {{ title|striptags|e }}{{ titlesuffix }} {% endblock %} @@ -133,5 +147,19 @@ {% include "versions.html" %} + + + + + From aa8beadec37c5fe20cf4212b426a3c9d1b0955ed Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 13:50:15 -0500 Subject: [PATCH 040/182] Fix a documentation item. --- library/system/setup | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/system/setup b/library/system/setup index 939752f145..816039469c 100755 --- a/library/system/setup +++ b/library/system/setup @@ -83,7 +83,7 @@ ansible all -m setup -a 'filter=ansible_*_mb' # Display only facts returned by facter. ansible all -m setup -a 'filter=facter_*' -# Display only facts returned by facter. +# Display only facts about certain interfaces. ansible all -m setup -a 'filter=ansible_eth[0-2]' """ From 0bdf615612bbd1423fb5b246b2aced628e78c96d Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 13:51:56 -0500 Subject: [PATCH 041/182] User report of DO not taking underscores so tweaking docs --- library/cloud/digital_ocean | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/cloud/digital_ocean b/library/cloud/digital_ocean index 400bf3bf54..427956b5f7 100644 --- a/library/cloud/digital_ocean +++ b/library/cloud/digital_ocean @@ -112,7 +112,7 @@ EXAMPLES = ''' - digital_ocean: > state=present command=droplet - name=my_new_droplet + name=mydroplet client_id=XXX api_key=XXX size_id=1 @@ -131,7 +131,7 @@ EXAMPLES = ''' state=present command=droplet id=123 - name=my_new_droplet + name=mydroplet client_id=XXX api_key=XXX size_id=1 @@ -147,7 +147,7 @@ EXAMPLES = ''' - digital_ocean: > state=present ssh_key_ids=id1,id2 - name=my_new_droplet + name=mydroplet client_id=XXX api_key=XXX size_id=1 From eb0509fce7a2d0c657cbb7d394baaff6f7379b54 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 14:06:15 -0500 Subject: [PATCH 042/182] Update the styling for the module pages slightly. --- hacking/templates/rst.j2 | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/hacking/templates/rst.j2 b/hacking/templates/rst.j2 index 556ea1e36c..8d279676cb 100644 --- a/hacking/templates/rst.j2 +++ b/hacking/templates/rst.j2 @@ -21,7 +21,7 @@ {% if options -%} .. raw:: html - +
    @@ -58,6 +58,7 @@ {% endif %} +{% if not plainexamples %} .. raw:: html {% for example in examples %} @@ -69,22 +70,19 @@

    {% endfor %}
    +{% endif %} {% if plainexamples %} -.. raw:: html -
    +Examples::
    +
     @{ plainexamples | escape | indent(4, True) }@
    -    
    {% endif %} {% if notes %} -.. raw:: html - -

    Notes

    - {% for note in notes %} -

    @{ note | html_ify }@

    - {% endfor %} +{% for note in notes %} +.. note:: @{ note | html_ify }@

    +{% endfor %} {% endif %} From f4aa6c53bd364408e58d1c5c7cfee90b21a066b2 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 14:12:00 -0500 Subject: [PATCH 043/182] Format module requirements as notes in Sphinx --- hacking/templates/rst.j2 | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/hacking/templates/rst.j2 b/hacking/templates/rst.j2 index 8d279676cb..13cdd435c9 100644 --- a/hacking/templates/rst.j2 +++ b/hacking/templates/rst.j2 @@ -47,15 +47,11 @@ {% endif %} {% if requirements %} -.. raw:: html +{% for req in requirements %} -

    - Requirements: - {% for req in requirements %} - @{ req | html_ify }@ - {% endfor %} -

    +.. note:: Requires @{ req | html_ify }@ +{% endfor %} {% endif %} {% if not plainexamples %} From a5e8e10bb0cb6913d240a8b378509fb8d5e00996 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 14:17:47 -0500 Subject: [PATCH 044/182] Removed unused things. --- hacking/module_formatter.py | 23 ------------ hacking/templates/man.j2 | 74 ------------------------------------- 2 files changed, 97 deletions(-) delete mode 100644 hacking/templates/man.j2 diff --git a/hacking/module_formatter.py b/hacking/module_formatter.py index 787bf4e2d2..5038b2e6ac 100755 --- a/hacking/module_formatter.py +++ b/hacking/module_formatter.py @@ -114,24 +114,6 @@ def write_data(text, options, outputname, module): ##################################################################################### -def boilerplate(): - ''' prints the boilerplate for module docs ''' - - if not os.path.exists(EXAMPLE_YAML): - print >>sys.stderr, "Missing example boiler plate: %s" % EXAMPLE_YAML - print "DOCUMENTATION = '''" - print file(EXAMPLE_YAML).read() - print "'''" - print "" - print "" - print "EXAMPLES = '''" - print "# example of doing ___ from a playbook" - print "your_module: some_arg=1 other_arg=2" - print "'''" - print "" - -##################################################################################### - def list_modules(module_dir): ''' returns a hash of categories, each category being a hash of module names to file paths ''' @@ -167,7 +149,6 @@ def generate_parser(): p.add_option("-v", "--verbose", action='store_true', default=False, help="Verbose") p.add_option("-o", "--output-dir", action="store", dest="output_dir", default=None, help="Output directory for module files") p.add_option("-I", "--includes-file", action="store", dest="includes_file", default=None, help="Create a file containing list of processed modules") - p.add_option("-G", "--generate", action="store_true", dest="do_boilerplate", default=False, help="generate boilerplate docs to stdout") p.add_option('-V', action='version', help='Show version number and exit') return p @@ -294,10 +275,6 @@ def process_category(category, categories, options, env, template, outputname): def validate_options(options): ''' validate option parser options ''' - if options.do_boilerplate: - boilerplate() - sys.exit(0) - if not options.module_dir: print >>sys.stderr, "--module-dir is required" sys.exit(1) diff --git a/hacking/templates/man.j2 b/hacking/templates/man.j2 deleted file mode 100644 index c85b14a2b1..0000000000 --- a/hacking/templates/man.j2 +++ /dev/null @@ -1,74 +0,0 @@ -.TH ANSIBLE.@{ module | upper }@ 3 "@{ now_date }@" "@{ ansible_version }@" "ANSIBLE MODULES" -.\" generated from @{ filename }@ -.SH NAME -@{ module }@ \- @{ short_description }@ -.\" ------ DESCRIPTION -.SH DESCRIPTION -{% for desc in description %} -.PP -@{ desc | convert_symbols_to_format }@ -{% endfor %} -.\" ------ OPTIONS -.\" -.\" -{% if options %} -.SH OPTIONS -{% for k in option_keys %} - {% set v = options[k] %} - -.IP @{ k }@ -{% for desc in v.description %}@{ desc | convert_symbols_to_format }@{% endfor %} -{% if v.get('choices') %} - -.IR Choices : -{% for choice in v.get('choices',[]) %}{% if choice == True %}yes{%elif choice == False %}no{% else %}@{ choice }@{% endif %}{% if not loop.last %},{%else%}.{%endif%}{% endfor %}{% endif %} -{% if v.get('required') %}(required){% endif %} -{% if v.get('default') %} (default: {% if v.get('default') == True %}yes{%elif v.get('default') == False %}no{% else %}@{ v.get('default') }@){% endif %}{% endif %} -{#---------------------------------------------- #} -{% if v.get('version_added') %} -(Added in Ansible version @{ v.get('version_added') }@.) -{% endif %} -{% endfor %} -{% endif %} -.\" -.\" -.\" ------ NOTES -{% if notes %} -.SH NOTES -{% for note in notes %} -.PP -@{ note | convert_symbols_to_format }@ -{% endfor %} -{% endif %} -.\" -.\" -.\" ------ EXAMPLES -{% if examples is defined %} -.SH EXAMPLES -{% for e in examples %} -.PP -{% if e['description'] %} -@{ e['description'] | convert_symbols_to_format }@ -{% endif %} - -.nf -@{ e['code'] }@ -.fi -{% endfor %} -{% endif %} -.\" ------ PLAINEXAMPLES -{% if plainexamples is defined %} -.SH EXAMPLES -.nf -@{ plainexamples }@ -.fi -{% endif %} - -.\" ------- AUTHOR -{% if author is defined %} -.SH AUTHOR -@{ author }@ -{% endif %} -.SH SEE ALSO -.IR ansible (1), -.I http://ansible.github.com/modules.html#@{docuri}@ From af1f8db51d659f3fbc23537c8fbe51f791d0b7d8 Mon Sep 17 00:00:00 2001 From: Mikhail Sobolev Date: Wed, 25 Dec 2013 20:38:40 +0200 Subject: [PATCH 045/182] remove trailing whitespaces --- docsite/Makefile | 2 +- hacking/module_formatter.py | 8 ++++---- hacking/templates/rst.j2 | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docsite/Makefile b/docsite/Makefile index f31fd3d58f..ab4649e370 100644 --- a/docsite/Makefile +++ b/docsite/Makefile @@ -27,6 +27,6 @@ clean: .PHONEY: docs clean modules: $(FORMATTER) ../hacking/templates/rst.j2 - PYTHONPATH=../lib $(FORMATTER) -t rst --template-dir=../hacking/templates --module-dir=../library -o rst/ + PYTHONPATH=../lib $(FORMATTER) -t rst --template-dir=../hacking/templates --module-dir=../library -o rst/ diff --git a/hacking/module_formatter.py b/hacking/module_formatter.py index 5038b2e6ac..8490a7d432 100755 --- a/hacking/module_formatter.py +++ b/hacking/module_formatter.py @@ -146,7 +146,7 @@ def generate_parser(): p.add_option("-M", "--module-dir", action="store", dest="module_dir", default=MODULEDIR, help="Ansible library path") p.add_option("-T", "--template-dir", action="store", dest="template_dir", default="hacking/templates", help="directory containing Jinja2 templates") p.add_option("-t", "--type", action='store', dest='type', choices=['html', 'latex', 'man', 'rst', 'json', 'markdown', 'js'], default='latex', help="Document type") - p.add_option("-v", "--verbose", action='store_true', default=False, help="Verbose") + p.add_option("-v", "--verbose", action='store_true', default=False, help="Verbose") p.add_option("-o", "--output-dir", action="store", dest="output_dir", default=None, help="Output directory for module files") p.add_option("-I", "--includes-file", action="store", dest="includes_file", default=None, help="Create a file containing list of processed modules") p.add_option('-V', action='version', help='Show version number and exit') @@ -241,7 +241,7 @@ def process_category(category, categories, options, env, template, outputname): category_file_path = os.path.join(options.output_dir, "list_of_%s_modules.rst" % category) category_file = open(category_file_path, "w") - print "*** recording category %s in %s ***" % (category, category_file_path) + print "*** recording category %s in %s ***" % (category, category_file_path) # TODO: start a new category file @@ -300,14 +300,14 @@ def main(): last_category = None category_names = categories.keys() category_names.sort() - + category_list_path = os.path.join(options.output_dir, "modules_by_category.rst") category_list_file = open(category_list_path, "w") category_list_file.write("Module Index\n") category_list_file.write("============\n") category_list_file.write("\n\n") category_list_file.write(".. toctree::\n") - + for category in category_names: category_list_file.write(" list_of_%s_modules\n" % category) process_category(category, categories, options, env, template, outputname) diff --git a/hacking/templates/rst.j2 b/hacking/templates/rst.j2 index 13cdd435c9..28f4596706 100644 --- a/hacking/templates/rst.j2 +++ b/hacking/templates/rst.j2 @@ -15,7 +15,7 @@ {% endif %} {% for desc in description -%} -@{ desc | convert_symbols_to_format }@ +@{ desc | convert_symbols_to_format }@ {% endfor %} {% if options -%} From 7965d331f37f399c8e042718336f76f9d976dcee Mon Sep 17 00:00:00 2001 From: Mikhail Sobolev Date: Wed, 25 Dec 2013 21:05:01 +0200 Subject: [PATCH 046/182] do not produce too deep toc for category lists --- hacking/module_formatter.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hacking/module_formatter.py b/hacking/module_formatter.py index 8490a7d432..a6c0cf0eaf 100755 --- a/hacking/module_formatter.py +++ b/hacking/module_formatter.py @@ -254,16 +254,19 @@ def process_category(category, categories, options, env, template, outputname): category_header = "%s Modules" % (category.title()) underscores = "`" * len(category_header) - category_file.write(category_header) - category_file.write("\n") - category_file.write(underscores) - category_file.write("\n") - category_file.write(".. toctree::\n") + category_file.write("""\ +%s +%s + +.. toctree:: + :maxdepth: 1 + +""" % (category_header, underscores)) for module in modules: result = process_module(module, options, env, template, outputname, module_map) if result != "SKIPPED": - category_file.write(" %s_module\n" % module) + category_file.write(" %s_module\n" % module) category_file.close() From 1713013dbabfa6686ba6d3c12b532364bb750afa Mon Sep 17 00:00:00 2001 From: Mikhail Sobolev Date: Wed, 25 Dec 2013 21:05:43 +0200 Subject: [PATCH 047/182] provide sections and local TOC for module documentation --- hacking/templates/rst.j2 | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/hacking/templates/rst.j2 b/hacking/templates/rst.j2 index 28f4596706..c646a0e13f 100644 --- a/hacking/templates/rst.j2 +++ b/hacking/templates/rst.j2 @@ -2,6 +2,13 @@ @{ module }@ ++++++++++++++++++++++++++++++++++++++ +{% if author %} +:Author: @{ author }@ +{% endif %} + +.. contents:: + :local: + :depth: 1 {# ------------------------------------------ # @@ -10,6 +17,9 @@ # --------------------------------------------#} +Synopsis +-------- + {% if version_added is defined -%} .. versionadded:: @{ version_added }@ {% endif %} @@ -19,6 +29,9 @@ {% endfor %} {% if options -%} +Options +------- + .. raw:: html
    parameter required
    @@ -54,7 +67,10 @@ {% endfor %} {% endif %} -{% if not plainexamples %} +{% if examples or plainexamples %} +Examples +-------- + .. raw:: html {% for example in examples %} @@ -66,19 +82,18 @@

    {% endfor %}
    -{% endif %} {% if plainexamples %} -Examples:: +:: @{ plainexamples | escape | indent(4, True) }@ {% endif %} - +{% endif %} {% if notes %} {% for note in notes %} -.. note:: @{ note | html_ify }@

    +.. note:: @{ note | convert_symbols_to_format }@ {% endfor %} {% endif %} From 8b680343dd49b767f34e1129fbfaebc0b3924799 Mon Sep 17 00:00:00 2001 From: Mikhail Sobolev Date: Wed, 25 Dec 2013 21:08:14 +0200 Subject: [PATCH 048/182] fix example formatting --- docsite/rst/modules.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docsite/rst/modules.rst b/docsite/rst/modules.rst index 3818800a12..1e2a851d4a 100644 --- a/docsite/rst/modules.rst +++ b/docsite/rst/modules.rst @@ -30,7 +30,7 @@ From playbooks, Ansible modules are executed in a very similar way:: - name: reboot the servers action: command /sbin/reboot -t now -Which can be abbreviated to: +Which can be abbreviated to:: - name: reboot the servers command: /sbin/reboot -t now From fce90e0370d3811e4a9058dde02fae5d737b5c9c Mon Sep 17 00:00:00 2001 From: Mikhail Sobolev Date: Wed, 25 Dec 2013 21:20:11 +0200 Subject: [PATCH 049/182] use rst formatting for sphinx part --- hacking/templates/rst.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hacking/templates/rst.j2 b/hacking/templates/rst.j2 index c646a0e13f..91b3967520 100644 --- a/hacking/templates/rst.j2 +++ b/hacking/templates/rst.j2 @@ -62,7 +62,7 @@ Options {% if requirements %} {% for req in requirements %} -.. note:: Requires @{ req | html_ify }@ +.. note:: Requires @{ req | convert_symbols_to_format }@ {% endfor %} {% endif %} From d6e28b4c579563cd17d54fed53ccfc1aac2e4df4 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 21:05:25 -0500 Subject: [PATCH 050/182] Enable the very cool "Edit on GitHub" links for pages where the RST is straight out of github. --- docsite/_themes/srtd/breadcrumbs.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docsite/_themes/srtd/breadcrumbs.html b/docsite/_themes/srtd/breadcrumbs.html index 1d2e778982..834cc649b2 100644 --- a/docsite/_themes/srtd/breadcrumbs.html +++ b/docsite/_themes/srtd/breadcrumbs.html @@ -1,9 +1,9 @@ From 83d298accce9cbc8be1449558309322941556555 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 21:29:54 -0500 Subject: [PATCH 051/182] Add note about how to submit doc changes to generated module docs, limit TOC depth for module category list. --- docsite/_themes/srtd/footer.html | 3 ++- hacking/module_formatter.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docsite/_themes/srtd/footer.html b/docsite/_themes/srtd/footer.html index e42d753ff6..684469cac1 100644 --- a/docsite/_themes/srtd/footer.html +++ b/docsite/_themes/srtd/footer.html @@ -26,5 +26,6 @@ {%- endif %}

    - {% trans %}Sphinx theme provided by Read the Docs{% endtrans %} +Ansible docs are generated from Sphinx using a theme provided by Read the Docs. {% if pagename.endswith("_module") %}. Module documentation is not edited directly, but is generated from the source code for the modules. To submit an update to module docs, edit the 'DOCUMENTATION' metadata in the module source tree. {% endif %} + diff --git a/hacking/module_formatter.py b/hacking/module_formatter.py index a6c0cf0eaf..237486db24 100755 --- a/hacking/module_formatter.py +++ b/hacking/module_formatter.py @@ -310,9 +310,10 @@ def main(): category_list_file.write("============\n") category_list_file.write("\n\n") category_list_file.write(".. toctree::\n") + category_list_file.write(" :maxdepth: 1\n\n") for category in category_names: - category_list_file.write(" list_of_%s_modules\n" % category) + category_list_file.write(" list_of_%s_modules\n" % category) process_category(category, categories, options, env, template, outputname) category_list_file.close() From 1119ecebeef638e8a0ee26c9567903335bc99376 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Wed, 25 Dec 2013 21:36:23 -0500 Subject: [PATCH 052/182] Make AnsibleWorks a hyperlink in docs footer. --- docsite/_themes/srtd/footer.html | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/docsite/_themes/srtd/footer.html b/docsite/_themes/srtd/footer.html index 684469cac1..0e53d0eb21 100644 --- a/docsite/_themes/srtd/footer.html +++ b/docsite/_themes/srtd/footer.html @@ -13,13 +13,7 @@

    - {%- if show_copyright %} - {%- if hasdoc('copyright') %} - {% trans path=pathto('copyright'), copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} - {%- else %} - {% trans copyright=copyright|e %}© Copyright {{ copyright }}.{% endtrans %} - {%- endif %} - {%- endif %} + © Copyright 2013 AnsibleWorks. {%- if last_updated %} {% trans last_updated=last_updated|e %}Last updated on {{ last_updated }}.{% endtrans %} From 0711447d3a580c30f97eca2d6aaa7c8b63843201 Mon Sep 17 00:00:00 2001 From: Maykel Moya Date: Thu, 26 Dec 2013 12:34:33 +0100 Subject: [PATCH 053/182] Missing "is" in file module description --- library/files/file | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/files/file b/library/files/file index 8dcead1b40..86ee83a693 100644 --- a/library/files/file +++ b/library/files/file @@ -124,7 +124,7 @@ options: choices: [ "yes", "no" ] description: - 'force the creation of the symlinks in two cases: the source file does - not exist (but will appear later); the destination exists and a file (so, we need to unlink the + not exist (but will appear later); the destination exists and is a file (so, we need to unlink the "path" file and create symlink to the "src" file in place of it).' notes: - See also M(copy), M(template), M(assemble) From 81bc44ccc2ed7a1d6d8097c27a64f33ea43c0398 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 26 Dec 2013 11:41:05 -0500 Subject: [PATCH 054/182] Don't show tracebacks on Control-C. --- bin/ansible-playbook | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bin/ansible-playbook b/bin/ansible-playbook index 75bbbe054d..c0db66993c 100755 --- a/bin/ansible-playbook +++ b/bin/ansible-playbook @@ -270,4 +270,7 @@ if __name__ == "__main__": except errors.AnsibleError, e: display("ERROR: %s" % e, color='red', stderr=True) sys.exit(1) + except KeyboardInterrupt, ke: + display("ERROR: interrupted", color='red', stderr=True) + sys.exit(1) From b49cb2e540bd8460f4490e11870c52509c5eb482 Mon Sep 17 00:00:00 2001 From: lichesser Date: Thu, 26 Dec 2013 17:53:17 +0100 Subject: [PATCH 055/182] Partial fix for #4565. Works only for Debian 7 and later --- library/system/setup | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/system/setup b/library/system/setup index 816039469c..9a31b10da4 100755 --- a/library/system/setup +++ b/library/system/setup @@ -118,7 +118,8 @@ class Facts(object): '/etc/alpine-release': 'Alpine', '/etc/release': 'Solaris', '/etc/arch-release': 'Archlinux', - '/etc/SuSE-release': 'SuSE' } + '/etc/SuSE-release': 'SuSE', + '/etc/os-release': 'Debian' } SELINUX_MODE_DICT = { 1: 'enforcing', 0: 'permissive', -1: 'disabled' } # A list of dicts. If there is a platform with more than one @@ -328,6 +329,11 @@ class Facts(object): elif name == 'SuSE': data = get_file_content(path).splitlines() self.facts['distribution_release'] = data[2].split('=')[1].strip() + elif name == 'Debian': + data = get_file_content(path).split('\n')[0] + release = re.search("PRETTY_NAME.+ \(?([^ ]+?)\)?\"", data) + if release: + self.facts['distribution_release'] = release.groups()[0] else: self.facts['distribution'] = name From 488ef22f5e232f2a2cd33aecfc93343fa399cddd Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Thu, 26 Dec 2013 12:01:41 -0500 Subject: [PATCH 056/182] added more usefull example for jinja2 overrides (this one is actually used) Signed-off-by: Brian Coca --- library/files/template | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/files/template b/library/files/template index 35ac831e18..41dfba0804 100644 --- a/library/files/template +++ b/library/files/template @@ -48,8 +48,11 @@ options: required: false notes: - Since Ansible version 0.9, templates are loaded with C(trim_blocks=True). - - 'You can override jinja2 settings by adding a special header to template file. - i.e. C(#jinja2: trim_blocks: False)' + + - "Also, you can override jinja2 settings by adding a special header to template file. + i.e. C(#jinja2:variable_start_string:'[%' , variable_end_string:'%]') + which changes the variable interpolation markers to [% var %] instead of {{ var }}." + requirements: [] author: Michael DeHaan ''' From 5b2fda6fa6a71e09462fb0e0f1642927511213d6 Mon Sep 17 00:00:00 2001 From: beefsalad Date: Thu, 26 Dec 2013 12:25:52 -0600 Subject: [PATCH 057/182] added descriptions for yum options Updated main description to state that the yum module will handle package groups. Added descriptions for each example, as it is unclear to (some) users that @Name implies a group install. --- library/packaging/yum | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/packaging/yum b/library/packaging/yum index 4c930f2f77..a33aa797f4 100644 --- a/library/packaging/yum +++ b/library/packaging/yum @@ -31,7 +31,7 @@ module: yum version_added: historical short_description: Manages packages with the I(yum) package manager description: - - Will install, upgrade, remove, and list packages with the I(yum) package manager. + - Will install, upgrade, remove, and list packages or package groups with the I(yum) package manager. options: name: description: @@ -94,12 +94,19 @@ author: Seth Vidal ''' EXAMPLES = ''' +# description: installs the latest version of httpd - yum: name=httpd state=latest +# description: removes the httpd package - yum: name=httpd state=removed +# description: installs the latest version of httpd from the testing repo - yum: name=httpd enablerepo=testing state=installed +# description: upgrades all packages - yum: name=* state=latest +# description: installs the nginx rpm from a remote repo - yum: name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present +# description: installs nginx rpm from a local file - yum: name=/usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present +# description: installs the package group 'Development tool' - yum: name="@Development tools" state=present ''' From cfcdc3403e6f0b40f1c7727d4d25907536650c9b Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 26 Dec 2013 13:46:09 -0500 Subject: [PATCH 058/182] Use task names in examples. --- library/packaging/yum | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/library/packaging/yum b/library/packaging/yum index a33aa797f4..744a876a04 100644 --- a/library/packaging/yum +++ b/library/packaging/yum @@ -31,7 +31,7 @@ module: yum version_added: historical short_description: Manages packages with the I(yum) package manager description: - - Will install, upgrade, remove, and list packages or package groups with the I(yum) package manager. + - Installs, upgrade, removes, and lists packages and groups with the I(yum) package manager. options: name: description: @@ -41,7 +41,7 @@ options: aliases: [] list: description: - - Various non-idempotent commands for usage with C(/usr/bin/ansible) and I(not) playbooks. See examples. + - Various (non-idempotent) commands for usage with C(/usr/bin/ansible) and I(not) playbooks. See examples. required: false default: null state: @@ -94,20 +94,26 @@ author: Seth Vidal ''' EXAMPLES = ''' -# description: installs the latest version of httpd -- yum: name=httpd state=latest -# description: removes the httpd package -- yum: name=httpd state=removed -# description: installs the latest version of httpd from the testing repo -- yum: name=httpd enablerepo=testing state=installed -# description: upgrades all packages -- yum: name=* state=latest -# description: installs the nginx rpm from a remote repo -- yum: name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present -# description: installs nginx rpm from a local file -- yum: name=/usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present -# description: installs the package group 'Development tool' -- yum: name="@Development tools" state=present +- name: install the latest version of Apache + yum: name=httpd state=latest + +- name: remove the Apache package + yum: name=httpd state=removed + +- name: install the latest version of Apche from the testing repo + yum: name=httpd enablerepo=testing state=installed + +- name: upgrade all packages + yum: name=* state=latest + +- name: install the nginx rpm from a remote repo + yum: name=http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present + +- name: install nginx rpm from a local file + yum: name=/usr/local/src/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present + +- name: install the 'Development tools' package group + yum: name="@Development tools" state=present ''' def_qf = "%{name}-%{version}-%{release}.%{arch}" From 8b7327a2f65c77c2fe5ca0436ba97f71ed1d0629 Mon Sep 17 00:00:00 2001 From: Mikhail Sobolev Date: Thu, 26 Dec 2013 00:44:01 +0200 Subject: [PATCH 059/182] make examples/DOCUMENTATION.yml a link --- docsite/rst/developing_modules.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docsite/rst/developing_modules.rst b/docsite/rst/developing_modules.rst index 342dcba23b..5ce843352d 100644 --- a/docsite/rst/developing_modules.rst +++ b/docsite/rst/developing_modules.rst @@ -371,7 +371,7 @@ syntax highlighting before you include it in your Python file. Example +++++++ -See an example documentation string in the checkout under examples/DOCUMENTATION.yml +See an example documentation string in the checkout under `examples/DOCUMENTATION.yml `_. Include it in your module file like this:: From 84948292f33b15d05c462fee6765309df2812a35 Mon Sep 17 00:00:00 2001 From: Mikhail Sobolev Date: Thu, 26 Dec 2013 01:06:55 +0200 Subject: [PATCH 060/182] include short_description in the module page's title --- hacking/templates/rst.j2 | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/hacking/templates/rst.j2 b/hacking/templates/rst.j2 index 91b3967520..2fa4348846 100644 --- a/hacking/templates/rst.j2 +++ b/hacking/templates/rst.j2 @@ -1,7 +1,15 @@ .. _@{ module }@: -@{ module }@ -++++++++++++++++++++++++++++++++++++++ +{% if short_description %} +{% set title = module + ' - ' + short_description|convert_symbols_to_format %} +{% else %} +{% set title = module %} +{% endif %} +{% set title_len = title|length %} + +@{ title }@ +@{ '+' * title_len }@ + {% if author %} :Author: @{ author }@ {% endif %} From 8733b5da8ba87d427fd16750d3b96db1cc1039b1 Mon Sep 17 00:00:00 2001 From: Mikhail Sobolev Date: Thu, 26 Dec 2013 01:07:49 +0200 Subject: [PATCH 061/182] fix short/full description for elasticache --- library/cloud/elasticache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/cloud/elasticache b/library/cloud/elasticache index 9b40107d98..fa175bb397 100644 --- a/library/cloud/elasticache +++ b/library/cloud/elasticache @@ -17,9 +17,9 @@ DOCUMENTATION = """ --- module: elasticache +short_description: Manage cache clusters in Amazon Elasticache. description: - Manage cache clusters in Amazon Elasticache. -short_description: Manage cache clusters in Amazon Elasticache. - Returns information about the specified cache cluster. version_added: "1.4" requirements: [ "boto" ] From 1c29f043b442ba133fe39385be322aa60437cb05 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 26 Dec 2013 14:32:01 -0500 Subject: [PATCH 062/182] Add contents to various pages with more than one topic per page. --- docsite/rst/developing_api.rst | 2 ++ docsite/rst/developing_inventory.rst | 2 ++ docsite/rst/developing_modules.rst | 2 ++ docsite/rst/developing_plugins.rst | 2 ++ docsite/rst/intro_adhoc.rst | 2 ++ docsite/rst/intro_configuration.rst | 2 ++ docsite/rst/intro_dynamic_inventory.rst | 2 ++ docsite/rst/intro_getting_started.rst | 6 ++++-- docsite/rst/intro_inventory.rst | 2 ++ docsite/rst/intro_patterns.rst | 2 ++ docsite/rst/playbooks_best_practices.rst | 2 ++ docsite/rst/playbooks_checkmode.rst | 2 ++ docsite/rst/playbooks_conditionals.rst | 3 +++ docsite/rst/playbooks_delegation.rst | 2 ++ docsite/rst/playbooks_error_handling.rst | 2 ++ docsite/rst/playbooks_lookups.rst | 2 ++ docsite/rst/playbooks_loops.rst | 2 ++ docsite/rst/playbooks_roles.rst | 2 ++ docsite/rst/playbooks_variables.rst | 2 ++ 19 files changed, 41 insertions(+), 2 deletions(-) diff --git a/docsite/rst/developing_api.rst b/docsite/rst/developing_api.rst index da5ee9b1c8..15f9755e36 100644 --- a/docsite/rst/developing_api.rst +++ b/docsite/rst/developing_api.rst @@ -1,6 +1,8 @@ Python API ========== +.. contents:: Topics + There are several interesting ways to use Ansible from an API perspective. You can use the Ansible python API to control nodes, you can extend Ansible to respond to various python events, you can write various plugins, and you can plug in inventory data from external data sources. This document diff --git a/docsite/rst/developing_inventory.rst b/docsite/rst/developing_inventory.rst index 5fd8678517..760ad80db9 100644 --- a/docsite/rst/developing_inventory.rst +++ b/docsite/rst/developing_inventory.rst @@ -1,6 +1,8 @@ Developing Dynamic Inventory Sources ==================================== +.. contents:: Topics + As described in `intro_inventory_dynamic`, ansible can pull inventory information from dynamic sources, including cloud sources. How do we write a new one? diff --git a/docsite/rst/developing_modules.rst b/docsite/rst/developing_modules.rst index 5ce843352d..d9892774fe 100644 --- a/docsite/rst/developing_modules.rst +++ b/docsite/rst/developing_modules.rst @@ -1,6 +1,8 @@ Developing Modules ================== +.. contents:: Topics + Ansible modules are reusable units of magic that can be used by the Ansible API, or by the `ansible` or `ansible-playbook` programs. diff --git a/docsite/rst/developing_plugins.rst b/docsite/rst/developing_plugins.rst index 3e967f3136..32ff187cab 100644 --- a/docsite/rst/developing_plugins.rst +++ b/docsite/rst/developing_plugins.rst @@ -1,6 +1,8 @@ Developing Plugins ================== +.. contents:: Topics + Ansible is pluggable in a lot of other ways separate from inventory scripts and callbacks. Many of these features are there to cover fringe use cases and are infrequently needed, and others are pluggable simply because they are there to implement core features in ansible and were most convenient to be made pluggable. diff --git a/docsite/rst/intro_adhoc.rst b/docsite/rst/intro_adhoc.rst index 7ed54f2dbb..ba1033d061 100644 --- a/docsite/rst/intro_adhoc.rst +++ b/docsite/rst/intro_adhoc.rst @@ -1,6 +1,8 @@ Introduction To Ad-Hoc Commands =============================== +.. contents:: Topics + .. highlight:: bash The following examples show how to use `/usr/bin/ansible` for running diff --git a/docsite/rst/intro_configuration.rst b/docsite/rst/intro_configuration.rst index 543b11cc48..b4f675f414 100644 --- a/docsite/rst/intro_configuration.rst +++ b/docsite/rst/intro_configuration.rst @@ -1,6 +1,8 @@ The Ansible Configuration File ++++++++++++++++++++++++++++++ +.. contents:: Topics + .. highlight:: bash Certain things in Ansible are adjustable in a configuration file. In general, the stock configuration is probably diff --git a/docsite/rst/intro_dynamic_inventory.rst b/docsite/rst/intro_dynamic_inventory.rst index d1aa4cae5b..11a2dda9a5 100644 --- a/docsite/rst/intro_dynamic_inventory.rst +++ b/docsite/rst/intro_dynamic_inventory.rst @@ -3,6 +3,8 @@ Dynamic Inventory ================= +.. contents:: Topics + Often a user of a configuration management system will want to keep inventory in a different software system. Ansible provides a basic text-based system as described in :doc:`intro_inventory` but what if you want to use something else? diff --git a/docsite/rst/intro_getting_started.rst b/docsite/rst/intro_getting_started.rst index fc0c651951..ae1172d307 100644 --- a/docsite/rst/intro_getting_started.rst +++ b/docsite/rst/intro_getting_started.rst @@ -1,10 +1,12 @@ Getting Started =============== +.. contents:: Topics + .. _gs_about: -About -````` +Foreword +```````` Now that you've read :doc:`intro_installation` and installed Ansible, it's time to dig in and get started with some commands. diff --git a/docsite/rst/intro_inventory.rst b/docsite/rst/intro_inventory.rst index 5d22464776..604603d516 100644 --- a/docsite/rst/intro_inventory.rst +++ b/docsite/rst/intro_inventory.rst @@ -3,6 +3,8 @@ Inventory ========= +.. contents:: Topics + Ansible works against multiple systems in your infrastructure at the same time. It does this by selecting portions of systems listed in Ansible's inventory file, which defaults to being saved in diff --git a/docsite/rst/intro_patterns.rst b/docsite/rst/intro_patterns.rst index 4504ef21ef..7d8ebe0f52 100644 --- a/docsite/rst/intro_patterns.rst +++ b/docsite/rst/intro_patterns.rst @@ -1,6 +1,8 @@ Patterns ++++++++ +.. contents:: Topics + Patterns in Ansible are how we decide which hosts to manage. This can mean what hosts to communicate with, but in terms of :doc:`playbooks` it actually means what hosts to apply a particular configuration or IT process to. diff --git a/docsite/rst/playbooks_best_practices.rst b/docsite/rst/playbooks_best_practices.rst index 275a4d3ea5..efa4a4cc32 100644 --- a/docsite/rst/playbooks_best_practices.rst +++ b/docsite/rst/playbooks_best_practices.rst @@ -5,6 +5,8 @@ Here are some tips for making the most of Ansible playbooks. You can find some example playbooks illustrating these best practices in our `ansible-examples repository `_. (NOTE: These may not use all of the features in the latest release, but are still an excellent reference!). +.. contents:: Topics + .. _content_organization: Content Organization diff --git a/docsite/rst/playbooks_checkmode.rst b/docsite/rst/playbooks_checkmode.rst index 40ee15ff15..cc34ff5bdf 100644 --- a/docsite/rst/playbooks_checkmode.rst +++ b/docsite/rst/playbooks_checkmode.rst @@ -3,6 +3,8 @@ Check Mode ("Dry Run") .. versionadded:: 1.1 +.. contents:: Topics + When ansible-playbook is executed with --check it will not make any changes on remote systems. Instead, any module instrumented to support 'check mode' (which contains most of the primary core modules, but it is not required that all modules do this) will report what changes they would have made rather than making them. Other modules that do not support check mode will also take no action, but just will not report what changes they might have made. diff --git a/docsite/rst/playbooks_conditionals.rst b/docsite/rst/playbooks_conditionals.rst index ab5f1e08ef..75daad2e4c 100644 --- a/docsite/rst/playbooks_conditionals.rst +++ b/docsite/rst/playbooks_conditionals.rst @@ -1,6 +1,9 @@ Conditionals ============ +.. contents:: Topics + + Often the result of a play may depend on the value of a variable, fact (something learned about the remote system), or previous task result. In some cases, the values of variables may depend on other variables. Further, additional groups can be created to manage hosts based on diff --git a/docsite/rst/playbooks_delegation.rst b/docsite/rst/playbooks_delegation.rst index 86e67902c7..535d2e97bb 100644 --- a/docsite/rst/playbooks_delegation.rst +++ b/docsite/rst/playbooks_delegation.rst @@ -1,6 +1,8 @@ Delegation, Rolling Updates, and Local Actions ============================================== +.. contents:: Topics + Being designed for multi-tier deployments since the beginning, Ansible is great at doing things on one host on behalf of another, or doing local steps with reference to some remote hosts. This in particular this is very applicable when setting up continuous deployment infrastructure or zero downtime rolling updates, where you might be talking with load balancers or monitoring systems. diff --git a/docsite/rst/playbooks_error_handling.rst b/docsite/rst/playbooks_error_handling.rst index e4c5e413fe..af5c021da5 100644 --- a/docsite/rst/playbooks_error_handling.rst +++ b/docsite/rst/playbooks_error_handling.rst @@ -1,6 +1,8 @@ Error Handling In Playbooks =========================== +.. contents:: Topics + Ansible normally has defaults that make sure to check the return codes of commands and modules and it fails fast -- forcing an error to be dealt with unless you decide otherwise. diff --git a/docsite/rst/playbooks_lookups.rst b/docsite/rst/playbooks_lookups.rst index d14645476e..52ddaedb19 100644 --- a/docsite/rst/playbooks_lookups.rst +++ b/docsite/rst/playbooks_lookups.rst @@ -7,6 +7,8 @@ in Ansible, and are typically used to load variables or templates with informati .. note:: This is considered an advanced feature, and many users will probably not rely on these features. +.. contents:: Topics + .. _getting_file_contents: Intro to Lookups: Getting File Contents diff --git a/docsite/rst/playbooks_loops.rst b/docsite/rst/playbooks_loops.rst index 9b73c74473..672b7f5107 100644 --- a/docsite/rst/playbooks_loops.rst +++ b/docsite/rst/playbooks_loops.rst @@ -6,6 +6,8 @@ repeat a polling step until a certain result is reached. This chapter is all about how to use loops in playbooks. +.. contents:: Topics + .. _standard_loops: Standard Loops diff --git a/docsite/rst/playbooks_roles.rst b/docsite/rst/playbooks_roles.rst index 4af68eb64c..05fd681f26 100644 --- a/docsite/rst/playbooks_roles.rst +++ b/docsite/rst/playbooks_roles.rst @@ -1,6 +1,8 @@ Playbook Roles and Include Statements ===================================== +.. contents:: Topics + Introduction ```````````` diff --git a/docsite/rst/playbooks_variables.rst b/docsite/rst/playbooks_variables.rst index 8e17d68c83..9d25a95873 100644 --- a/docsite/rst/playbooks_variables.rst +++ b/docsite/rst/playbooks_variables.rst @@ -1,6 +1,8 @@ Variables ========= +.. contents:: Topics + While automation exists to make it easier to make things repeatable, all of your systems are likely not exactly alike. All of your systems are likely not the same. On some systems you may want to set some behavior From bde41bac359313207dadcc480a8fafb7261f465f Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 26 Dec 2013 14:33:25 -0500 Subject: [PATCH 063/182] Fix some small docsite formatting things. --- docsite/rst/playbooks_variables.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docsite/rst/playbooks_variables.rst b/docsite/rst/playbooks_variables.rst index 9d25a95873..127c0bc300 100644 --- a/docsite/rst/playbooks_variables.rst +++ b/docsite/rst/playbooks_variables.rst @@ -67,7 +67,7 @@ This can be nice as it's right there when you are reading the playbook. .. _included_variables: Variables defined from included files and roles ------------------------------------------------ +``````````````````````````````````````````````` It turns out we've already talked about variables in another place too. @@ -85,13 +85,13 @@ Ansible allows you to reference variables in your playbooks using the Jinja2 templating system. While you can do a lot of complex things in Jinja, only the basics are things you really need to learn at first. -For instance, in a simple template, you can do something like +For instance, in a simple template, you can do something like:: My amp goes to {{ max_amp_value }} And that will provide the most basic form of variable substitution. -This is also valid directly in playbooks, and you'll occasionally want to do things like: +This is also valid directly in playbooks, and you'll occasionally want to do things like:: template: src=foo.cfg.j2 dest={{ remote_install_path}}/foo.cfg From eec9b6c8b8f32fb7a1ca945706eb51967d302066 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 26 Dec 2013 14:34:35 -0500 Subject: [PATCH 064/182] Fix a intra-doc link. --- docsite/rst/playbooks_variables.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docsite/rst/playbooks_variables.rst b/docsite/rst/playbooks_variables.rst index 127c0bc300..a7f42aeb51 100644 --- a/docsite/rst/playbooks_variables.rst +++ b/docsite/rst/playbooks_variables.rst @@ -71,7 +71,7 @@ Variables defined from included files and roles It turns out we've already talked about variables in another place too. -As described in `intro_roles`, variables can also be included in the playbook via include files, which may or may +As described in :doc:`intro_roles`, variables can also be included in the playbook via include files, which may or may not be part of an "Ansible Role". Usage of roles is preferred as it provides a nice organizational system. .. _about_jinja2: From af98ce8433871f2799020ef85615536b21bee8b6 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 26 Dec 2013 14:35:37 -0500 Subject: [PATCH 065/182] Add topics index to installation page. --- docsite/rst/intro_installation.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docsite/rst/intro_installation.rst b/docsite/rst/intro_installation.rst index 61ff6a6fe3..39ffecc483 100644 --- a/docsite/rst/intro_installation.rst +++ b/docsite/rst/intro_installation.rst @@ -1,6 +1,8 @@ Installation ============ +.. contents:: Topics + .. _getting_ansible: Getting Ansible From f532b449d71deb3eb8b0928796a77a5eacbb3eec Mon Sep 17 00:00:00 2001 From: Jim Kleckner Date: Thu, 26 Dec 2013 16:21:16 -0800 Subject: [PATCH 066/182] Fix mysql_db dump and import to use port argument The code for mysql_db did not pass the port argument when state=dump or state=import. --- library/database/mysql_db | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/database/mysql_db b/library/database/mysql_db index f949cced30..b6fbe5f83f 100644 --- a/library/database/mysql_db +++ b/library/database/mysql_db @@ -116,13 +116,13 @@ def db_delete(cursor, db): cursor.execute(query) return True -def db_dump(module, host, user, password, db_name, target, socket=None): +def db_dump(module, host, user, password, db_name, target, port, socket=None): cmd = module.get_bin_path('mysqldump', True) cmd += " --quick --user=%s --password=%s" %(user, password) if socket is not None: cmd += " --socket=%s" % socket else: - cmd += " --host=%s" % host + cmd += " --host=%s --port=%s" % (host, port) cmd += " %s" % db_name if os.path.splitext(target)[-1] == '.gz': cmd = cmd + ' | gzip > ' + target @@ -133,13 +133,13 @@ def db_dump(module, host, user, password, db_name, target, socket=None): rc, stdout, stderr = module.run_command(cmd) return rc, stdout, stderr -def db_import(module, host, user, password, db_name, target, socket=None): +def db_import(module, host, user, password, db_name, target, port, socket=None): cmd = module.get_bin_path('mysql', True) cmd += " --user=%s --password=%s" %(user, password) if socket is not None: cmd += " --socket=%s" % socket else: - cmd += " --host=%s" % host + cmd += " --host=%s --port=%s" % (host, port) cmd += " -D %s" % db_name if os.path.splitext(target)[-1] == '.gz': cmd = 'gunzip < ' + target + ' | ' + cmd @@ -282,6 +282,7 @@ def main(): elif state == "dump": rc, stdout, stderr = db_dump(module, login_host, login_user, login_password, db, target, + port=module.params['login_port'], socket=module.params['login_unix_socket']) if rc != 0: module.fail_json(msg="%s" % stderr) @@ -290,6 +291,7 @@ def main(): elif state == "import": rc, stdout, stderr = db_import(module, login_host, login_user, login_password, db, target, + port=module.params['login_port'], socket=module.params['login_unix_socket']) if rc != 0: module.fail_json(msg="%s" % stderr) From edca1d69cf1b5c86de6462bfc1fcc979b0083d87 Mon Sep 17 00:00:00 2001 From: Jim Kleckner Date: Thu, 26 Dec 2013 16:32:32 -0800 Subject: [PATCH 067/182] Fix documentation example for the fail module The example for the fail module doesn't work: http://www.ansibleworks.com/docs/modules.html#fail The current text shows: - fail: msg="The system may not be provisioned according to the CMDB status." when: "{{ cmdb_status }} != 'to-be-staged'" The "when" documentation indicates that the argument is already a Jinja2 expression: http://www.ansibleworks.com/docs/playbooks_conditionals.html#the-when-statement Thus, the following is when: cmdb_status != "to-be-staged" is preferred even though the following could work but generates a deprecation warning: when: {{cmdb_status != "to-be-staged"}} --- library/utilities/fail | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/utilities/fail b/library/utilities/fail index 7023f357ca..23f5b83668 100644 --- a/library/utilities/fail +++ b/library/utilities/fail @@ -40,5 +40,5 @@ author: Dag Wieers EXAMPLES = ''' # Example playbook using fail and when together - fail: msg="The system may not be provisioned according to the CMDB status." - when: "{{ cmdb_status }} != 'to-be-staged'" + when: cmdb_status != "to-be-staged" ''' From 64652fa6abb7c975216dd941cbd0132fbfc9f40f Mon Sep 17 00:00:00 2001 From: Mikhail Sobolev Date: Sat, 21 Dec 2013 09:06:38 +0200 Subject: [PATCH 068/182] fix various documentation xrefs --- docsite/rst/developing_api.rst | 2 +- docsite/rst/developing_inventory.rst | 3 ++- docsite/rst/faq.rst | 2 +- docsite/rst/guide_aws.rst | 2 +- docsite/rst/playbooks_conditionals.rst | 4 ++-- docsite/rst/playbooks_intro.rst | 5 +++-- docsite/rst/playbooks_loops.rst | 2 +- docsite/rst/playbooks_variables.rst | 10 +++++----- 8 files changed, 16 insertions(+), 14 deletions(-) diff --git a/docsite/rst/developing_api.rst b/docsite/rst/developing_api.rst index 15f9755e36..56cae9d4c2 100644 --- a/docsite/rst/developing_api.rst +++ b/docsite/rst/developing_api.rst @@ -37,7 +37,7 @@ It's pretty simple:: The run method returns results per host, grouped by whether they could be contacted or not. Return types are module specific, as -expressed in the 'ansible-modules' documentation.:: +expressed in the :doc:`modules` documentation.:: { "dark" : { diff --git a/docsite/rst/developing_inventory.rst b/docsite/rst/developing_inventory.rst index 760ad80db9..4630b71114 100644 --- a/docsite/rst/developing_inventory.rst +++ b/docsite/rst/developing_inventory.rst @@ -2,8 +2,9 @@ Developing Dynamic Inventory Sources ==================================== .. contents:: Topics + :local: -As described in `intro_inventory_dynamic`, ansible can pull inventory information from dynamic sources, including cloud sources. +As described in :doc:`intro_dynamic_inventory`, ansible can pull inventory information from dynamic sources, including cloud sources. How do we write a new one? diff --git a/docsite/rst/faq.rst b/docsite/rst/faq.rst index 90ae12ac8b..167100f240 100644 --- a/docsite/rst/faq.rst +++ b/docsite/rst/faq.rst @@ -91,7 +91,7 @@ Where does the configuration file live and what can I configure in it? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -See `intro_configuration`. +See :doc:`intro_configuration`. .. _who_would_ever_want_to_disable_cowsay_but_ok_here_is_how: diff --git a/docsite/rst/guide_aws.rst b/docsite/rst/guide_aws.rst index 24ab9c5946..72e1cb7306 100644 --- a/docsite/rst/guide_aws.rst +++ b/docsite/rst/guide_aws.rst @@ -122,7 +122,7 @@ Pull Configuration For some the delay between refreshing host information and acting on that host information (i.e. running Ansible tasks against the hosts) may be too long. This may be the case in such scenarios where EC2 AutoScaling is being used to scale the number of instances as a result of a particular event. Such an event may require that hosts come online and are configured as soon as possible (even a 1 minute delay may be undesirable). Its possible to pre-bake machine images which contain the necessary ansible-pull script and components to pull and run a playbook via git. The machine images could be configured to run ansible-pull upon boot as part of the bootstrapping procedure. -More information on pull-mode playbooks can be found `here `_. +Read :ref:`ansible-pull` for more information on pull-mode playbooks. (Various developments around Ansible are also going to make this easier in the near future. Stay tuned!) diff --git a/docsite/rst/playbooks_conditionals.rst b/docsite/rst/playbooks_conditionals.rst index 75daad2e4c..d62dc87e84 100644 --- a/docsite/rst/playbooks_conditionals.rst +++ b/docsite/rst/playbooks_conditionals.rst @@ -21,7 +21,7 @@ Sometimes you will want to skip a particular step on a particular host. This co as simple as not installing a certain package if the operating system is a particular version, or it could be something like performing some cleanup steps if a filesystem is getting full. -This is easy to do in Ansible, with the `when` clause, which contains a Jinja2 expression (see `playbooks_variables`). +This is easy to do in Ansible, with the `when` clause, which contains a Jinja2 expression (see :doc:`playbooks_variables`). It's actually pretty simple:: tasks: @@ -125,7 +125,7 @@ Or with a role:: - { role: debian_stock_config, when: ansible_os_family == 'Debian' } You will note a lot of 'skipped' output by default in Ansible when using this approach on systems that don't match the criteria. -Read up on the 'group_by' module in the `modules` docs for a more streamlined way to accomplish the same thing. +Read up on the 'group_by' module in the :doc:`modules` docs for a more streamlined way to accomplish the same thing. Conditional Imports ``````````````````` diff --git a/docsite/rst/playbooks_intro.rst b/docsite/rst/playbooks_intro.rst index 402e0481ad..42bdfd9cb3 100644 --- a/docsite/rst/playbooks_intro.rst +++ b/docsite/rst/playbooks_intro.rst @@ -335,8 +335,7 @@ Let's run a playbook using a parallelism level of 10:: ansible-playbook playbook.yml -f 10 -.. _tips_and_tricks: - +.. _ansible-pull: Ansible-Pull ```````````` @@ -353,6 +352,8 @@ Run 'ansible-pull --help' for details. There's also a `clever playbook `_ available to using ansible in push mode to configure ansible-pull via a crontab! +.. _tips_and_tricks: + Tips and Tricks ``````````````` diff --git a/docsite/rst/playbooks_loops.rst b/docsite/rst/playbooks_loops.rst index 672b7f5107..7b3d474c1f 100644 --- a/docsite/rst/playbooks_loops.rst +++ b/docsite/rst/playbooks_loops.rst @@ -382,7 +382,7 @@ Subsequent loops over the registered variable to inspect the results may look li Writing Your Own Iterators `````````````````````````` -While you ordinarily shouldn't have to, should you wish to write your own ways to loop over arbitrary datastructures, you can read `developing_plugins` for some starter +While you ordinarily shouldn't have to, should you wish to write your own ways to loop over arbitrary datastructures, you can read :doc:`developing_plugins` for some starter information. Each of the above features are implemented as plugins in ansible, so there are many implementations to reference. .. seealso:: diff --git a/docsite/rst/playbooks_variables.rst b/docsite/rst/playbooks_variables.rst index a7f42aeb51..c32486ccc0 100644 --- a/docsite/rst/playbooks_variables.rst +++ b/docsite/rst/playbooks_variables.rst @@ -17,7 +17,7 @@ based on those variables. Variables in Ansible are how we deal with differences between systems. -Once understanding variables you'll also want to dig into `playbooks_conditionals` and `playbooks_loops`. +Once understanding variables you'll also want to dig into :doc:`playbooks_conditionals` and :doc:`playbooks_loops`. Useful things like the "group_by" module and the "when" conditional can also be used with variables, and to help manage differences between systems. @@ -49,7 +49,7 @@ a bit of a refresher. Often you'll want to set variables based on what groups a machine is in. For instance, maybe machines in Boston want to use 'boston.ntp.example.com' as an NTP server. -See the `intro_inventory` document for multiple ways on how to define variables in inventory. +See the :doc:`intro_inventory` document for multiple ways on how to define variables in inventory. .. _playbook_variables: @@ -232,7 +232,7 @@ Hey Wait, A YAML Gotcha ``````````````````````` YAML syntax requires that if you start a value with {{ foo }} you quote the whole line, since it wants to be -sure you aren't trying to start a YAML dictionary. This is covered on the `YAMLSyntax` page. +sure you aren't trying to start a YAML dictionary. This is covered on the :doc:`YAMLSyntax` page. This won't work:: @@ -572,7 +572,7 @@ Registered Variables Another major use of variables is running a command and using the result of that command to save the result into a variable. The value of a task being executed in ansible can be saved in a variable and used later. See some examples of this in the -`playbooks_conditionals` chapter. +:doc:`playbooks_conditionals` chapter. While it's mentioned elsewhere in that document too, here's a quick syntax example:: @@ -819,7 +819,7 @@ control you might want over values. First off, group variables are super powerful. Site wide defaults should be defined as a 'group_vars/all' setting. Group variables are generally placed alongside -your inventory file. They can also be returned by a dynamic inventory script (see `intro_dynamic_inventory`) or defined +your inventory file. They can also be returned by a dynamic inventory script (see :doc:`intro_dynamic_inventory`) or defined in things like AnsibleWorks AWX from the UI or API:: --- From 4658a9806686f715ebd881653d7097e5494bfde0 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 27 Dec 2013 14:50:19 -0500 Subject: [PATCH 069/182] Remove line about undocumented variables as they should be documented :) --- docsite/rst/playbooks_variables.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docsite/rst/playbooks_variables.rst b/docsite/rst/playbooks_variables.rst index c32486ccc0..0f84ad4ddf 100644 --- a/docsite/rst/playbooks_variables.rst +++ b/docsite/rst/playbooks_variables.rst @@ -649,8 +649,6 @@ A frequently used idiom is walking a group to find all IP addresses in that grou An example of this could include pointing a frontend proxy server to all of the app servers, setting up the correct firewall rules between servers, etc. -Just a few other 'magic' variables are available... There aren't many. - Additionally, *inventory_hostname* is the name of the hostname as configured in Ansible's inventory host file. This can be useful for when you don't want to rely on the discovered hostname `ansible_hostname` or for other mysterious reasons. If you have a long FQDN, *inventory_hostname_short* also contains the part up to the first From d4cd1fba8b6db455de2542862a180b9f0f396347 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 27 Dec 2013 15:01:27 -0500 Subject: [PATCH 070/182] add a note about raw/endraw --- library/files/template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/files/template b/library/files/template index 41dfba0804..ccc6bafadc 100644 --- a/library/files/template +++ b/library/files/template @@ -51,7 +51,7 @@ notes: - "Also, you can override jinja2 settings by adding a special header to template file. i.e. C(#jinja2:variable_start_string:'[%' , variable_end_string:'%]') - which changes the variable interpolation markers to [% var %] instead of {{ var }}." + which changes the variable interpolation markers to [% var %] instead of {{ var }}." This is the best way to prevent evaluation of things that look like, but should not be Jinja2. raw/endraw in Jinja2 will not work as you expect because templates in Ansible are recursively evaluated. requirements: [] author: Michael DeHaan From 132a7fa88eb53e1ad2efa5a8d64e874ed9eb182e Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 27 Dec 2013 15:19:19 -0500 Subject: [PATCH 071/182] Added note about tag groups to AWS guide. --- docsite/rst/guide_aws.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docsite/rst/guide_aws.rst b/docsite/rst/guide_aws.rst index 72e1cb7306..7bbbbee61d 100644 --- a/docsite/rst/guide_aws.rst +++ b/docsite/rst/guide_aws.rst @@ -115,6 +115,22 @@ You may wish to schedule a regular refresh of the inventory cache to accommodate Put this into a crontab as appropriate to make calls from your Ansible master server to the EC2 API endpoints and gather host information. The aim is to keep the view of hosts as up-to-date as possible, so schedule accordingly. Playbook calls could then also be scheduled to act on the refreshed hosts inventory after each refresh. This approach means that machine images can remain "raw", containing no payload and OS-only. Configuration of the workload is handled entirely by Ansible. +Tags +++++ + +There's a feature in the ec2 inventory script where hosts tagged with +certain keys and values automatically appear in certain groups. + +For instance, if a host is given the "class" tag with the value of "webserver", +it will be automatically discoverable via a dynamic group like so:: + + - hosts: tag_class_webserver + tasks: + - ping + +Using this philosophy can be a great way to manage groups dynamically, without +having to maintain seperate inventory. + .. _aws_pull: Pull Configuration From fdb8505a5e8890dfb0113934b49c29d3353031b0 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 27 Dec 2013 15:33:33 -0500 Subject: [PATCH 072/182] Add ansible-pull's -o to the manpage. --- docs/man/man1/ansible-pull.1.asciidoc.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/man/man1/ansible-pull.1.asciidoc.in b/docs/man/man1/ansible-pull.1.asciidoc.in index 2e39d53aa8..12a38f674b 100644 --- a/docs/man/man1/ansible-pull.1.asciidoc.in +++ b/docs/man/man1/ansible-pull.1.asciidoc.in @@ -83,6 +83,9 @@ Purge the checkout after the playbook is run. Module used to checkout playbook repository. Defaults to git. +*-o*, *--only-if-changed*:: + +Run the playbook only if the repository has changed AUTHOR ------ From 81fdd5d02a3c6d78746b6dbdc429277dac943b19 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 27 Dec 2013 15:42:11 -0500 Subject: [PATCH 073/182] Add a note about hosts being ok in multiple groups. --- docsite/rst/intro_inventory.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docsite/rst/intro_inventory.rst b/docsite/rst/intro_inventory.rst index 604603d516..0030162da0 100644 --- a/docsite/rst/intro_inventory.rst +++ b/docsite/rst/intro_inventory.rst @@ -35,6 +35,9 @@ The format for /etc/ansible/hosts is an INI format and looks like this:: The things in brackets are group names, which are used in classifying systems and deciding what systems you are controlling at what times and for what purpose. +It is ok to put systems in more than one group, for instance a server could be both a webserver and a dbserver. +If you do, note that variables will come from all of the groups they are a member of, and variable precedence is detailed in a later chapter. + If you have hosts that run on non-standard SSH ports you can put the port number after the hostname with a colon. Ports listed in your SSH config file won't be used, so it is important that you set them if things are not running on the default port:: From 12ecff71ba29a6197657536f7f8841cee2a56aa5 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 27 Dec 2013 15:52:53 -0500 Subject: [PATCH 074/182] Add a few notes about variables to the FAQ. --- docsite/rst/faq.rst | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/docsite/rst/faq.rst b/docsite/rst/faq.rst index 167100f240..4a252a76ec 100644 --- a/docsite/rst/faq.rst +++ b/docsite/rst/faq.rst @@ -138,6 +138,42 @@ Then you can use the facts inside your template, like this:: {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }} {% endfor %} +.. _programatic_access_to_a_variable:: + +How do I access a variable name programatically? +++++++++++++++++++++++++++++++++++++++++++++++++ + +An example may come up where we need to get the ipv4 address of an arbitrary interface, where the interface to be used may be supplied +via a role parameter or other input. Variable names can be built by adding strings together, like so:: + + {{ hostvars[inventory_hostname]['ansible_' + which_interface]['ipv4']['address'] }} + +The trick about going through hostvars is neccessary because it's a dictionary of the entire namespace of variables. 'inventory_hostname' +is a magic variable that indiciates the current host you are looping over in the host loop. + +.. _first_host_in_a_group:: + +How do I access a variable of the first host in a group? +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +What happens if we want the ip address of the first webserver in the webservers group? Well, we can do that too. Note that if we +are using dynamic inventory, which host is the 'first' may not be consistent, so you wouldn't want to do this unless your inventory +was static and predictable. (If you are using AWX, it will use database order, so this isn't a problem even if you are using cloud +based inventory scripts). + +Anyway, here's the trick:: + + {{ hostvars[groups['webservers'][0]]['ansible_eth0']['ipv4']['address'] }} + +Notice how we're pulling out the hostname of the first machine of the webservers group. If you are doing this in a template, you +could use the Jinja2 '#set' directive to simplify this, or in a playbook, you could also use set_fact: + + - set_fact: headnode={{ groups[['webservers'][0]] }} + + - debug: msg={{ hostvars[headnode].ansible_eth0.ipv4.address }} + +Notice how we interchanged the bracket syntax for dots -- that can be done anywhere. + .. _file_recursion: How do I copy files recursively onto a target host? From 80bb49c8b71657e4605b569f4cb02800a6ddff7f Mon Sep 17 00:00:00 2001 From: Christian Jensen Date: Sun, 29 Dec 2013 15:56:38 -0800 Subject: [PATCH 075/182] Fix a small typo --- docsite/rst/guide_rolling_upgrade.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docsite/rst/guide_rolling_upgrade.rst b/docsite/rst/guide_rolling_upgrade.rst index bdaf1939ab..6d723d2c3f 100644 --- a/docsite/rst/guide_rolling_upgrade.rst +++ b/docsite/rst/guide_rolling_upgrade.rst @@ -118,7 +118,7 @@ Here is lamp_haproxy's ``group_vars/all`` file. As you might expect, these varia This is a YAML file, and you can create lists and dictionaries for more complex variable structures. In this case, we are just setting two variables, one for the port for the web server, and one for the -NTP server that our machiens should use for time synchronization. +NTP server that our machines should use for time synchronization. Here's another group variables file. This is ``group_vars/dbservers`` which applies to the hosts in the ``dbservers`` group:: From 99616d0c80b0b11211a1ed8fba160af7f79e9409 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Mon, 30 Dec 2013 20:21:15 -0500 Subject: [PATCH 076/182] Pass scrub_data by default, see response from DO here: https://www.digitalocean.com/blog --- library/cloud/digital_ocean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/cloud/digital_ocean b/library/cloud/digital_ocean index 427956b5f7..bd975cf1b9 100644 --- a/library/cloud/digital_ocean +++ b/library/cloud/digital_ocean @@ -221,7 +221,7 @@ class Droplet(JsonfyMixIn): raise TimeoutError('Wait for droplet running timeout', self.id) def destroy(self): - return self.manager.destroy_droplet(self.id) + return self.manager.destroy_droplet(self.id, scrub_data=True) @classmethod def setup(cls, client_id, api_key): From 4f1f13b100dbdbbf786ef8b3db8cf96596cce115 Mon Sep 17 00:00:00 2001 From: Deon Bredenhann Date: Tue, 31 Dec 2013 13:40:21 +0200 Subject: [PATCH 077/182] Remove trailing space --- lib/ansible/runner/filter_plugins/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ansible/runner/filter_plugins/core.py b/lib/ansible/runner/filter_plugins/core.py index 495e17e2fa..f44089c750 100644 --- a/lib/ansible/runner/filter_plugins/core.py +++ b/lib/ansible/runner/filter_plugins/core.py @@ -58,7 +58,7 @@ def changed(*a, **kw): if not 'changed' in item: changed = False if ('results' in item # some modules return a 'results' key - and type(item['results']) == list + and type(item['results']) == list and type(item['results'][0]) == dict): for result in item['results']: changed = changed or result.get('changed', False) From c85aac8b173ec13b844aa35cd60f00015a7d1b42 Mon Sep 17 00:00:00 2001 From: Deon Bredenhann Date: Tue, 31 Dec 2013 13:41:40 +0200 Subject: [PATCH 078/182] Treat a False mandatory variable as defined. --- lib/ansible/runner/filter_plugins/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/ansible/runner/filter_plugins/core.py b/lib/ansible/runner/filter_plugins/core.py index f44089c750..1cd53bbaa3 100644 --- a/lib/ansible/runner/filter_plugins/core.py +++ b/lib/ansible/runner/filter_plugins/core.py @@ -76,9 +76,12 @@ def skipped(*a, **kw): def mandatory(a): ''' Make a variable mandatory ''' - if not a: + try: + a + except NameError: raise errors.AnsibleFilterError('Mandatory variable not defined.') - return a + else: + return a def bool(a): ''' return a bool for the arg ''' From b2cd4a62decc43f8960a11c54e02a88bb90c72d6 Mon Sep 17 00:00:00 2001 From: jeromew Date: Tue, 31 Dec 2013 14:29:46 -0500 Subject: [PATCH 079/182] fix issue #5372 on ssh_alt: accept -K option even for a user with NOPASSWD --- .../runner/connection_plugins/ssh_alt.py | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/ansible/runner/connection_plugins/ssh_alt.py b/lib/ansible/runner/connection_plugins/ssh_alt.py index 7b40cb7e4c..1f9fb04bd5 100644 --- a/lib/ansible/runner/connection_plugins/ssh_alt.py +++ b/lib/ansible/runner/connection_plugins/ssh_alt.py @@ -151,6 +151,9 @@ class Connection(object): ssh_cmd = self._password_cmd() ssh_cmd += ["ssh", "-C"] if not in_data: + # we can only use tty when we are not pipelining the modules. piping data into /usr/bin/python + # inside a tty automatically invokes the python interactive-mode but the modules are not + # compatible with the interactive-mode ("unexpected indent" mainly because of empty lines) ssh_cmd += ["-tt"] if utils.VERBOSITY > 3: ssh_cmd += ["-vvv"] @@ -200,31 +203,52 @@ class Connection(object): p = subprocess.Popen(ssh_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = p.stdin - self._send_password() if self.runner.sudo and sudoable and self.runner.sudo_pass: + # several cases are handled for sudo privileges with password + # * NOPASSWD (tty & no-tty): detect success_key on stdout + # * without NOPASSWD: + # * detect prompt on stdout (tty) + # * detect prompt on stderr (no-tty) fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK) + fcntl.fcntl(p.stderr, fcntl.F_SETFL, + fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK) sudo_output = '' - if in_data: - # no terminal => no prompt on output. process is waiting for sudo_pass - stdin.write(self.runner.sudo_pass + '\n') + sudo_errput = '' + while not sudo_output.endswith(prompt) and success_key not in sudo_output: - rfd, wfd, efd = select.select([p.stdout], [], + rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout], self.runner.timeout) + if p.stderr in rfd: + chunk = p.stderr.read() + if not chunk: + raise errors.AnsibleError('ssh connection closed waiting for sudo password prompt') + sudo_errput += chunk + incorrect_password = gettext.dgettext( + "sudo", "Sorry, try again.") + if sudo_errput.strip().endswith("%s%s" % (prompt, incorrect_password)): + raise errors.AnsibleError('Incorrect sudo password') + elif sudo_errput.endswith(prompt): + stdin.write(self.runner.sudo_pass + '\n') + if p.stdout in rfd: chunk = p.stdout.read() if not chunk: raise errors.AnsibleError('ssh connection closed waiting for sudo password prompt') sudo_output += chunk - else: + + if not rfd: + # timeout. wrap up process communication stdout = p.communicate() raise errors.AnsibleError('ssh connection error waiting for sudo password prompt') + if success_key not in sudo_output: stdin.write(self.runner.sudo_pass + '\n') fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK) + fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) & ~os.O_NONBLOCK) # We can't use p.communicate here because the ControlMaster may have stdout open as well stdout = '' stderr = '' From 763c0305c170e2bb091b17c9277a80013aaaed6e Mon Sep 17 00:00:00 2001 From: Rutger Spiertz Date: Thu, 2 Jan 2014 15:41:24 +0100 Subject: [PATCH 080/182] apt_repository: don't crash if default_file doesn't exist --- library/packaging/apt_repository | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/packaging/apt_repository b/library/packaging/apt_repository index 9965bc22a3..26b18ec4bc 100644 --- a/library/packaging/apt_repository +++ b/library/packaging/apt_repository @@ -3,6 +3,7 @@ # (c) 2012, Matt Wright # (c) 2013, Alexander Saltanov +# (c) 2014, Rutger Spiertz # # This file is part of Ansible # @@ -111,8 +112,9 @@ class SourcesList(object): self.files = {} # group sources by file self.default_file = apt_pkg.config.find_file('Dir::Etc::sourcelist') - # read sources.list - self.load(self.default_file) + # read sources.list if it exists + if os.path.isfile(self.default_file): + self.load(self.default_file) # read sources.list.d for file in glob.iglob('%s/*.list' % apt_pkg.config.find_dir('Dir::Etc::sourceparts')): From cb2214d6f849ff682c5fa49c22fd1385aef515e8 Mon Sep 17 00:00:00 2001 From: James Tanner Date: Thu, 2 Jan 2014 14:58:12 -0500 Subject: [PATCH 081/182] Fixes #4935 Synchronize module: set destination to an fqdn if connection is not local, and abide by ansible_remote_user --- .../runner/action_plugins/synchronize.py | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/ansible/runner/action_plugins/synchronize.py b/lib/ansible/runner/action_plugins/synchronize.py index ae36516aae..4c323dc32d 100644 --- a/lib/ansible/runner/action_plugins/synchronize.py +++ b/lib/ansible/runner/action_plugins/synchronize.py @@ -34,6 +34,13 @@ class ActionModule(object): else: return path + def _process_remote(self, host, path, user): + transport = self.runner.transport + if not host in ['127.0.0.1', 'localhost'] or transport != "local": + return '%s@%s:%s' % (user, host, path) + else: + return path + def setup(self, module_name, inject): ''' Always default to localhost as delegate if None defined ''' if inject.get('delegate_to') is None: @@ -76,7 +83,7 @@ class ActionModule(object): inv_port = inject.get('ansible_ssh_port', inject['inventory_hostname']) if inv_port != dest_port and inv_port != inject['inventory_hostname']: options['dest_port'] = inv_port - + # edge case: explicit delegate and dest_host are the same if dest_host == inject['delegate_to']: @@ -92,8 +99,15 @@ class ActionModule(object): private_key = os.path.expanduser(private_key) options['private_key'] = private_key - src = self._process_origin(src_host, src, user) - dest = self._process_origin(dest_host, dest, user) + # use the mode to define src and dest's url + if options.get('mode', 'push') == 'pull': + # src is a remote path: @, dest is a local path + src = self._process_remote(src_host, src, user) + dest = self._process_origin(dest_host, dest, user) + else: + # src is a local path, dest is a remote path: @ + src = self._process_origin(src_host, src, user) + dest = self._process_remote(dest_host, dest, user) options['src'] = src options['dest'] = dest From dacef1915ac1ec88400ac69b6c37156a81ac5602 Mon Sep 17 00:00:00 2001 From: Jim Kleckner Date: Thu, 2 Jan 2014 12:04:03 -0800 Subject: [PATCH 082/182] Add quotes to password argument for dump/import The password is passed on a command line for dump and import and needs quoting. Ideally, this would not be passed on a command line at all - any ideas? Or at least have a stronger form of quoting so that embedded single quotes will be escaped. --- library/database/mysql_db | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/database/mysql_db b/library/database/mysql_db index b6fbe5f83f..cf98701115 100644 --- a/library/database/mysql_db +++ b/library/database/mysql_db @@ -118,7 +118,7 @@ def db_delete(cursor, db): def db_dump(module, host, user, password, db_name, target, port, socket=None): cmd = module.get_bin_path('mysqldump', True) - cmd += " --quick --user=%s --password=%s" %(user, password) + cmd += " --quick --user=%s --password='%s'" %(user, password) if socket is not None: cmd += " --socket=%s" % socket else: @@ -135,7 +135,7 @@ def db_dump(module, host, user, password, db_name, target, port, socket=None): def db_import(module, host, user, password, db_name, target, port, socket=None): cmd = module.get_bin_path('mysql', True) - cmd += " --user=%s --password=%s" %(user, password) + cmd += " --user=%s --password='%s'" %(user, password) if socket is not None: cmd += " --socket=%s" % socket else: From 86719ce77fe01f8c55148be52de8ab1e68da64f2 Mon Sep 17 00:00:00 2001 From: James Tanner Date: Thu, 2 Jan 2014 16:17:24 -0500 Subject: [PATCH 083/182] Fixes #5040 setup module: do not add primary interface info to the secondary interface data --- library/system/setup | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/library/system/setup b/library/system/setup index 816039469c..542280624e 100755 --- a/library/system/setup +++ b/library/system/setup @@ -1540,8 +1540,7 @@ class LinuxNetwork(Network): iface = words[-1] if iface != device: interfaces[iface] = {} - interfaces[iface].update(interfaces[device]) - if "ipv4_secondaries" not in interfaces[iface]: + if not secondary and "ipv4_secondaries" not in interfaces[iface]: interfaces[iface]["ipv4_secondaries"] = [] if not secondary or "ipv4" not in interfaces[iface]: interfaces[iface]['ipv4'] = {'address': address, @@ -1553,6 +1552,15 @@ class LinuxNetwork(Network): 'netmask': netmask, 'network': network, }) + + # add this secondary IP to the main device + if secondary: + interfaces[device]["ipv4_secondaries"].append({ + 'address': address, + 'netmask': netmask, + 'network': network, + }) + # If this is the default address, update default_ipv4 if 'address' in default_ipv4 and default_ipv4['address'] == address: default_ipv4['netmask'] = netmask From 2f811c40d745a3aca3ee6bcc389f9be62ec19193 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 2 Jan 2014 17:36:52 -0500 Subject: [PATCH 084/182] Basic docsite formatting fixups --- docs/man/man1/ansible-pull.1 | 32 +++++++++++++++++++++++------ docsite/rst/playbooks_variables.rst | 2 +- hacking/templates/rst.j2 | 2 +- library/files/template | 4 ++-- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/docs/man/man1/ansible-pull.1 b/docs/man/man1/ansible-pull.1 index 7644751d97..6d75a3cc5e 100644 --- a/docs/man/man1/ansible-pull.1 +++ b/docs/man/man1/ansible-pull.1 @@ -1,13 +1,22 @@ '\" t .\" Title: ansible -.\" Author: [see the "AUTHOR" section] -.\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" Date: 11/27/2013 +.\" Author: :doctype:manpage +.\" Generator: DocBook XSL Stylesheets v1.76.1 +.\" Date: 01/02/2014 .\" Manual: System administration commands -.\" Source: Ansible 1.4.1 +.\" Source: Ansible 1.5 .\" Language: English .\" -.TH "ANSIBLE" "1" "11/27/2013" "Ansible 1\&.4\&.1" "System administration commands" +.TH "ANSIBLE" "1" "01/02/2014" "Ansible 1\&.5" "System administration commands" +.\" ----------------------------------------------------------------- +.\" * Define some portability stuff +.\" ----------------------------------------------------------------- +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.\" http://bugs.debian.org/507673 +.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html +.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- @@ -25,7 +34,7 @@ ansible-pull \- set up a remote copy of ansible on each managed node ansible \-d DEST \-U URL [options] [ ] .SH "DESCRIPTION" .sp -\fBAnsible\fR is an extra\-simple tool/framework/API for doing \'remote things\' over SSH\&. +\fBAnsible\fR is an extra\-simple tool/framework/API for doing \*(Aqremote things\*(Aq over SSH\&. .sp Use ansible\-pull to set up a remote copy of ansible on each managed node, each set to run via cron and update playbook source via a source repository\&. This inverts the default \fBpush\fR architecture of ansible into a \fBpull\fR architecture, which has near\-limitless scaling potential\&. .sp @@ -77,6 +86,11 @@ Purge the checkout after the playbook is run\&. .RS 4 Module used to checkout playbook repository\&. Defaults to git\&. .RE +.PP +\fB\-o\fR, \fB\-\-only\-if\-changed\fR +.RS 4 +Run the playbook only if the repository has changed +.RE .SH "AUTHOR" .sp Ansible was originally written by Michael DeHaan\&. See the AUTHORS file for a complete list of contributors\&. @@ -90,3 +104,9 @@ Ansible is released under the terms of the GPLv3 License\&. \fBansible\fR(1), \fBansible\-playbook\fR(1), \fBansible\-doc\fR(1) .sp Extensive documentation as well as IRC and mailing list info is available on the ansible home page: https://ansible\&.github\&.com/ +.SH "AUTHOR" +.PP +\fB:doctype:manpage\fR +.RS 4 +Author. +.RE diff --git a/docsite/rst/playbooks_variables.rst b/docsite/rst/playbooks_variables.rst index 0f84ad4ddf..693cc0b5a0 100644 --- a/docsite/rst/playbooks_variables.rst +++ b/docsite/rst/playbooks_variables.rst @@ -71,7 +71,7 @@ Variables defined from included files and roles It turns out we've already talked about variables in another place too. -As described in :doc:`intro_roles`, variables can also be included in the playbook via include files, which may or may +As described in :doc:`playbooks_roles`, variables can also be included in the playbook via include files, which may or may not be part of an "Ansible Role". Usage of roles is preferred as it provides a nice organizational system. .. _about_jinja2: diff --git a/hacking/templates/rst.j2 b/hacking/templates/rst.j2 index 2fa4348846..5c39314476 100644 --- a/hacking/templates/rst.j2 +++ b/hacking/templates/rst.j2 @@ -95,7 +95,7 @@ Examples :: -@{ plainexamples | escape | indent(4, True) }@ +@{ plainexamples | indent(4, True) }@ {% endif %} {% endif %} diff --git a/library/files/template b/library/files/template index ccc6bafadc..5f64b6d9e6 100644 --- a/library/files/template +++ b/library/files/template @@ -47,11 +47,11 @@ options: - all arguments accepted by the M(file) module also work here required: false notes: - - Since Ansible version 0.9, templates are loaded with C(trim_blocks=True). + - "Since Ansible version 0.9, templates are loaded with C(trim_blocks=True)." - "Also, you can override jinja2 settings by adding a special header to template file. i.e. C(#jinja2:variable_start_string:'[%' , variable_end_string:'%]') - which changes the variable interpolation markers to [% var %] instead of {{ var }}." This is the best way to prevent evaluation of things that look like, but should not be Jinja2. raw/endraw in Jinja2 will not work as you expect because templates in Ansible are recursively evaluated. + which changes the variable interpolation markers to [% var %] instead of {{ var }}. This is the best way to prevent evaluation of things that look like, but should not be Jinja2. raw/endraw in Jinja2 will not work as you expect because templates in Ansible are recursively evaluated." requirements: [] author: Michael DeHaan From 1251538e967de7c89f4bfcf9806b5bf1842a899a Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 2 Jan 2014 17:37:48 -0500 Subject: [PATCH 085/182] Fix syntax of docsite anchors. --- docsite/rst/faq.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docsite/rst/faq.rst b/docsite/rst/faq.rst index 4a252a76ec..2634195a87 100644 --- a/docsite/rst/faq.rst +++ b/docsite/rst/faq.rst @@ -138,7 +138,7 @@ Then you can use the facts inside your template, like this:: {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }} {% endfor %} -.. _programatic_access_to_a_variable:: +.. _programatic_access_to_a_variable: How do I access a variable name programatically? ++++++++++++++++++++++++++++++++++++++++++++++++ @@ -151,7 +151,7 @@ via a role parameter or other input. Variable names can be built by adding stri The trick about going through hostvars is neccessary because it's a dictionary of the entire namespace of variables. 'inventory_hostname' is a magic variable that indiciates the current host you are looping over in the host loop. -.. _first_host_in_a_group:: +.. _first_host_in_a_group: How do I access a variable of the first host in a group? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ From c4a8a6d3c2b13276f09ff85a859e5dddd816e916 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 2 Jan 2014 17:42:18 -0500 Subject: [PATCH 086/182] Add an "all" category so all modules show up uncategorized there. --- hacking/module_formatter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hacking/module_formatter.py b/hacking/module_formatter.py index 237486db24..2202ec038c 100755 --- a/hacking/module_formatter.py +++ b/hacking/module_formatter.py @@ -117,7 +117,7 @@ def write_data(text, options, outputname, module): def list_modules(module_dir): ''' returns a hash of categories, each category being a hash of module names to file paths ''' - categories = {} + categories = dict(all=dict()) files = glob.glob("%s/*" % module_dir) for d in files: if os.path.isdir(d): @@ -129,6 +129,7 @@ def list_modules(module_dir): if not category in categories: categories[category] = {} categories[category][module] = f + categories['all'][module] = f return categories ##################################################################################### From de2ffc1276483ae5ae0e1d11a0b3fe2cd1418b31 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Thu, 2 Jan 2014 17:55:59 -0500 Subject: [PATCH 087/182] Don't need to document these guys. --- lib/ansible/utils/module_docs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ansible/utils/module_docs.py b/lib/ansible/utils/module_docs.py index afa4c6536e..3a5d078296 100644 --- a/lib/ansible/utils/module_docs.py +++ b/lib/ansible/utils/module_docs.py @@ -25,7 +25,7 @@ import traceback # modules that are ok that they do not have documentation strings BLACKLIST_MODULES = [ - 'async_wrapper', + 'async_wrapper', 'accelerate', 'async_status' ] def get_docstring(filename, verbose=False): From 318116f29152fdb1b4bffc463ed6dca4c37d0ad8 Mon Sep 17 00:00:00 2001 From: John Jarvis Date: Thu, 2 Jan 2014 20:16:19 -0500 Subject: [PATCH 088/182] bad closing quote in footer --- docsite/_themes/srtd/footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docsite/_themes/srtd/footer.html b/docsite/_themes/srtd/footer.html index 0e53d0eb21..ea0efc35ad 100644 --- a/docsite/_themes/srtd/footer.html +++ b/docsite/_themes/srtd/footer.html @@ -20,6 +20,6 @@ {%- endif %}

    -Ansible docs are generated from Sphinx using a theme provided by Read the Docs. {% if pagename.endswith("_module") %}. Module documentation is not edited directly, but is generated from the source code for the modules. To submit an update to module docs, edit the 'DOCUMENTATION' metadata in the module source tree. {% endif %} +Ansible docs are generated from GitHub sources using Sphinx using a theme provided by Read the Docs. {% if pagename.endswith("_module") %}. Module documentation is not edited directly, but is generated from the source code for the modules. To submit an update to module docs, edit the 'DOCUMENTATION' metadata in the module source tree. {% endif %} From 279098bd034701acfb7ff5d6f03061b90780e10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dag=20H=C3=B8idahl?= Date: Fri, 3 Jan 2014 15:15:20 +0100 Subject: [PATCH 089/182] Use "brew list " instead of grepping the output of "brew list". Some brew packages are not listed with their package name, e.g. libjpeg. --- library/packaging/homebrew | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/packaging/homebrew b/library/packaging/homebrew index 8801a4e6c4..ab1362acf1 100644 --- a/library/packaging/homebrew +++ b/library/packaging/homebrew @@ -71,7 +71,7 @@ def query_package(module, brew_path, name, state="present"): """ Returns whether a package is installed or not. """ if state == "present": - rc, out, err = module.run_command("%s list -m1 | grep -q '^%s$'" % (brew_path, name)) + rc, out, err = module.run_command("%s list %s" % (brew_path, name)) if rc == 0: return True From f2150b101e145f0d1934bfc543842f03ad25c5e2 Mon Sep 17 00:00:00 2001 From: rgbj Date: Fri, 3 Jan 2014 15:41:12 +0100 Subject: [PATCH 090/182] On OpenBSD, make user module status 'changed' only if necessary when using 'login_class' parameter --- library/system/user | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/library/system/user b/library/system/user index 48bcf75171..797c25e87a 100644 --- a/library/system/user +++ b/library/system/user @@ -901,8 +901,21 @@ class OpenBSDUser(User): cmd.append(self.shell) if self.login_class is not None: - cmd.append('-L') - cmd.append(self.login_class) + # find current login class + user_login_class = None + userinfo_cmd = [self.module.get_bin_path('userinfo', True), self.name] + (rc, out, err) = self.execute_command(userinfo_cmd) + + for line in out.splitlines(): + tokens = line.split() + + if tokens[0] == 'class' and len(tokens) == 2: + user_login_class = tokens[1] + + # act only if login_class change + if self.login_class != user_login_class: + cmd.append('-L') + cmd.append(self.login_class) if self.update_password == 'always' and self.password is not None and info[1] != self.password: cmd.append('-p') From fc473b3246435ed88ef979a56d904cf6b2afcd20 Mon Sep 17 00:00:00 2001 From: James Tanner Date: Fri, 3 Jan 2014 09:46:16 -0500 Subject: [PATCH 091/182] Fixes #5146 Handle missing stdin when running under celery with rabbitmq or redis --- lib/ansible/runner/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 3318bcba2b..92b25788d0 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -405,8 +405,13 @@ class Runner(object): return flags try: - if not new_stdin: - self._new_stdin = os.fdopen(os.dup(sys.stdin.fileno())) + fileno = sys.stdin.fileno() + except ValueError: + fileno = None + + try: + if not new_stdin and fileno is not None: + self._new_stdin = os.fdopen(os.dup(fileno)) else: self._new_stdin = new_stdin From 0882eefdff933391be40644890d453927685849f Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 3 Jan 2014 11:06:32 -0500 Subject: [PATCH 092/182] Merge conflict --- docs/man/man1/ansible-pull.1 | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/man/man1/ansible-pull.1 b/docs/man/man1/ansible-pull.1 index 6d75a3cc5e..fb727631eb 100644 --- a/docs/man/man1/ansible-pull.1 +++ b/docs/man/man1/ansible-pull.1 @@ -7,16 +7,7 @@ .\" Source: Ansible 1.5 .\" Language: English .\" -.TH "ANSIBLE" "1" "01/02/2014" "Ansible 1\&.5" "System administration commands" -.\" ----------------------------------------------------------------- -.\" * Define some portability stuff -.\" ----------------------------------------------------------------- -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.\" http://bugs.debian.org/507673 -.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html -.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.ie \n(.g .ds Aq \(aq -.el .ds Aq ' +.TH "ANSIBLE" "1" "01/03/2014" "Ansible 1\&.5" "System administration commands" .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- From 1270e2350c439a50b6a6d9500d0dc1e47e374886 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 3 Jan 2014 11:00:11 -0500 Subject: [PATCH 093/182] Remove man3 stuff since this is available via ansible-doc, rpm target should build manpages. --- Makefile | 2 +- packaging/rpm/ansible.spec | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 990d9cdc0b..20f54855ab 100644 --- a/Makefile +++ b/Makefile @@ -124,7 +124,7 @@ install: sdist: clean docs $(PYTHON) setup.py sdist -t MANIFEST.in -rpmcommon: sdist +rpmcommon: $(MANPAGES) sdist @mkdir -p rpm-build @cp dist/*.gz rpm-build/ @sed -e 's#^Version:.*#Version: $(VERSION)#' -e 's#^Release:.*#Release: $(RPMRELEASE)%{?dist}#' $(RPMSPEC) >rpm-build/$(NAME).spec diff --git a/packaging/rpm/ansible.spec b/packaging/rpm/ansible.spec index 6088bd7ba6..ce929e4cdf 100644 --- a/packaging/rpm/ansible.spec +++ b/packaging/rpm/ansible.spec @@ -79,9 +79,8 @@ are transferred to managed machines automatically. mkdir -p %{buildroot}/etc/ansible/ cp examples/hosts %{buildroot}/etc/ansible/ cp examples/ansible.cfg %{buildroot}/etc/ansible/ -mkdir -p %{buildroot}/%{_mandir}/{man1,man3}/ +mkdir -p %{buildroot}/%{_mandir}/man1/ cp -v docs/man/man1/*.1 %{buildroot}/%{_mandir}/man1/ -cp -v docs/man/man3/*.3 %{buildroot}/%{_mandir}/man3/ mkdir -p %{buildroot}/%{_datadir}/ansible cp -rv library/* %{buildroot}/%{_datadir}/ansible/ @@ -98,7 +97,6 @@ rm -rf %{buildroot} %config(noreplace) %{_sysconfdir}/ansible %doc README.md PKG-INFO COPYING %doc %{_mandir}/man1/ansible* -%doc %{_mandir}/man3/ansible.* %doc examples/playbooks From 21fdb2bbc7f68999d512787c5d9130e2b5d55ae6 Mon Sep 17 00:00:00 2001 From: James Tanner Date: Fri, 3 Jan 2014 11:18:20 -0500 Subject: [PATCH 094/182] Fixes #5200 Handle template contents with unicode strings better --- lib/ansible/utils/template.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/ansible/utils/template.py b/lib/ansible/utils/template.py index 9180f47326..2491035c59 100644 --- a/lib/ansible/utils/template.py +++ b/lib/ansible/utils/template.py @@ -497,7 +497,14 @@ def template_from_file(basedir, path, vars): if data.endswith('\n') and not res.endswith('\n'): res = res + '\n' - return template(basedir, res, vars) + + if isinstance(res, unicode): + # do not try to re-template a unicode string + result = res + else: + result = template(basedir, res, vars) + + return result def template_from_string(basedir, data, vars, fail_on_undefined=False): ''' run a string through the (Jinja2) templating engine ''' From 5d50bcf6dc47da9c57c5cef76f825516c04f7186 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 3 Jan 2014 13:18:47 -0500 Subject: [PATCH 095/182] Correct typo in variable in docs. --- docsite/rst/playbooks_variables.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docsite/rst/playbooks_variables.rst b/docsite/rst/playbooks_variables.rst index 693cc0b5a0..e76ff64a9e 100644 --- a/docsite/rst/playbooks_variables.rst +++ b/docsite/rst/playbooks_variables.rst @@ -835,7 +835,7 @@ If for some crazy reason we wanted to tell just a specific host to use a specifi --- # file: /etc/ansible/host_vars/xyz.boston.example.com - ntp-server: override.example.com + ntp_server: override.example.com So that covers inventory and what you would normally set there. It's a great place for things that deal with geography or behavior. Since groups are frequently the entity that maps roles onto hosts, it is sometimes a shortcut to set variables on the group instead of defining them on a role. You could go either way. From 28b9fd4e30c77ffbeefd6cfa0499854750fd43a8 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 3 Jan 2014 13:46:31 -0500 Subject: [PATCH 096/182] We have had only_if, when_foo, etc, deprecated for a while and said they would be removed in 1.5. Now they are, with friendly error messages still. Users of these features should use "when:" as documented at docs.ansible.com. Similarly, include + with_items has been removed. The solution is to loop inside the task files, see with_nested / with_together, etc. --- lib/ansible/playbook/__init__.py | 2 +- lib/ansible/playbook/play.py | 9 ++------- lib/ansible/playbook/task.py | 4 ++-- lib/ansible/utils/__init__.py | 17 +++++++++++++---- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index 36788f2409..e2bd662618 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -161,7 +161,7 @@ class PlayBook(object): play_basedirs = [] if type(playbook_data) != list: - raise errors.AnsibleError("parse error: playbooks must be formatted as a YAML list") + raise errors.AnsibleError("parse error: playbooks must be formatted as a YAML list, got %s" % type(playbook_data)) basedir = os.path.dirname(path) or '.' utils.plugins.push_basedir(basedir) diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index a2fdc643f2..cb8665dcf5 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -484,14 +484,9 @@ class Play(object): include_vars = {} for k in x: if k.startswith("with_"): - utils.deprecated("include + with_items is an unsupported feature and has been undocumented for many releases because of this", "1.5") - plugin_name = k[5:] - if plugin_name not in utils.plugins.lookup_loader: - raise errors.AnsibleError("cannot find lookup plugin named %s for usage in with_%s" % (plugin_name, plugin_name)) - terms = template(self.basedir, x[k], task_vars) - items = utils.plugins.lookup_loader.get(plugin_name, basedir=self.basedir, runner=None).run(terms, inject=task_vars) + utils.deprecated("include + with_items is a removed deprecated feature", "1.5", removed=True) elif k.startswith("when_"): - included_additional_conditions.insert(0, utils.compile_when_to_only_if("%s %s" % (k[5:], x[k]))) + utils.deprecated("\"when_:\" is a removed deprecated feature, use the simplified 'when:' conditional directly", None, removed=True) elif k == 'when': if type(x[k]) is str: included_additional_conditions.insert(0, utils.compile_when_to_only_if("jinja2_compare %s" % x[k])) diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 849a1b26ab..be26c8f699 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -98,7 +98,7 @@ class Task(object): utils.warning("It is unneccessary to use '{{' in conditionals, leave variables in loop expressions bare.") ds[x] = "jinja2_compare %s" % (ds[x]) elif x.startswith("when_"): - utils.deprecated("The 'when_' conditional is a deprecated syntax as of 1.2. Switch to using the regular unified 'when' statements as described in ansibleworks.com/docs/.","1.5") + utils.deprecated("The 'when_' conditional has been removed. Switch to using the regular unified 'when' statements as described in ansibleworks.com/docs/.","1.5", removed=True) if 'when' in ds: raise errors.AnsibleError("multiple when_* statements specified in task %s" % (ds.get('name', ds['action']))) @@ -191,7 +191,7 @@ class Task(object): self.only_if = ds.get('only_if', 'True') if self.only_if != 'True': - utils.deprecated("only_if is a very old feature and has been obsolete since 0.9, please switch to the 'when' conditional as described at http://ansibleworks.com/docs","1.5") + utils.deprecated("only_if is a very old feature and has been obsolete since 0.9, please switch to the 'when' conditional as described at http://ansibleworks.com/docs","1.5",removed=True) self.when = ds.get('when', None) self.changed_when = ds.get('changed_when', None) diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index 126e86869a..9a57a2ef52 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -979,12 +979,21 @@ def listify_lookup_plugin_terms(terms, basedir, inject): return terms -def deprecated(msg, version): +def deprecated(msg, version, removed=False): ''' used to print out a deprecation message.''' - if not C.DEPRECATION_WARNINGS: + + if not removed and not C.DEPRECATION_WARNINGS: return - new_msg = "\n[DEPRECATION WARNING]: %s. This feature will be removed in version %s." % (msg, version) - new_msg = new_msg + " Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.\n\n" + + if not removed: + if version: + new_msg = "\n[DEPRECATION WARNING]: %s. This feature will be removed in version %s." % (msg, version) + else: + new_msg = "\n[DEPRECATION WARNING]: %s. This feature will be removed in a future release." % (msg) + new_msg = new_msg + " Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.\n\n" + else: + raise errors.AnsibleError("[DEPRECATED]: %s. Please update your playbooks." % msg) + wrapped = textwrap.wrap(new_msg, 79) new_msg = "\n".join(wrapped) + "\n" From 191be7b951eb8c34b73b367334e0cb4343af0779 Mon Sep 17 00:00:00 2001 From: James Tanner Date: Fri, 3 Jan 2014 15:29:18 -0500 Subject: [PATCH 097/182] Resolves issues with newer versions of pip not having a --use-mirrors paramater for the install command --- library/packaging/pip | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/library/packaging/pip b/library/packaging/pip index 13cf162fe1..414dfcc093 100644 --- a/library/packaging/pip +++ b/library/packaging/pip @@ -275,6 +275,7 @@ def main(): pip = _get_pip(module, env, module.params['executable']) cmd = '%s %s' % (pip, state_map[state]) + cmd_opts = None # If there's a virtualenv we want things we install to be able to use other # installations that exist as binaries within this virtualenv. Example: we @@ -319,7 +320,11 @@ def main(): is_local_path = True # for tarball or vcs source, applying --use-mirrors doesn't really make sense is_package = is_vcs or is_tar or is_local_path # just a shortcut for bool - if not is_package and state != 'absent' and use_mirrors: + + if cmd_opts is None: + cmd_opts = _get_cmd_options(module, cmd) + + if not is_package and state != 'absent' and use_mirrors and '--use-mirrors' in cmd_opts: cmd += ' --use-mirrors' cmd += ' %s' % _get_full_name(name, version) elif requirements: From 8e5b7d3095aa0064f551713749ae6296082a5794 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 3 Jan 2014 19:13:21 -0500 Subject: [PATCH 098/182] Remove code underlying when_* and only_if, which are deprecated features slated for removal in the 1.5 release. --- CHANGELOG.md | 13 +++ docsite/rst/glossary.rst | 9 +- lib/ansible/playbook/__init__.py | 2 +- lib/ansible/playbook/play.py | 10 +- lib/ansible/playbook/task.py | 30 ++---- lib/ansible/runner/__init__.py | 2 +- lib/ansible/utils/__init__.py | 152 ++++++------------------------- test/TestUtils.py | 60 ++++++------ 8 files changed, 87 insertions(+), 191 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a73f7e3253..4069b403e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ Ansible Changes By Release ## 1.5 "Love Walks In" - Release pending! +Major features/changes: + +* when_foo which was previously deprecated is now removed, use "when:" instead. Code generates appropriate error suggestion. +* include + with_items which was previously deprecated is now removed, ditto. Use with_nested / with_together, etc. +* only_if, which is much older than when_foo and was deprecated, is similarly removed. +* ssh_alt connection plugin, much more performant than standard -c ssh, in tree, soon to replace ssh.py (in this release) + +New modules: + +* Details pending + +Misc: + * no_reboot is now defaulted to "no" in the ec2_ami module to ensure filesystem consistency in the resulting AMI. ## 1.4.3 "Could This Be Magic" - December 20, 2013 diff --git a/docsite/rst/glossary.rst b/docsite/rst/glossary.rst index 8c10086b15..352e808481 100644 --- a/docsite/rst/glossary.rst +++ b/docsite/rst/glossary.rst @@ -56,8 +56,8 @@ Conditionals ++++++++++++ A conditional is an expression that evaluates to true or false that decides whether a given task will be executed on a given -machine or not. Ansible's conditionals include 'when_boolean', -'when_string', and 'when_integer'. These are discussed in the playbook documentation. +machine or not. Ansible's conditionals are powered by the 'when' statement, and are +discussed in the playbook documentation. Diff Mode +++++++++ @@ -220,11 +220,6 @@ JSON Ansible uses JSON for return data from remote modules. This allows modules to be written in any language, not just Python. -only_if -+++++++ - -A deprecated form of the "when:" statement. It should no longer be used. - Library +++++++ diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index e2bd662618..189fd63d99 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -310,7 +310,7 @@ class PlayBook(object): remote_port=task.play.remote_port, module_vars=task.module_vars, default_vars=task.default_vars, private_key_file=self.private_key_file, setup_cache=self.SETUP_CACHE, basedir=task.play.basedir, - conditional=task.only_if, callbacks=self.runner_callbacks, + conditional=task.when, callbacks=self.runner_callbacks, sudo=task.sudo, sudo_user=task.sudo_user, transport=task.transport, sudo_pass=task.sudo_pass, is_playbook=True, check=self.check, diff=self.diff, environment=task.environment, complex_args=task.args, diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index cb8665dcf5..85a9378cf1 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -489,11 +489,11 @@ class Play(object): utils.deprecated("\"when_:\" is a removed deprecated feature, use the simplified 'when:' conditional directly", None, removed=True) elif k == 'when': if type(x[k]) is str: - included_additional_conditions.insert(0, utils.compile_when_to_only_if("jinja2_compare %s" % x[k])) + included_additional_conditions.insert(0, x[k]) elif type(x[k]) is list: for i in x[k]: - included_additional_conditions.insert(0, utils.compile_when_to_only_if("jinja2_compare %s" % i)) - elif k in ("include", "vars", "default_vars", "only_if", "sudo", "sudo_user", "role_name"): + included_additional_conditions.insert(0, i) + elif k in ("include", "vars", "default_vars", "sudo", "sudo_user", "role_name"): continue else: include_vars[k] = x[k] @@ -511,8 +511,8 @@ class Play(object): if 'vars' in x: task_vars = utils.combine_vars(task_vars, x['vars']) - if 'only_if' in x: - included_additional_conditions.append(x['only_if']) + if 'when' in x: + included_additional_conditions.append(x['when']) new_role = None if 'role_name' in x: diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index be26c8f699..59f5c6fe1f 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -24,7 +24,7 @@ import sys class Task(object): __slots__ = [ - 'name', 'meta', 'action', 'only_if', 'when', 'async_seconds', 'async_poll_interval', + 'name', 'meta', 'action', 'when', 'async_seconds', 'async_poll_interval', 'notify', 'module_name', 'module_args', 'module_vars', 'default_vars', 'play', 'notified_by', 'tags', 'register', 'role_name', 'delegate_to', 'first_available_file', 'ignore_errors', @@ -35,7 +35,7 @@ class Task(object): # to prevent typos and such VALID_KEYS = [ - 'name', 'meta', 'action', 'only_if', 'async', 'poll', 'notify', + 'name', 'meta', 'action', 'when', 'async', 'poll', 'notify', 'first_available_file', 'include', 'tags', 'register', 'ignore_errors', 'delegate_to', 'local_action', 'transport', 'remote_user', 'sudo', 'sudo_user', 'sudo_pass', 'when', 'connection', 'environment', 'args', @@ -96,7 +96,6 @@ class Task(object): elif x in [ 'changed_when', 'failed_when', 'when']: if isinstance(ds[x], basestring) and ds[x].lstrip().startswith("{{"): utils.warning("It is unneccessary to use '{{' in conditionals, leave variables in loop expressions bare.") - ds[x] = "jinja2_compare %s" % (ds[x]) elif x.startswith("when_"): utils.deprecated("The 'when_' conditional has been removed. Switch to using the regular unified 'when' statements as described in ansibleworks.com/docs/.","1.5", removed=True) @@ -128,8 +127,8 @@ class Task(object): self.module_vars['delay'] = ds.get('delay', 5) self.module_vars['retries'] = ds.get('retries', 3) self.module_vars['register'] = ds.get('register', None) - self.until = "jinja2_compare %s" % (ds.get('until')) - self.module_vars['until'] = utils.compile_when_to_only_if(self.until) + self.until = ds.get('until') + self.module_vars['until'] = self.until # rather than simple key=value args on the options line, these represent structured data and the values # can be hashes and lists, not just scalars @@ -188,22 +187,10 @@ class Task(object): self.name = self.action # load various attributes - self.only_if = ds.get('only_if', 'True') - - if self.only_if != 'True': - utils.deprecated("only_if is a very old feature and has been obsolete since 0.9, please switch to the 'when' conditional as described at http://ansibleworks.com/docs","1.5",removed=True) - self.when = ds.get('when', None) self.changed_when = ds.get('changed_when', None) - - if self.changed_when is not None: - self.changed_when = utils.compile_when_to_only_if(self.changed_when) - self.failed_when = ds.get('failed_when', None) - if self.failed_when is not None: - self.failed_when = utils.compile_when_to_only_if(self.failed_when) - self.async_seconds = int(ds.get('async', 0)) # not async by default self.async_poll_interval = int(ds.get('poll', 10)) # default poll = 10 seconds self.notify = ds.get('notify', []) @@ -276,12 +263,7 @@ class Task(object): self.tags.extend(apply_tags) self.tags.extend(import_tags) - if self.when is not None: - if self.only_if != 'True': - raise errors.AnsibleError('when obsoletes only_if, only use one or the other') - self.only_if = utils.compile_when_to_only_if(self.when) - if additional_conditions: new_conditions = additional_conditions - new_conditions.append(self.only_if) - self.only_if = new_conditions + new_conditions.append(self.when) + self.when = new_conditions diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 92b25788d0..011a31512b 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -1067,6 +1067,6 @@ class Runner(object): if self.always_run is None: self.always_run = self.module_vars.get('always_run', False) self.always_run = check_conditional( - self.always_run, self.basedir, inject, fail_on_undefined=True, jinja2=True) + self.always_run, self.basedir, inject, fail_on_undefined=True) return (self.check and not self.always_run) diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index 9a57a2ef52..0fb141946b 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -163,58 +163,44 @@ def is_changed(result): return (result.get('changed', False) in [ True, 'True', 'true']) -def check_conditional(conditional, basedir, inject, fail_on_undefined=False, jinja2=False): +def check_conditional(conditional, basedir, inject, fail_on_undefined=False): if isinstance(conditional, list): for x in conditional: - if not check_conditional(x, basedir, inject, fail_on_undefined=fail_on_undefined, jinja2=jinja2): + if not check_conditional(x, basedir, inject, fail_on_undefined=fail_on_undefined): return False return True - if jinja2: - conditional = "jinja2_compare %s" % conditional - - if conditional.startswith("jinja2_compare"): - conditional = conditional.replace("jinja2_compare ","") - # allow variable names - if conditional in inject and str(inject[conditional]).find('-') == -1: - conditional = inject[conditional] - conditional = template.template(basedir, conditional, inject, fail_on_undefined=fail_on_undefined) - original = str(conditional).replace("jinja2_compare ","") - # a Jinja2 evaluation that results in something Python can eval! - presented = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional - conditional = template.template(basedir, presented, inject) - val = conditional.strip() - if val == presented: - # the templating failed, meaning most likely a - # variable was undefined. If we happened to be - # looking for an undefined variable, return True, - # otherwise fail - if conditional.find("is undefined") != -1: - return True - elif conditional.find("is defined") != -1: - return False - else: - raise errors.AnsibleError("error while evaluating conditional: %s" % original) - elif val == "True": - return True - elif val == "False": - return False - else: - raise errors.AnsibleError("unable to evaluate conditional: %s" % original) - if not isinstance(conditional, basestring): return conditional - try: - conditional = conditional.replace("\n", "\\n") - result = safe_eval(conditional) - if result not in [ True, False ]: - raise errors.AnsibleError("Conditional expression must evaluate to True or False: %s" % conditional) - return result - - except (NameError, SyntaxError): - raise errors.AnsibleError("Could not evaluate the expression: (%s)" % conditional) + conditional = conditional.replace("jinja2_compare ","") + # allow variable names + if conditional in inject and str(inject[conditional]).find('-') == -1: + conditional = inject[conditional] + conditional = template.template(basedir, conditional, inject, fail_on_undefined=fail_on_undefined) + original = str(conditional).replace("jinja2_compare ","") + # a Jinja2 evaluation that results in something Python can eval! + presented = "{%% if %s %%} True {%% else %%} False {%% endif %%}" % conditional + conditional = template.template(basedir, presented, inject) + val = conditional.strip() + if val == presented: + # the templating failed, meaning most likely a + # variable was undefined. If we happened to be + # looking for an undefined variable, return True, + # otherwise fail + if conditional.find("is undefined") != -1: + return True + elif conditional.find("is defined") != -1: + return False + else: + raise errors.AnsibleError("error while evaluating conditional: %s" % original) + elif val == "True": + return True + elif val == "False": + return False + else: + raise errors.AnsibleError("unable to evaluate conditional: %s" % original) def is_executable(path): '''is the given path executable?''' @@ -756,86 +742,6 @@ def boolean(value): else: return False -def compile_when_to_only_if(expression): - ''' - when is a shorthand for writing only_if conditionals. It requires less quoting - magic. only_if is retained for backwards compatibility. - ''' - - # when: set $variable - # when: unset $variable - # when: failed $json_result - # when: changed $json_result - # when: int $x >= $z and $y < 3 - # when: int $x in $alist - # when: float $x > 2 and $y <= $z - # when: str $x != $y - # when: jinja2_compare asdf # implies {{ asdf }} - - if type(expression) not in [ str, unicode ]: - raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression) - tokens = expression.split() - if len(tokens) < 2: - raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression) - - # when_set / when_unset - if tokens[0] in [ 'set', 'unset' ]: - tcopy = tokens[1:] - for (i,t) in enumerate(tokens[1:]): - if t.find("$") != -1: - tcopy[i] = "is_%s('''%s''')" % (tokens[0], t) - else: - tcopy[i] = t - return " ".join(tcopy) - - # when_failed / when_changed - elif tokens[0] in [ 'failed', 'changed' ]: - tcopy = tokens[1:] - for (i,t) in enumerate(tokens[1:]): - if t.find("$") != -1: - tcopy[i] = "is_%s(%s)" % (tokens[0], t) - else: - tcopy[i] = t - return " ".join(tcopy) - - # when_integer / when_float / when_string - elif tokens[0] in [ 'integer', 'float', 'string' ]: - cast = None - if tokens[0] == 'integer': - cast = 'int' - elif tokens[0] == 'string': - cast = 'str' - elif tokens[0] == 'float': - cast = 'float' - tcopy = tokens[1:] - for (i,t) in enumerate(tokens[1:]): - #if re.search(t, r"^\w"): - # bare word will turn into Jinja2 so all the above - # casting is really not needed - #tcopy[i] = "%s('''%s''')" % (cast, t) - t2 = t.strip() - if (t2[0].isalpha() or t2[0] == '$') and cast == 'str' and t2 != 'in': - tcopy[i] = "'%s'" % (t) - else: - tcopy[i] = t - result = " ".join(tcopy) - return result - - - # when_boolean - elif tokens[0] in [ 'bool', 'boolean' ]: - tcopy = tokens[1:] - for (i, t) in enumerate(tcopy): - if t.find("$") != -1: - tcopy[i] = "(is_set('''%s''') and '''%s'''.lower() not in ('false', 'no', 'n', 'none', '0', ''))" % (t, t) - return " ".join(tcopy) - - # the stock 'when' without qualification (new in 1.2), assumes Jinja2 terms - elif tokens[0] == 'jinja2_compare': - return " ".join(tokens) - else: - raise errors.AnsibleError("invalid usage of when_ operator: %s" % expression) - def make_sudo_cmd(sudo_user, executable, cmd): """ helper function for connection plugins to create sudo commands diff --git a/test/TestUtils.py b/test/TestUtils.py index fbdbe0f405..0e7a64a481 100644 --- a/test/TestUtils.py +++ b/test/TestUtils.py @@ -112,27 +112,27 @@ class TestUtils(unittest.TestCase): # boolean assert(ansible.utils.check_conditional( - 'jinja2_compare true', '/', {}) == True) + 'true', '/', {}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare false', '/', {}) == False) + 'false', '/', {}) == False) assert(ansible.utils.check_conditional( - 'jinja2_compare True', '/', {}) == True) + 'True', '/', {}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare False', '/', {}) == False) + 'False', '/', {}) == False) # integer assert(ansible.utils.check_conditional( - 'jinja2_compare 1', '/', {}) == True) + '1', '/', {}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare 0', '/', {}) == False) + '0', '/', {}) == False) # string, beware, a string is truthy unless empty assert(ansible.utils.check_conditional( - 'jinja2_compare "yes"', '/', {}) == True) + '"yes"', '/', {}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare "no"', '/', {}) == True) + '"no"', '/', {}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare ""', '/', {}) == False) + '""', '/', {}) == False) def test_check_conditional_jinja2_variable_literals(self): @@ -140,61 +140,61 @@ class TestUtils(unittest.TestCase): # boolean assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': 'True'}) == True) + 'var', '/', {'var': 'True'}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': 'true'}) == True) + 'var', '/', {'var': 'true'}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': 'False'}) == False) + 'var', '/', {'var': 'False'}) == False) assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': 'false'}) == False) + 'var', '/', {'var': 'false'}) == False) # integer assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': '1'}) == True) + 'var', '/', {'var': '1'}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': 1}) == True) + 'var', '/', {'var': 1}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': '0'}) == False) + 'var', '/', {'var': '0'}) == False) assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': 0}) == False) + 'var', '/', {'var': 0}) == False) # string, beware, a string is truthy unless empty assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': '"yes"'}) == True) + 'var', '/', {'var': '"yes"'}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': '"no"'}) == True) + 'var', '/', {'var': '"no"'}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': '""'}) == False) + 'var', '/', {'var': '""'}) == False) # Python boolean in Jinja2 expression assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': True}) == True) + 'var', '/', {'var': True}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': False}) == False) + 'var', '/', {'var': False}) == False) def test_check_conditional_jinja2_expression(self): assert(ansible.utils.check_conditional( - 'jinja2_compare 1 == 1', '/', {}) == True) + '1 == 1', '/', {}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare bar == 42', '/', {'bar': 42}) == True) + 'bar == 42', '/', {'bar': 42}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare bar != 42', '/', {'bar': 42}) == False) + 'bar != 42', '/', {'bar': 42}) == False) def test_check_conditional_jinja2_expression_in_variable(self): assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': '1 == 1'}) == True) + 'var', '/', {'var': '1 == 1'}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': 'bar == 42', 'bar': 42}) == True) + 'var', '/', {'var': 'bar == 42', 'bar': 42}) == True) assert(ansible.utils.check_conditional( - 'jinja2_compare var', '/', {'var': 'bar != 42', 'bar': 42}) == False) + 'var', '/', {'var': 'bar != 42', 'bar': 42}) == False) def test_check_conditional_jinja2_unicode(self): assert(ansible.utils.check_conditional( - u'jinja2_compare "\u00df"', '/', {}) == True) + u'"\u00df"', '/', {}) == True) assert(ansible.utils.check_conditional( - u'jinja2_compare var == "\u00df"', '/', {'var': u'\u00df'}) == True) + u'var == "\u00df"', '/', {'var': u'\u00df'}) == True) ##################################### From e96377685e6982b8665b667e282c4b23304bacf4 Mon Sep 17 00:00:00 2001 From: Paul Beattie Date: Sat, 4 Jan 2014 00:22:14 +0000 Subject: [PATCH 099/182] Fixed typo in example documentation --- library/system/sysctl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/system/sysctl b/library/system/sysctl index 71320a3453..1a4fd7fe47 100644 --- a/library/system/sysctl +++ b/library/system/sysctl @@ -81,7 +81,7 @@ EXAMPLES = ''' # Set kernel.panic to 3 in /tmp/test_sysctl.conf, check if the sysctl key # seems writable, but do not reload sysctl, and do not check kernel value # after (not needed, because the real /etc/sysctl.conf was not updated) -- sysctl: name=kernel.panic value=3 sysctl_file=/tmp/test_sysctl.conf check=before reload=no +- sysctl: name=kernel.panic value=3 sysctl_file=/tmp/test_sysctl.conf checks=before reload=no ''' # ============================================================== From c41a23a35ad166aa1e22d205fe8ad11318bef88a Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Fri, 3 Jan 2014 19:23:19 -0500 Subject: [PATCH 100/182] Complete removal/refactoring of conditional deprecations. --- lib/ansible/utils/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index 0fb141946b..32a3993a08 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -165,6 +165,9 @@ def is_changed(result): def check_conditional(conditional, basedir, inject, fail_on_undefined=False): + if conditional is None or conditional == '': + return True + if isinstance(conditional, list): for x in conditional: if not check_conditional(x, basedir, inject, fail_on_undefined=fail_on_undefined): From 35328ed5032643bfc731bacbe74295657436540b Mon Sep 17 00:00:00 2001 From: Jesse Keating Date: Fri, 3 Jan 2014 16:39:13 -0800 Subject: [PATCH 101/182] Return early from setup step when possible The _list_available_hosts call can be lengthy, and in the case where gather_facts is disabled the call is pointless. So re-arrange the logic to return early from _do_setup_step when gather_facts is false. --- lib/ansible/playbook/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index e2bd662618..c34973dd75 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -433,11 +433,11 @@ class PlayBook(object): def _do_setup_step(self, play): ''' get facts from the remote system ''' - host_list = self._list_available_hosts(play.hosts) - if play.gather_facts is False: return {} - elif play.gather_facts is None: + + host_list = self._list_available_hosts(play.hosts) + if play.gather_facts is None: host_list = [h for h in host_list if h not in self.SETUP_CACHE or 'module_setup' not in self.SETUP_CACHE[h]] if len(host_list) == 0: return {} From 23720ff19dc3befbc5cf0a0cce54821697be7255 Mon Sep 17 00:00:00 2001 From: Jesse Keating Date: Fri, 3 Jan 2014 18:07:00 -0800 Subject: [PATCH 102/182] Use list comprehensions for efficiency For loops can be inefficient, particularly when doing a dot command with them. https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Loops and https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Avoiding_dots... This patch makes use of list comprehensions to be as efficient as possible while still retaining order. Efficiency was lost a bit when switching away from sets() but this gains some of it back. --- lib/ansible/inventory/__init__.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/ansible/inventory/__init__.py b/lib/ansible/inventory/__init__.py index f202a14192..d36e47d212 100644 --- a/lib/ansible/inventory/__init__.py +++ b/lib/ansible/inventory/__init__.py @@ -133,11 +133,7 @@ class Inventory(object): # exclude hosts not in a subset, if defined if self._subset: subset = self._get_hosts(self._subset) - new_hosts = [] - for h in hosts: - if h in subset and h not in new_hosts: - new_hosts.append(h) - hosts = new_hosts + hosts = [ h for h in hosts if h in subset ] # exclude hosts mentioned in any restriction (ex: failed hosts) if self._restriction is not None: @@ -183,9 +179,7 @@ class Inventory(object): elif p.startswith("&"): hosts = [ h for h in hosts if h in that ] else: - for h in that: - if h not in hosts: - hosts.append(h) + hosts.extend([ h for h in that if h not in hosts ]) return hosts From 10350d1639ff5ef1eb9e2d2febd0b5c6e317f22c Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Sat, 4 Jan 2014 13:31:44 -0500 Subject: [PATCH 103/182] Update various copyrights. Not complete, but sufficient. --- lib/ansible/__init__.py | 2 +- lib/ansible/callback_plugins/noop.py | 2 +- lib/ansible/callbacks.py | 2 +- lib/ansible/color.py | 2 +- lib/ansible/constants.py | 2 +- lib/ansible/errors.py | 2 +- lib/ansible/inventory/__init__.py | 2 +- lib/ansible/inventory/group.py | 2 +- lib/ansible/inventory/host.py | 2 +- lib/ansible/inventory/ini.py | 2 +- lib/ansible/inventory/script.py | 2 +- lib/ansible/inventory/vars_plugins/group_vars.py | 2 +- lib/ansible/module_common.py | 2 +- lib/ansible/playbook/__init__.py | 2 +- lib/ansible/playbook/play.py | 2 +- lib/ansible/playbook/task.py | 2 +- lib/ansible/runner/__init__.py | 2 +- lib/ansible/runner/action_plugins/assemble.py | 2 +- lib/ansible/runner/action_plugins/async.py | 2 +- lib/ansible/runner/action_plugins/copy.py | 2 +- lib/ansible/runner/action_plugins/fetch.py | 2 +- lib/ansible/runner/action_plugins/include_vars.py | 2 +- lib/ansible/runner/poller.py | 2 +- lib/ansible/runner/return_data.py | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/ansible/__init__.py b/lib/ansible/__init__.py index 7486c49b46..bad06025a1 100644 --- a/lib/ansible/__init__.py +++ b/lib/ansible/__init__.py @@ -1,4 +1,4 @@ -# (c) 2012-2013, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/callback_plugins/noop.py b/lib/ansible/callback_plugins/noop.py index 8af5a133e6..3d03ba55d6 100644 --- a/lib/ansible/callback_plugins/noop.py +++ b/lib/ansible/callback_plugins/noop.py @@ -1,4 +1,4 @@ -# (C) 2012-2013, Michael DeHaan, +# (C) 2012-2014, Michael DeHaan, # This file is part of Ansible # diff --git a/lib/ansible/callbacks.py b/lib/ansible/callbacks.py index 5274a6f8a5..4681dd2fe0 100644 --- a/lib/ansible/callbacks.py +++ b/lib/ansible/callbacks.py @@ -1,4 +1,4 @@ -# (C) 2012-2013, Michael DeHaan, +# (C) 2012-2014, Michael DeHaan, # This file is part of Ansible # diff --git a/lib/ansible/color.py b/lib/ansible/color.py index 304967993c..e5f6f4d2ba 100644 --- a/lib/ansible/color.py +++ b/lib/ansible/color.py @@ -1,4 +1,4 @@ -# (c) 2012, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index 2a08dd93d7..386291e23c 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -1,4 +1,4 @@ -# (c) 2012, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/errors.py b/lib/ansible/errors.py index acbec4714f..344762f343 100644 --- a/lib/ansible/errors.py +++ b/lib/ansible/errors.py @@ -1,4 +1,4 @@ -# (c) 2012, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/inventory/__init__.py b/lib/ansible/inventory/__init__.py index f202a14192..10bb56ca71 100644 --- a/lib/ansible/inventory/__init__.py +++ b/lib/ansible/inventory/__init__.py @@ -1,4 +1,4 @@ -# (c) 2012, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/inventory/group.py b/lib/ansible/inventory/group.py index 6fc812dc0c..c5270ad554 100644 --- a/lib/ansible/inventory/group.py +++ b/lib/ansible/inventory/group.py @@ -1,4 +1,4 @@ -# (c) 2012, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/inventory/host.py b/lib/ansible/inventory/host.py index fea867382e..19b919ac66 100644 --- a/lib/ansible/inventory/host.py +++ b/lib/ansible/inventory/host.py @@ -1,4 +1,4 @@ -# (c) 2012, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/inventory/ini.py b/lib/ansible/inventory/ini.py index 69689758aa..9dc576bf79 100644 --- a/lib/ansible/inventory/ini.py +++ b/lib/ansible/inventory/ini.py @@ -1,4 +1,4 @@ -# (c) 2012, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/inventory/script.py b/lib/ansible/inventory/script.py index 9474c3ebd7..05348b6839 100644 --- a/lib/ansible/inventory/script.py +++ b/lib/ansible/inventory/script.py @@ -1,4 +1,4 @@ -# (c) 2012, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/inventory/vars_plugins/group_vars.py b/lib/ansible/inventory/vars_plugins/group_vars.py index 2f5ee70146..3473b3192c 100644 --- a/lib/ansible/inventory/vars_plugins/group_vars.py +++ b/lib/ansible/inventory/vars_plugins/group_vars.py @@ -1,4 +1,4 @@ -# (c) 2012-2013, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/module_common.py b/lib/ansible/module_common.py index 7beac36bee..da02882d93 100644 --- a/lib/ansible/module_common.py +++ b/lib/ansible/module_common.py @@ -1,4 +1,4 @@ -# (c) 2013, Michael DeHaan +# (c) 2013-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index 189fd63d99..ca491406ff 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -1,4 +1,4 @@ -# (c) 2012-2013, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index 85a9378cf1..ebd7dcbf33 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -1,4 +1,4 @@ -# (c) 2012-2013, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 59f5c6fe1f..df496e11a8 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -1,4 +1,4 @@ -# (c) 2012-2013, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py index 011a31512b..dabd8cd954 100644 --- a/lib/ansible/runner/__init__.py +++ b/lib/ansible/runner/__init__.py @@ -1,4 +1,4 @@ -# (c) 2012-2013, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/runner/action_plugins/assemble.py b/lib/ansible/runner/action_plugins/assemble.py index 3f55023aa9..773f77e6b1 100644 --- a/lib/ansible/runner/action_plugins/assemble.py +++ b/lib/ansible/runner/action_plugins/assemble.py @@ -1,4 +1,4 @@ -# (c) 2013, Michael DeHaan +# (c) 2013-2014, Michael DeHaan # Stephen Fromm # Brian Coca # diff --git a/lib/ansible/runner/action_plugins/async.py b/lib/ansible/runner/action_plugins/async.py index c977af007c..12fe279a47 100644 --- a/lib/ansible/runner/action_plugins/async.py +++ b/lib/ansible/runner/action_plugins/async.py @@ -1,4 +1,4 @@ -# (c) 2012, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/runner/action_plugins/copy.py b/lib/ansible/runner/action_plugins/copy.py index 7364de9b10..e493e7abf4 100644 --- a/lib/ansible/runner/action_plugins/copy.py +++ b/lib/ansible/runner/action_plugins/copy.py @@ -1,4 +1,4 @@ -# (c) 2012, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/runner/action_plugins/fetch.py b/lib/ansible/runner/action_plugins/fetch.py index 28e06f8cfc..205023fad9 100644 --- a/lib/ansible/runner/action_plugins/fetch.py +++ b/lib/ansible/runner/action_plugins/fetch.py @@ -1,4 +1,4 @@ -# (c) 2012, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/runner/action_plugins/include_vars.py b/lib/ansible/runner/action_plugins/include_vars.py index 344cc82fa6..8d333af6df 100644 --- a/lib/ansible/runner/action_plugins/include_vars.py +++ b/lib/ansible/runner/action_plugins/include_vars.py @@ -1,4 +1,4 @@ -# (c) 2013, Benno Joy +# (c) 2013-2014, Benno Joy # # This file is part of Ansible # diff --git a/lib/ansible/runner/poller.py b/lib/ansible/runner/poller.py index af2be9246f..c69b2e76da 100644 --- a/lib/ansible/runner/poller.py +++ b/lib/ansible/runner/poller.py @@ -1,4 +1,4 @@ -# (c) 2012-2013, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # diff --git a/lib/ansible/runner/return_data.py b/lib/ansible/runner/return_data.py index 5e4933fefb..94e454cc98 100644 --- a/lib/ansible/runner/return_data.py +++ b/lib/ansible/runner/return_data.py @@ -1,4 +1,4 @@ -# (c) 2012-2013, Michael DeHaan +# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # From 87258f6d428a74c5da0eeda76f797981a9418984 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Sat, 4 Jan 2014 14:03:42 -0500 Subject: [PATCH 104/182] Resolve merge commit --- lib/ansible/playbook/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index 4b5a483348..dc7991aaf7 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -437,10 +437,6 @@ class PlayBook(object): return {} host_list = self._list_available_hosts(play.hosts) - if play.gather_facts is None: - host_list = [h for h in host_list if h not in self.SETUP_CACHE or 'module_setup' not in self.SETUP_CACHE[h]] - if len(host_list) == 0: - return {} self.callbacks.on_setup() self.inventory.restrict_to(host_list) From c039e276a2089cac804db4b0ea9072d8a27fee86 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Sat, 4 Jan 2014 13:49:07 -0500 Subject: [PATCH 105/182] Fact gathering on a second play against the same hosts should not be implicitly off since this is an undocumented beheavior and potentially rather unwanted when a play could change a fact. gather_facts: True/False can still be explicitly used per play. Reverts 564a212b3c6df0af099d97dbaee29a2e26155e89 --- lib/ansible/playbook/play.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index ebd7dcbf33..92a40ba6ec 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -114,7 +114,7 @@ class Play(object): self.sudo = ds.get('sudo', self.playbook.sudo) self.sudo_user = ds.get('sudo_user', self.playbook.sudo_user) self.transport = ds.get('connection', self.playbook.transport) - self.gather_facts = ds.get('gather_facts', None) + self.gather_facts = ds.get('gather_facts', True) self.remote_port = self.remote_port self.any_errors_fatal = utils.boolean(ds.get('any_errors_fatal', 'false')) self.accelerate = utils.boolean(ds.get('accelerate', 'false')) From 93a55e8dfff0311da28d00bc56565ad5b8599b4e Mon Sep 17 00:00:00 2001 From: smoothify Date: Sat, 4 Jan 2014 23:15:19 +0000 Subject: [PATCH 106/182] Don't override sudo if transport is set to local. https://github.com/ansible/ansible/pull/5251 --- .../runner/action_plugins/synchronize.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/ansible/runner/action_plugins/synchronize.py b/lib/ansible/runner/action_plugins/synchronize.py index 4c323dc32d..875cfe692e 100644 --- a/lib/ansible/runner/action_plugins/synchronize.py +++ b/lib/ansible/runner/action_plugins/synchronize.py @@ -43,11 +43,18 @@ class ActionModule(object): def setup(self, module_name, inject): ''' Always default to localhost as delegate if None defined ''' + + # Store original transport and sudo values. + self.original_transport = inject.get('ansible_connection', self.runner.transport) + self.original_sudo = self.runner.sudo + self.transport_overridden = False + if inject.get('delegate_to') is None: inject['delegate_to'] = '127.0.0.1' - inject['ansible_connection'] = 'local' - # If sudo is active, disable from the connection set self.sudo to True. - if self.runner.sudo: + # IF original transport is not local, override transport and disable sudo. + if self.original_transport != 'local': + inject['ansible_connection'] = 'local' + self.transport_overridden = True self.runner.sudo = False def run(self, conn, tmp, module_name, module_args, @@ -78,6 +85,7 @@ class ActionModule(object): dest_host = inject.get('ansible_ssh_host', inject['inventory_hostname']) # allow ansible_ssh_host to be templated dest_host = template.template(self.runner.basedir, dest_host, inject, fail_on_undefined=True) + dest_is_local = dest_host in ['127.0.0.1', 'localhost'] dest_port = options.get('dest_port') inv_port = inject.get('ansible_ssh_port', inject['inventory_hostname']) @@ -114,8 +122,11 @@ class ActionModule(object): if 'mode' in options: del options['mode'] + # Allow custom rsync path argument. rsync_path = options.get('rsync_path', None) - if not rsync_path and self.runner.sudo: + + # If no rsync_path is set, sudo was originally set, and dest is remote then add 'sudo rsync' argument. + if not rsync_path and self.transport_overridden and self.original_sudo and not dest_is_local: rsync_path = 'sudo rsync' # make sure rsync path is quoted. From d8d1152940ffa5e27ff52743f4a59972b2850540 Mon Sep 17 00:00:00 2001 From: James Tanner Date: Mon, 6 Jan 2014 10:38:56 -0500 Subject: [PATCH 107/182] Update latest released version of ansible to 1.4.4 on docsite --- docsite/rst/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docsite/rst/index.rst b/docsite/rst/index.rst index 864cd33796..d3e409c15f 100644 --- a/docsite/rst/index.rst +++ b/docsite/rst/index.rst @@ -18,7 +18,7 @@ upgrade remote daemons or the problem of not being able to manage systems becaus You may be interested in reading about `some notable Ansible users `_. -This documentation covers the current released version of Ansible (1.4.3) and also some development version features (1.5). For recent features, in each section, the version of Ansible where the feature is added is indicated. AnsibleWorks releases a new major release of Ansible approximately every 2 months. The core application evolves somewhat conservatively, valuing simplicity in language design and setup, while the community around new modules and plugins being developed and contributed moves very very quickly, typically adding 20 or so new modules in each release. +This documentation covers the current released version of Ansible (1.4.4) and also some development version features (1.5). For recent features, in each section, the version of Ansible where the feature is added is indicated. AnsibleWorks releases a new major release of Ansible approximately every 2 months. The core application evolves somewhat conservatively, valuing simplicity in language design and setup, while the community around new modules and plugins being developed and contributed moves very very quickly, typically adding 20 or so new modules in each release. .. _an_introduction: From ccbc99fe4fa415a7d30ab173a3d5d8733cd50ef8 Mon Sep 17 00:00:00 2001 From: James Cammarata Date: Mon, 6 Jan 2014 10:40:47 -0500 Subject: [PATCH 108/182] Fixed splitting of role/user name when username has a '.' in it This may still be an issue if users create roles with a '.' in the name though. We will probably have to disallow that in the role naming convention. --- bin/ansible-galaxy | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/ansible-galaxy b/bin/ansible-galaxy index a6845fc86e..7fbc404921 100755 --- a/bin/ansible-galaxy +++ b/bin/ansible-galaxy @@ -241,7 +241,10 @@ def api_lookup_role_by_name(api_server, role_name): role_name = urllib.quote(role_name) try: - user_name,role_name = role_name.split(".", 1) + parts = role_name.split(".") + user_name = ".".join(parts[0:-1]) + role_name = parts[-1] + print " downloading role '%s', owned by %s" % (role_name, user_name) except: print "Invalid role name (%s). You must specify username.rolename" % role_name sys.exit(1) From ab51bd23a25fceb7b779de90879396ce6e68b000 Mon Sep 17 00:00:00 2001 From: James Tanner Date: Mon, 6 Jan 2014 14:19:20 -0500 Subject: [PATCH 109/182] Fixes #5196 return a unique list of hostnames for a single host pattern --- lib/ansible/inventory/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/ansible/inventory/__init__.py b/lib/ansible/inventory/__init__.py index db53ba60a8..465d5a4b81 100644 --- a/lib/ansible/inventory/__init__.py +++ b/lib/ansible/inventory/__init__.py @@ -21,6 +21,7 @@ import fnmatch import os import re import subprocess +from sets import Set import ansible.constants as C from ansible.inventory.ini import InventoryParser @@ -251,6 +252,8 @@ class Inventory(object): """ Get all host names matching the pattern """ hosts = [] + hostnames = Set() + # ignore any negative checks here, this is handled elsewhere pattern = pattern.replace("!","").replace("&", "") @@ -259,8 +262,9 @@ class Inventory(object): for group in groups: for host in group.get_hosts(): if pattern == 'all' or self._match(group.name, pattern) or self._match(host.name, pattern): - if host not in results: + if host not in results and host.name not in hostnames: results.append(host) + hostnames.add(host.name) return results def clear_pattern_cache(self): From 5c64d703b96a55ff99f2d23a8c8867a6f75a53d4 Mon Sep 17 00:00:00 2001 From: James Tanner Date: Mon, 6 Jan 2014 15:40:11 -0500 Subject: [PATCH 110/182] Fixes #5227 Corrected doc example of with_first_found --- docsite/rst/playbooks_conditionals.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docsite/rst/playbooks_conditionals.rst b/docsite/rst/playbooks_conditionals.rst index d62dc87e84..2dc1329c3e 100644 --- a/docsite/rst/playbooks_conditionals.rst +++ b/docsite/rst/playbooks_conditionals.rst @@ -193,10 +193,10 @@ The following example shows how to template out a configuration file that was ve - name: template a file template: src={{ item }} dest=/etc/myapp/foo.conf with_first_found: - files: + - files: - {{ ansible_distribution }}.conf - default.conf - paths: + paths: - search_location_one/somedir/ - /opt/other_location/somedir/ From d6b78e9dd6c5f819bff0e38e54075f1af4cfd62a Mon Sep 17 00:00:00 2001 From: James Tanner Date: Mon, 6 Jan 2014 16:47:19 -0500 Subject: [PATCH 111/182] Fixes #5238 Improve error messaging about unquoted variable references in playbooks --- lib/ansible/utils/__init__.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py index 32a3993a08..a263ed15ff 100644 --- a/lib/ansible/utils/__init__.py +++ b/lib/ansible/utils/__init__.py @@ -447,8 +447,30 @@ Note: The error may actually appear before this position: line %s, column %s %s %s""" % (path, mark.line + 1, mark.column + 1, before_probline, probline, arrow) - msg = process_common_errors(msg, probline, mark.column) + unquoted_var = None + if '{{' in probline and '}}' in probline: + if '"{{' not in probline or "'{{" not in probline: + unquoted_var = True + msg = process_common_errors(msg, probline, mark.column) + if not unquoted_var: + msg = process_common_errors(msg, probline, mark.column) + else: + msg = msg + """ +We could be wrong, but this one looks like it might be an issue with +missing quotes. Always quote template expression brackets when they +start a value. For instance: + + with_items: + - {{ foo }} + +Should be written as: + + with_items: + - "{{ foo }}" + +""" + msg = process_common_errors(msg, probline, mark.column) else: # No problem markers means we have to throw a generic From a0d0a708821c442fc3f56135e77f244065b2ef66 Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Mon, 6 Jan 2014 22:55:19 +0000 Subject: [PATCH 112/182] Modified docsite layout to use Google custom search. --- docsite/_themes/srtd/layout.html | 27 ++++++++++++++- docsite/_themes/srtd/searchbox.html | 52 ++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/docsite/_themes/srtd/layout.html b/docsite/_themes/srtd/layout.html index 6c922d0db3..94d3ac0590 100644 --- a/docsite/_themes/srtd/layout.html +++ b/docsite/_themes/srtd/layout.html @@ -38,6 +38,7 @@ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga {# CSS #} + {# JS #} {% if not embedded %} @@ -101,6 +102,27 @@ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga + + @@ -114,7 +136,7 @@ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga {% include "searchbox.html" %} -
    +