commit
825d4d11be
|
@ -52,7 +52,7 @@ local PipelineTesting = {
|
|||
commands: [
|
||||
"pip install -r test-requirements.txt -qq",
|
||||
"pip install -qq .",
|
||||
"bandit -r ./ansiblelater",
|
||||
"bandit -r ./ansiblelater -x ./ansiblelater/tests",
|
||||
],
|
||||
depends_on: [
|
||||
"clone",
|
||||
|
|
|
@ -69,7 +69,7 @@ steps:
|
|||
commands:
|
||||
- pip install -r test-requirements.txt -qq
|
||||
- pip install -qq .
|
||||
- bandit -r ./ansiblelater
|
||||
- bandit -r ./ansiblelater -x ./ansiblelater/tests
|
||||
environment:
|
||||
PY_COLORS: 1
|
||||
depends_on:
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
[flake8]
|
||||
# Temp disable Docstring checks D101, D102, D103, D107
|
||||
ignore = E501, W503, F401, N813, D101, D102, D103, D107
|
||||
max-line-length = 100
|
||||
inline-quotes = double
|
||||
exclude = .git,.tox,__pycache__,build,dist,tests,*.pyc,*.egg-info,.cache,.eggs
|
||||
application-import-names = ansiblelater
|
||||
format = ${cyan}%(path)s:%(row)d:%(col)d${reset}: ${red_bold}%(code)s${reset} %(text)s
|
|
@ -0,0 +1,41 @@
|
|||
repository:
|
||||
name: ansible-later
|
||||
description: Ansible - Lovely Automation Testing Framework
|
||||
topics: ansible, ansible-later, ansible-review, best practice
|
||||
|
||||
private: false
|
||||
has_issues: true
|
||||
has_wiki: false
|
||||
has_downloads: true
|
||||
|
||||
default_branch: master
|
||||
|
||||
allow_squash_merge: true
|
||||
allow_merge_commit: true
|
||||
allow_rebase_merge: true
|
||||
|
||||
labels:
|
||||
- name: bug
|
||||
color: d73a4a
|
||||
description: Something isn't working
|
||||
- name: duplicate
|
||||
color: cfd3d7
|
||||
description: This issue or pull request already exists
|
||||
- name: enhancement
|
||||
color: a2eeef
|
||||
description: New feature or request
|
||||
- name: good first issue
|
||||
color: 7057ff
|
||||
description: Good for newcomers
|
||||
- name: help wanted
|
||||
color: 008672
|
||||
description: Extra attention is needed
|
||||
- name: invalid
|
||||
color: e4e669
|
||||
description: This doesn't seem right
|
||||
- name: question
|
||||
color: d876e3
|
||||
description: Further information is requested
|
||||
- name: wontfix
|
||||
color: ffffff
|
||||
description: This will not be worked on
|
22
CHANGELOG.md
22
CHANGELOG.md
|
@ -1,2 +1,22 @@
|
|||
**BREAKING RELEASE**
|
||||
|
||||
ansible-later contains some fundamental restructuring and is not backward compatible
|
||||
with old releases.
|
||||
|
||||
- BREAKING
|
||||
- Switch configuration files to YAML
|
||||
- Enable multi location configuration files [#14](https://github.com/xoxys/ansible-later/issues/14)
|
||||
- ID's used in standards have to be unique (or not set)
|
||||
|
||||
- FEATURE
|
||||
- Add optional JSON logging [#13](https://github.com/xoxys/ansible-later/issues/13)
|
||||
- Add exclude options in config files [#16](https://github.com/xoxys/ansible-later/issues/16)
|
||||
- Add multiprocessing for better perfomance [#12](https://github.com/xoxys/ansible-later/issues/12)
|
||||
|
||||
- ENHANCEMENT
|
||||
- Respect `PY_COLORS` to get colorized output for nontty environments ([#10](https://github.com/xoxys/ansible-later/pull/10))
|
||||
- Allow passing glob patterns to cli [#16](https://github.com/xoxys/ansible-later/issues/16)
|
||||
- Rule settings (e.g. for yamllint) can be set in config file [#7](https://github.com/xoxys/ansible-later/issues/7)
|
||||
- Remove simple print outputs and switch to python logging module [#13](https://github.com/xoxys/ansible-later/issues/13)
|
||||
- Restructure log output for better readability [#13](https://github.com/xoxys/ansible-later/issues/13)
|
||||
- Better loglevel control from cli (-vvv/-qqq) [#13](https://github.com/xoxys/ansible-later/issues/13)
|
||||
- Better inventory file classification [#15](https://github.com/xoxys/ansible-later/issues/15)
|
||||
|
|
209
README.md
209
README.md
|
@ -13,28 +13,27 @@ for his work on ansible-review and ansible-lint.
|
|||
it helps to have a coding or best practice guideline in place. This will make ansible roles more readable for all
|
||||
maintainers and can reduce the troubleshooting time.
|
||||
|
||||
`ansible-later` does _**not**_ ensure that your role will work as expected.
|
||||
`ansible-later` does _**not**_ ensure that your role will work as expected. For Deployment test you can use other tools
|
||||
like [molecule](https://github.com/ansible/molecule).
|
||||
|
||||
The project name is an acronym for **L**ovely **A**utomation **TE**sting f**R**mework.
|
||||
|
||||
## Table of Content
|
||||
|
||||
- [ansible-later](#ansible-later)
|
||||
- [Table of Content](#table-of-content)
|
||||
- [Setup](#setup)
|
||||
- [Using pip](#using-pip)
|
||||
- [From source](#from-source)
|
||||
- [Usage](#usage)
|
||||
- [Configuration](#configuration)
|
||||
- [Review a git repositories](#review-a-git-repositories)
|
||||
- [Review a list of files](#review-a-list-of-files)
|
||||
- [Buildin rules](#buildin-rules)
|
||||
- [Build your own](#build-your-own)
|
||||
- [The standards file](#the-standards-file)
|
||||
- [Candidates](#candidates)
|
||||
- [Minimal standards checks](#minimal-standards-checks)
|
||||
- [License](#license)
|
||||
- [Maintainers and Contributors](#maintainers-and-contributors)
|
||||
- [Setup](#setup)
|
||||
- [Using pip](#using-pip)
|
||||
- [From source](#from-source)
|
||||
- [Configuration](#configuration)
|
||||
- [Default settings](#default-settings)
|
||||
- [CLI Options](#cli-options)
|
||||
- [Usage](#usage)
|
||||
- [Buildin rules](#buildin-rules)
|
||||
- [Build your own rules](#build-your-own-rules)
|
||||
- [The standards file](#the-standards-file)
|
||||
- [Candidates](#candidates)
|
||||
- [Minimal standards checks](#minimal-standards-checks)
|
||||
- [License](#license)
|
||||
- [Maintainers and Contributors](#maintainers-and-contributors)
|
||||
|
||||
---
|
||||
|
||||
|
@ -54,9 +53,117 @@ sudo pip install ansible-later
|
|||
|
||||
```Shell
|
||||
# Install dependency
|
||||
git clone https://repourl
|
||||
git clone https://github.com/xoxys/ansible-later
|
||||
export PYTHONPATH=$PYTHONPATH:`pwd`/ansible-later/ansiblelater
|
||||
export PATH=$PATH:`pwd`/ansible-later/ansiblelater/bin
|
||||
export PATH=$PATH:`pwd`/ansible-later/bin
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
ansible-later comes with some default settigs which should be sufficent for most users to start,
|
||||
but you can adjust most settings to your needs.
|
||||
|
||||
Changes can be made in a yaml configuration file or through cli options
|
||||
which will be processed in the following order (last wins):
|
||||
|
||||
- default config (build-in)
|
||||
- global config file (this will depend on your operating system)
|
||||
- folderbased config file (`.later.yml` file in current working folder)
|
||||
- cli options
|
||||
|
||||
Be careful! YAML Attributes will be overwritten while lists in any
|
||||
config file will be merged.
|
||||
|
||||
To make it easier to review a singel file e.g. for debugging purpose, amsible-later
|
||||
will ignore `exclude_files` and `ignore_dotfiles` options.
|
||||
|
||||
#### Default settings
|
||||
|
||||
```YAML
|
||||
---
|
||||
ansible:
|
||||
# Add the name of used custom ansible modules.
|
||||
# Otherwise ansible-later can't detect unknown modules
|
||||
# and will through an error.
|
||||
custom_modules: []
|
||||
# Settings for variable formatting rule (ANSIBLE0004)
|
||||
double-braces:
|
||||
max-spaces-inside: 1
|
||||
min-spaces-inside: 1
|
||||
|
||||
# Global logging configuration
|
||||
# If you would like to force colored output (e.g. non-tty)
|
||||
# set emvironment variable `PY_COLORS=1`
|
||||
logging:
|
||||
# You can enable json logging if a parsable output is required
|
||||
json: False
|
||||
# Possible options debug | info | warning | error | critical
|
||||
level: "warning"
|
||||
|
||||
# Global settings for all defined rules
|
||||
rules:
|
||||
# list of files to exclude
|
||||
exclude_files: []
|
||||
# Examples:
|
||||
# - molecule/
|
||||
# - files/**/*.py
|
||||
|
||||
# List of Ansible rule ID's
|
||||
# If empty all rules will be used.
|
||||
filter: []
|
||||
|
||||
# All dotfiles (including hidden folders) are excluded by default.
|
||||
# You can disable this setting and handle dotfiles by yourself with `exclude_files`.
|
||||
ignore_dotfiles: True
|
||||
# Path to the folder containing your custom standards file
|
||||
standards: ansiblelater/data
|
||||
|
||||
# Block to control included yamlllint rules.
|
||||
# See https://yamllint.readthedocs.io/en/stable/rules.html
|
||||
yamllint:
|
||||
colons:
|
||||
max-spaces-after: 1
|
||||
max-spaces-before: 0
|
||||
document-start:
|
||||
present: True
|
||||
empty-lines:
|
||||
max: 1
|
||||
max-end: 1
|
||||
max-start: 0
|
||||
hyphens:
|
||||
max-spaces-after: 1
|
||||
indentation:
|
||||
check-multi-line-strings: False
|
||||
indent-sequences: True
|
||||
spaces: 2
|
||||
```
|
||||
|
||||
#### CLI Options
|
||||
|
||||
You can get all available cli options by running `ansible-later --help`:
|
||||
|
||||
```Shell
|
||||
$ ansible-later --help
|
||||
usage: ansible-later [-h] [-c CONFIG_FILE] [-r RULES.STANDARDS]
|
||||
[-s RULES.FILTER] [-v] [-q] [--version]
|
||||
[rules.files [rules.files ...]]
|
||||
|
||||
Validate ansible files against best pratice guideline
|
||||
|
||||
positional arguments:
|
||||
rules.files
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-c CONFIG_FILE, --config CONFIG_FILE
|
||||
location of configuration file
|
||||
-r RULES.STANDARDS, --rules RULES.STANDARDS
|
||||
location of standards rules
|
||||
-s RULES.FILTER, --standards RULES.FILTER
|
||||
limit standards to specific ID's
|
||||
-v increase log level
|
||||
-q decrease log level
|
||||
--version show program's version number and exit
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
@ -65,26 +172,23 @@ export PATH=$PATH:`pwd`/ansible-later/ansiblelater/bin
|
|||
ansible-later FILES
|
||||
```
|
||||
|
||||
Where FILES is a space delimited list of files to review.
|
||||
ansible-later is _not_ recursive and won't descend
|
||||
into child folders; it just processes the list of files you give it.
|
||||
|
||||
Passing a folder in with the list of files will elicit a warning:
|
||||
Where FILES is a space delimited list of files to review. You can also pass glob
|
||||
patterns to ansible-later:
|
||||
|
||||
```Shell
|
||||
WARN: Couldn't classify file ./foldername
|
||||
# Review single files
|
||||
ansible-later meta/main.yml tasks/install.yml
|
||||
|
||||
# Review all yml files (including subfolders)
|
||||
ansible-later **/*.yml
|
||||
```
|
||||
|
||||
ansible-later will review inventory files, role
|
||||
files, python code (modules, plugins) and playbooks.
|
||||
ansible-later will review inventory files, role f0iles, python code (modules, plugins)
|
||||
and playbooks.
|
||||
|
||||
- The goal is that each file that changes in a
|
||||
changeset should be reviewable simply by passing
|
||||
those files as the arguments to ansible-later.
|
||||
- Roles are slightly harder, and sub-roles are yet
|
||||
harder still (currently just using `-R` to process
|
||||
roles works very well, but doesn't examine the
|
||||
structure of the role)
|
||||
- Using `{{ playbook_dir }}` in sub roles is so far
|
||||
very hard.
|
||||
- This should work against various repository styles
|
||||
|
@ -93,42 +197,7 @@ files, python code (modules, plugins) and playbooks.
|
|||
- per-playbook repository
|
||||
- It should work with roles requirement files and with local roles
|
||||
|
||||
#### Configuration
|
||||
|
||||
If your standards (and optionally inhouse rules) are set up, create
|
||||
a configuration file in the appropriate location (this will depend on
|
||||
your operating system)
|
||||
|
||||
The location can be found by using `ansible-later` with no arguments.
|
||||
|
||||
You can override the configuration file location with the `-c` flag.
|
||||
|
||||
```INI
|
||||
[rules]
|
||||
standards = /path/to/your/standards/rules
|
||||
```
|
||||
|
||||
The standards directory can be overridden with the `-d` argument.
|
||||
|
||||
#### Review a git repositories
|
||||
|
||||
- `git ls-files | xargs ansible-later` works well in
|
||||
a roles repo to review the whole role. But it will
|
||||
review the whole of other repos too.
|
||||
- `git ls-files *[^LICENSE,.md] | xargs ansible-later`
|
||||
works like the first example but excludes some
|
||||
unnecessary files.
|
||||
- `git diff branch_to_compare | ansible-later` will
|
||||
review only the changes between the branches and
|
||||
surrounding context.
|
||||
|
||||
#### Review a list of files
|
||||
|
||||
- `find . -type f | xargs ansible-later` will review
|
||||
all files in the current folder (and all subfolders),
|
||||
even if they're not checked into git
|
||||
|
||||
#### Buildin rules
|
||||
### Buildin rules
|
||||
|
||||
Reviews are nothing without some rules or standards against which to review. ansible-later
|
||||
comes with a couple of built-in checks explained in the following table.
|
||||
|
@ -160,7 +229,7 @@ comes with a couple of built-in checks explained in the following table.
|
|||
| check_become_user | ANSIBLE0015 | `become` should be always used combined with `become_user`. | |
|
||||
| check_filter_separation | ANSIBLE0016 | Jinja2 filters should be separated with spaces. | |
|
||||
|
||||
### Build your own
|
||||
### Build your own rules
|
||||
|
||||
#### The standards file
|
||||
|
||||
|
@ -173,7 +242,9 @@ Create a file called standards.py (this can import other modules)
|
|||
from ansiblelater include Standard, Result
|
||||
|
||||
tasks_are_uniquely_named = Standard(dict(
|
||||
# ID's are optional but if you use ID's they have to be unique
|
||||
id="ANSIBLE0003",
|
||||
# Short description of the standard goal
|
||||
name="Tasks and handlers must be uniquely named within a single file",
|
||||
check=check_unique_named_task,
|
||||
version="0.1",
|
||||
|
@ -229,7 +300,7 @@ which contains some meta informations and is an instance of one of following obj
|
|||
| HostVars | all files (including subdirs) within the parent dir `host_vars` |
|
||||
| Meta | all files within the parent dir `meta` |
|
||||
| Code | all files within the parent dir `library`, `lookup_plugins`, `callback_plugins` and `filter_plugins` or python files (`.py`) |
|
||||
| Inventory | all files within the parent dir `inventory` and `inventory` or `hosts` in filename |
|
||||
| Inventory | all files within the parent dir `inventories` and `inventory` or `hosts` as filename |
|
||||
| Rolesfile | all files with `rolesfile` or `requirements` in filename |
|
||||
| Makefile | all files with `Makefile` in filename |
|
||||
| Template | all files (including subdirs) within the parent dir `templates` or jinja2 files (`.j2`) |
|
||||
|
|
|
@ -1,313 +1,13 @@
|
|||
"""Default package."""
|
||||
|
||||
__author__ = "Robert Kaussow"
|
||||
__project__ = "ansible-later"
|
||||
__version__ = "0.1.5"
|
||||
__version__ = "0.2.0"
|
||||
__license__ = "MIT"
|
||||
__maintainer__ = "Robert Kaussow"
|
||||
__email__ = "mail@geeklabor.de"
|
||||
__status__ = "Production"
|
||||
|
||||
from ansiblelater import logger
|
||||
|
||||
import re
|
||||
import os
|
||||
import codecs
|
||||
import ansible
|
||||
from distutils.version import LooseVersion
|
||||
from ansiblelater.utils import info, warn, abort, error
|
||||
from ansiblelater.utils import read_standards
|
||||
from ansiblelater.utils import get_property
|
||||
from ansiblelater.utils import standards_latest
|
||||
from ansiblelater.utils import is_line_in_ranges
|
||||
from ansiblelater.utils import lines_ranges
|
||||
|
||||
try:
|
||||
# Ansible 2.4 import of module loader
|
||||
from ansible.plugins.loader import module_loader
|
||||
except ImportError:
|
||||
try:
|
||||
from ansible.plugins import module_loader
|
||||
except ImportError:
|
||||
from ansible.utils import module_finder as module_loader
|
||||
|
||||
|
||||
class AnsibleReviewFormatter(object):
|
||||
def format(self, match):
|
||||
formatstr = u"{0}:{1}: [{2}] {3} {4}"
|
||||
return formatstr.format(match.filename,
|
||||
match.linenumber,
|
||||
match.rule.id,
|
||||
match.message,
|
||||
match.line
|
||||
)
|
||||
|
||||
|
||||
class Standard(object):
|
||||
def __init__(self, standard_dict):
|
||||
if 'id' not in standard_dict:
|
||||
standard_dict.update(id='')
|
||||
else:
|
||||
standard_dict.update(id='[{}] '.format(standard_dict.get("id")))
|
||||
self.id = standard_dict.get("id")
|
||||
self.name = standard_dict.get("name")
|
||||
self.version = standard_dict.get("version")
|
||||
self.check = standard_dict.get("check")
|
||||
self.types = standard_dict.get("types")
|
||||
|
||||
def __repr__(self):
|
||||
return "Standard: %s (version: %s, types: %s)" % (
|
||||
self.name, self.version, self.types)
|
||||
|
||||
|
||||
class Error(object):
|
||||
def __init__(self, lineno, message):
|
||||
self.lineno = lineno
|
||||
self.message = message
|
||||
|
||||
def __repr__(self):
|
||||
if self.lineno:
|
||||
return "%s: %s" % (self.lineno, self.message)
|
||||
else:
|
||||
return " %s" % (self.message)
|
||||
|
||||
|
||||
class Result(object):
|
||||
def __init__(self, candidate, errors=None):
|
||||
self.candidate = candidate
|
||||
self.errors = errors or []
|
||||
|
||||
def message(self):
|
||||
return "\n".join(["{0}:{1}".format(self.candidate, error)
|
||||
for error in self.errors])
|
||||
|
||||
|
||||
class Candidate(object):
|
||||
def __init__(self, filename):
|
||||
self.path = filename
|
||||
self.binary = False
|
||||
self.vault = False
|
||||
|
||||
try:
|
||||
self.version = find_version(filename)
|
||||
with codecs.open(filename, mode='rb', encoding='utf-8') as f:
|
||||
if f.readline().startswith("$ANSIBLE_VAULT"):
|
||||
self.vault = True
|
||||
except UnicodeDecodeError:
|
||||
self.binary = True
|
||||
|
||||
self.filetype = type(self).__name__.lower()
|
||||
self.expected_version = True
|
||||
|
||||
def review(self, settings, lines=None):
|
||||
return candidate_review(self, settings, lines)
|
||||
|
||||
def __repr__(self):
|
||||
return "%s (%s)" % (type(self).__name__, self.path)
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.__dict__.get(item)
|
||||
|
||||
|
||||
class RoleFile(Candidate):
|
||||
def __init__(self, filename):
|
||||
super(RoleFile, self).__init__(filename)
|
||||
self.version = None
|
||||
parentdir = os.path.dirname(os.path.abspath(filename))
|
||||
while parentdir != os.path.dirname(parentdir):
|
||||
meta_file = os.path.join(parentdir, "meta", "main.yml")
|
||||
if os.path.exists(meta_file):
|
||||
self.version = find_version(meta_file)
|
||||
if self.version:
|
||||
break
|
||||
parentdir = os.path.dirname(parentdir)
|
||||
role_modules = os.path.join(parentdir, 'library')
|
||||
if os.path.exists(role_modules):
|
||||
module_loader.add_directory(role_modules)
|
||||
|
||||
|
||||
class Playbook(Candidate):
|
||||
pass
|
||||
|
||||
|
||||
class Task(RoleFile):
|
||||
def __init__(self, filename):
|
||||
super(Task, self).__init__(filename)
|
||||
self.filetype = 'tasks'
|
||||
|
||||
|
||||
class Handler(RoleFile):
|
||||
def __init__(self, filename):
|
||||
super(Handler, self).__init__(filename)
|
||||
self.filetype = 'handlers'
|
||||
|
||||
|
||||
class Vars(Candidate):
|
||||
pass
|
||||
|
||||
|
||||
class Unversioned(Candidate):
|
||||
def __init__(self, filename):
|
||||
super(Unversioned, self).__init__(filename)
|
||||
self.expected_version = False
|
||||
|
||||
|
||||
class InventoryVars(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
class HostVars(InventoryVars):
|
||||
pass
|
||||
|
||||
|
||||
class GroupVars(InventoryVars):
|
||||
pass
|
||||
|
||||
|
||||
class RoleVars(RoleFile):
|
||||
pass
|
||||
|
||||
|
||||
class Meta(RoleFile):
|
||||
pass
|
||||
|
||||
|
||||
class Inventory(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
class Code(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
class Template(RoleFile):
|
||||
pass
|
||||
|
||||
|
||||
class Doc(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
# For ease of checking files for tabs
|
||||
class Makefile(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
class File(RoleFile):
|
||||
pass
|
||||
|
||||
|
||||
class Rolesfile(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
def classify(filename):
|
||||
parentdir = os.path.basename(os.path.dirname(filename))
|
||||
|
||||
if parentdir in ['tasks']:
|
||||
return Task(filename)
|
||||
if parentdir in ['handlers']:
|
||||
return Handler(filename)
|
||||
if parentdir in ['vars', 'defaults']:
|
||||
return RoleVars(filename)
|
||||
if 'group_vars' in filename.split(os.sep):
|
||||
return GroupVars(filename)
|
||||
if 'host_vars' in filename.split(os.sep):
|
||||
return HostVars(filename)
|
||||
if parentdir in ['meta']:
|
||||
return Meta(filename)
|
||||
if parentdir in ['library', 'lookup_plugins', 'callback_plugins',
|
||||
'filter_plugins'] or filename.endswith('.py'):
|
||||
return Code(filename)
|
||||
if 'inventory' in filename or 'hosts' in filename or parentdir in ['inventory']:
|
||||
return Inventory(filename)
|
||||
if 'rolesfile' in filename or 'requirements' in filename:
|
||||
return Rolesfile(filename)
|
||||
if 'Makefile' in filename:
|
||||
return Makefile(filename)
|
||||
if 'templates' in filename.split(os.sep) or filename.endswith('.j2'):
|
||||
return Template(filename)
|
||||
if 'files' in filename.split(os.sep):
|
||||
return File(filename)
|
||||
if filename.endswith('.yml') or filename.endswith('.yaml'):
|
||||
return Playbook(filename)
|
||||
if 'README' in filename:
|
||||
return Doc(filename)
|
||||
return None
|
||||
|
||||
|
||||
def candidate_review(candidate, settings, lines=None):
|
||||
errors = 0
|
||||
standards = read_standards(settings)
|
||||
if getattr(standards, 'ansible_min_version', None) and \
|
||||
LooseVersion(standards.ansible_min_version) > LooseVersion(ansible.__version__):
|
||||
raise SystemExit("Standards require ansible version %s (current version %s). "
|
||||
"Please upgrade ansible." %
|
||||
(standards.ansible_min_version, ansible.__version__))
|
||||
|
||||
if getattr(standards, 'ansible_review_min_version', None) and \
|
||||
LooseVersion(standards.ansible_review_min_version) > LooseVersion(
|
||||
get_property("__version__")):
|
||||
raise SystemExit("Standards require ansible-later version %s (current version %s). "
|
||||
"Please upgrade ansible-later." %
|
||||
(standards.ansible_review_min_version, get_property("__version__")))
|
||||
|
||||
if not candidate.version:
|
||||
candidate.version = standards_latest(standards.standards)
|
||||
if candidate.expected_version:
|
||||
if isinstance(candidate, RoleFile):
|
||||
warn("%s %s is in a role that contains a meta/main.yml without a declared "
|
||||
"standards version. "
|
||||
"Using latest standards version %s" %
|
||||
(type(candidate).__name__, candidate.path, candidate.version),
|
||||
settings)
|
||||
else:
|
||||
warn("%s %s does not present standards version. "
|
||||
"Using latest standards version %s" %
|
||||
(type(candidate).__name__, candidate.path, candidate.version),
|
||||
settings)
|
||||
|
||||
info("%s %s declares standards version %s" %
|
||||
(type(candidate).__name__, candidate.path, candidate.version),
|
||||
settings)
|
||||
|
||||
for standard in standards.standards:
|
||||
if type(candidate).__name__.lower() not in standard.types:
|
||||
continue
|
||||
if settings.standards_filter and standard.name not in settings.standards_filter:
|
||||
continue
|
||||
result = standard.check(candidate, settings)
|
||||
|
||||
if not result:
|
||||
abort("Standard '%s' returns an empty result object." %
|
||||
(standard.check.__name__))
|
||||
|
||||
for err in [err for err in result.errors
|
||||
if not err.lineno or is_line_in_ranges(err.lineno, lines_ranges(lines))]:
|
||||
if not standard.version:
|
||||
warn("{id}Best practice '{name}' not met:\n{path}:{error}".format(
|
||||
id=standard.id, name=standard.name, path=candidate.path, error=err), settings)
|
||||
elif LooseVersion(standard.version) > LooseVersion(candidate.version):
|
||||
warn("{id}Future standard '{name}' not met:\n{path}:{error}".format(
|
||||
id=standard.id, name=standard.name, path=candidate.path, error=err), settings)
|
||||
else:
|
||||
error("{id}Standard '{name}' not met:\n{path}:{error}".format(
|
||||
id=standard.id, name=standard.name, path=candidate.path, error=err))
|
||||
errors = errors + 1
|
||||
if not result.errors:
|
||||
if not standard.version:
|
||||
info("Best practice '%s' met" % standard.name, settings)
|
||||
elif LooseVersion(standard.version) > LooseVersion(candidate.version):
|
||||
info("Future standard '%s' met" % standard.name, settings)
|
||||
else:
|
||||
info("Standard '%s' met" % standard.name, settings)
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def find_version(filename, version_regex=r"^# Standards:\s*([\d.]+)"):
|
||||
version_re = re.compile(version_regex)
|
||||
|
||||
with codecs.open(filename, mode='rb', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
match = version_re.match(line)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
LOG = logger.get_logger("ansiblelater")
|
||||
|
|
|
@ -1,79 +1,65 @@
|
|||
#!/usr/bin/env python
|
||||
"""Main program."""
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
from appdirs import AppDirs
|
||||
from pkg_resources import resource_filename
|
||||
from ansiblelater import classify
|
||||
from ansiblelater.utils import info, warn, read_config, get_property
|
||||
import argparse
|
||||
import multiprocessing
|
||||
|
||||
from ansiblelater import LOG
|
||||
from ansiblelater import __version__
|
||||
from ansiblelater import logger
|
||||
from ansiblelater.command import base
|
||||
from ansiblelater.command import candidates
|
||||
|
||||
|
||||
def main():
|
||||
config_dir = AppDirs("ansible-later").user_config_dir
|
||||
default_config_file = os.path.join(config_dir, "config.ini")
|
||||
"""Run main program."""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Validate ansible files against best pratice guideline")
|
||||
parser.add_argument("-c", "--config", dest="config_file",
|
||||
help="location of configuration file")
|
||||
parser.add_argument("-r", "--rules", dest="rules.standards",
|
||||
help="location of standards rules")
|
||||
parser.add_argument("-s", "--standards", dest="rules.filter", action="append",
|
||||
help="limit standards to specific ID's")
|
||||
parser.add_argument("-v", dest="logging.level", action="append_const", const=-1,
|
||||
help="increase log level")
|
||||
parser.add_argument("-q", dest="logging.level", action="append_const",
|
||||
const=1, help="decrease log level")
|
||||
parser.add_argument("rules.files", nargs="*")
|
||||
parser.add_argument("--version", action="version", version="%(prog)s {}".format(__version__))
|
||||
|
||||
parser = optparse.OptionParser("%prog playbook_file|role_file|inventory_file",
|
||||
version="%prog " + get_property("__version__"))
|
||||
parser.add_option('-c', dest='configfile', default=default_config_file,
|
||||
help="Location of configuration file: [%s]" % default_config_file)
|
||||
parser.add_option('-d', dest='rulesdir',
|
||||
help="Location of standards rules")
|
||||
parser.add_option('-q', dest='log_level', action="store_const", default=logging.WARN,
|
||||
const=logging.ERROR, help="Only output errors")
|
||||
parser.add_option('-s', dest='standards_filter', action='append',
|
||||
help="limit standards to specific names")
|
||||
parser.add_option('-v', dest='log_level', action="store_const", default=logging.WARN,
|
||||
const=logging.INFO, help="Show more verbose output")
|
||||
args = parser.parse_args().__dict__
|
||||
|
||||
options, args = parser.parse_args(sys.argv[1:])
|
||||
settings = read_config(options.configfile)
|
||||
settings = base.get_settings(args)
|
||||
config = settings.config
|
||||
|
||||
# Merge CLI options with config options. CLI options override config options.
|
||||
for key, value in options.__dict__.items():
|
||||
if value:
|
||||
setattr(settings, key, value)
|
||||
logger.update_logger(LOG, config["logging"]["level"], config["logging"]["json"])
|
||||
|
||||
if os.path.exists(settings.configfile):
|
||||
info("Using configuration file: %s" % settings.configfile, settings)
|
||||
else:
|
||||
warn("No configuration file found at %s" % settings.configfile, settings, file=sys.stderr)
|
||||
if not settings.rulesdir:
|
||||
rules_dir = os.path.join(resource_filename('ansiblelater', 'examples'))
|
||||
warn("Using example standards found at %s" % rules_dir, settings, file=sys.stderr)
|
||||
settings.rulesdir = rules_dir
|
||||
files = config["rules"]["files"]
|
||||
standards = base.get_standards(config["rules"]["standards"])
|
||||
|
||||
if len(args) == 0:
|
||||
candidates = []
|
||||
for root, dirs, files in os.walk("."):
|
||||
for filename in files:
|
||||
candidates.append(os.path.join(root, filename))
|
||||
else:
|
||||
candidates = args
|
||||
|
||||
errors = 0
|
||||
for filename in candidates:
|
||||
if ':' in filename:
|
||||
(filename, lines) = filename.split(":")
|
||||
else:
|
||||
lines = None
|
||||
candidate = classify(filename)
|
||||
workers = multiprocessing.cpu_count() - 2
|
||||
p = multiprocessing.Pool(workers)
|
||||
for filename in files:
|
||||
lines = None
|
||||
candidate = candidates.classify(filename, settings, standards)
|
||||
if candidate:
|
||||
if candidate.binary:
|
||||
info("Not reviewing binary file %s" % filename, settings)
|
||||
LOG.info("Not reviewing binary file %s" % filename)
|
||||
continue
|
||||
if candidate.vault:
|
||||
info("Not reviewing vault file %s" % filename, settings)
|
||||
LOG.info("Not reviewing vault file %s" % filename)
|
||||
continue
|
||||
if lines:
|
||||
info("Reviewing %s lines %s" % (candidate, lines), settings)
|
||||
LOG.info("Reviewing %s lines %s" % (candidate, lines))
|
||||
else:
|
||||
info("Reviewing all of %s" % candidate, settings)
|
||||
errors = errors + candidate.review(settings, lines)
|
||||
LOG.info("Reviewing all of %s" % candidate)
|
||||
p.imap(candidate.review, (settings, lines,))
|
||||
else:
|
||||
info("Couldn't classify file %s" % filename, settings)
|
||||
return errors
|
||||
LOG.info("Couldn't classify file %s" % filename)
|
||||
|
||||
p.close()
|
||||
p.join()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
# noqa
|
|
@ -0,0 +1,59 @@
|
|||
"""Base methods."""
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
import ansible
|
||||
import toolz
|
||||
|
||||
from ansiblelater import settings
|
||||
from ansiblelater import utils
|
||||
|
||||
|
||||
def get_settings(args):
|
||||
"""
|
||||
Get new settings object.
|
||||
|
||||
:param args: cli args from argparse
|
||||
:returns: Settings object
|
||||
|
||||
"""
|
||||
config = settings.Settings(
|
||||
args=args,
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def get_standards(filepath):
|
||||
sys.path.append(os.path.abspath(os.path.expanduser(filepath)))
|
||||
try:
|
||||
standards = importlib.import_module("standards")
|
||||
except ImportError as e:
|
||||
utils.sysexit_with_message(
|
||||
"Could not import standards from directory %s: %s" % (filepath, str(e)))
|
||||
|
||||
if getattr(standards, "ansible_min_version", None) and \
|
||||
LooseVersion(standards.ansible_min_version) > LooseVersion(ansible.__version__):
|
||||
utils.sysexit_with_message("Standards require ansible version %s (current version %s). "
|
||||
"Please upgrade ansible." %
|
||||
(standards.ansible_min_version, ansible.__version__))
|
||||
|
||||
if getattr(standards, "ansible_review_min_version", None) and \
|
||||
LooseVersion(standards.ansible_review_min_version) > LooseVersion(
|
||||
utils.get_property("__version__")):
|
||||
utils.sysexit_with_message(
|
||||
"Standards require ansible-later version %s (current version %s). "
|
||||
"Please upgrade ansible-later." %
|
||||
(standards.ansible_review_min_version, utils.get_property("__version__")))
|
||||
|
||||
normalized_std = (list(toolz.remove(lambda x: x.id == "", standards.standards)))
|
||||
unique_std = len(list(toolz.unique(normalized_std, key=lambda x: x.id)))
|
||||
all_std = len(normalized_std)
|
||||
if not all_std == unique_std:
|
||||
utils.sysexit_with_message(
|
||||
"Detect duplicate ID's in standards definition. Please use unique ID's only.")
|
||||
|
||||
return standards.standards
|
|
@ -0,0 +1,322 @@
|
|||
"""Review candidates."""
|
||||
|
||||
import codecs
|
||||
import copy
|
||||
import os
|
||||
import re
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
from six import iteritems
|
||||
|
||||
from ansiblelater import LOG
|
||||
from ansiblelater import utils
|
||||
from ansiblelater.logger import flag_extra
|
||||
|
||||
try:
|
||||
# Ansible 2.4 import of module loader
|
||||
from ansible.plugins.loader import module_loader
|
||||
except ImportError:
|
||||
try:
|
||||
from ansible.plugins import module_loader
|
||||
except ImportError:
|
||||
from ansible.utils import module_finder as module_loader
|
||||
|
||||
|
||||
class Candidate(object):
|
||||
"""
|
||||
Meta object for all files which later has to process.
|
||||
|
||||
Each file passed to later will be classified by type and
|
||||
bundled with necessary meta informations for rule processing.
|
||||
"""
|
||||
|
||||
def __init__(self, filename, settings={}, standards=[]):
|
||||
self.path = filename
|
||||
self.binary = False
|
||||
self.vault = False
|
||||
self.filetype = type(self).__name__.lower()
|
||||
self.expected_version = True
|
||||
self.standards = self._get_standards(settings, standards)
|
||||
|
||||
try:
|
||||
with codecs.open(filename, mode="rb", encoding="utf-8") as f:
|
||||
if f.readline().startswith("$ANSIBLE_VAULT"):
|
||||
self.vault = True
|
||||
except UnicodeDecodeError:
|
||||
self.binary = True
|
||||
|
||||
self.version = self._get_version(settings)
|
||||
|
||||
def _get_version(self, settings):
|
||||
path = self.path
|
||||
version = None
|
||||
|
||||
if not self.binary:
|
||||
if isinstance(self, RoleFile):
|
||||
parentdir = os.path.dirname(os.path.abspath(self.path))
|
||||
while parentdir != os.path.dirname(parentdir):
|
||||
meta_file = os.path.join(parentdir, "meta", "main.yml")
|
||||
if os.path.exists(meta_file):
|
||||
path = meta_file
|
||||
break
|
||||
parentdir = os.path.dirname(parentdir)
|
||||
|
||||
version_re = re.compile(r"^# Standards:\s*([\d.]+)")
|
||||
|
||||
with codecs.open(path, mode="rb", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
match = version_re.match(line)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
|
||||
if not version:
|
||||
version = utils.standards_latest(self.standards)
|
||||
if self.expected_version:
|
||||
if isinstance(self, RoleFile):
|
||||
LOG.warning(
|
||||
"%s %s is in a role that contains a meta/main.yml without a declared "
|
||||
"standards version. "
|
||||
"Using latest standards version %s" %
|
||||
(type(self).__name__, self.path, version))
|
||||
else:
|
||||
LOG.warning(
|
||||
"%s %s does not present standards version. "
|
||||
"Using latest standards version %s" %
|
||||
(type(self).__name__, self.path, version))
|
||||
else:
|
||||
LOG.info("%s %s declares standards version %s" %
|
||||
(type(self).__name__, self.path, version))
|
||||
|
||||
return version
|
||||
|
||||
def _get_standards(self, settings, standards):
|
||||
target_standards = []
|
||||
limits = settings.config["rules"]["filter"]
|
||||
|
||||
if limits:
|
||||
for standard in standards:
|
||||
if standard.id in limits:
|
||||
target_standards.append(standard)
|
||||
else:
|
||||
target_standards = standards
|
||||
|
||||
return target_standards
|
||||
|
||||
def review(self, settings, lines=None):
|
||||
errors = 0
|
||||
|
||||
for standard in self.standards:
|
||||
if type(self).__name__.lower() not in standard.types:
|
||||
continue
|
||||
result = standard.check(self, settings.config)
|
||||
|
||||
if not result:
|
||||
utils.sysexit_with_message("Standard '{}' returns an empty result object.".format(
|
||||
standard.check.__name__))
|
||||
|
||||
labels = {"tag": "review", "standard": standard.name, "file": self.path, "passed": True}
|
||||
|
||||
if standard.id and standard.id.strip():
|
||||
labels["id"] = standard.id
|
||||
|
||||
for err in [err for err in result.errors
|
||||
if not err.lineno or utils.is_line_in_ranges(err.lineno, utils.lines_ranges(lines))]: # noqa
|
||||
err_labels = copy.copy(labels)
|
||||
err_labels["passed"] = False
|
||||
if isinstance(err, Error):
|
||||
err_labels.update(err.to_dict())
|
||||
|
||||
if not standard.version:
|
||||
LOG.warning("{id}Best practice '{name}' not met:\n{path}:{error}".format(
|
||||
id=self._format_id(standard.id),
|
||||
name=standard.name,
|
||||
path=self.path,
|
||||
error=err), extra=flag_extra(err_labels))
|
||||
elif LooseVersion(standard.version) > LooseVersion(self.version):
|
||||
LOG.warning("{id}Future standard '{name}' not met:\n{path}:{error}".format(
|
||||
id=self._format_id(standard.id),
|
||||
name=standard.name,
|
||||
path=self.path,
|
||||
error=err), extra=flag_extra(err_labels))
|
||||
else:
|
||||
LOG.error("{id}Standard '{name}' not met:\n{path}:{error}".format(
|
||||
id=self._format_id(standard.id),
|
||||
name=standard.name,
|
||||
path=self.path,
|
||||
error=err), extra=flag_extra(err_labels))
|
||||
errors = errors + 1
|
||||
|
||||
def _format_id(self, standard_id):
|
||||
if standard_id and standard_id.strip():
|
||||
standard_id = "[{id}] ".format(id=standard_id.strip())
|
||||
|
||||
return standard_id
|
||||
|
||||
def __repr__(self): # noqa
|
||||
return "%s (%s)" % (type(self).__name__, self.path)
|
||||
|
||||
def __getitem__(self, item): # noqa
|
||||
return self.__dict__.get(item)
|
||||
|
||||
|
||||
class RoleFile(Candidate):
|
||||
def __init__(self, filename, settings={}, standards=[]):
|
||||
super(RoleFile, self).__init__(filename, settings, standards)
|
||||
|
||||
parentdir = os.path.dirname(os.path.abspath(filename))
|
||||
while parentdir != os.path.dirname(parentdir):
|
||||
role_modules = os.path.join(parentdir, "library")
|
||||
if os.path.exists(role_modules):
|
||||
module_loader.add_directory(role_modules)
|
||||
break
|
||||
parentdir = os.path.dirname(parentdir)
|
||||
|
||||
|
||||
class Playbook(Candidate):
|
||||
pass
|
||||
|
||||
|
||||
class Task(RoleFile):
|
||||
def __init__(self, filename, settings={}, standards=[]):
|
||||
super(Task, self).__init__(filename, settings, standards)
|
||||
self.filetype = "tasks"
|
||||
|
||||
|
||||
class Handler(RoleFile):
|
||||
def __init__(self, filename, settings={}, standards=[]):
|
||||
super(Handler, self).__init__(filename, settings, standards)
|
||||
self.filetype = "handlers"
|
||||
|
||||
|
||||
class Vars(Candidate):
|
||||
pass
|
||||
|
||||
|
||||
class Unversioned(Candidate):
|
||||
def __init__(self, filename, settings={}, standards=[]):
|
||||
super(Unversioned, self).__init__(filename, settings, standards)
|
||||
self.expected_version = False
|
||||
|
||||
|
||||
class InventoryVars(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
class HostVars(InventoryVars):
|
||||
pass
|
||||
|
||||
|
||||
class GroupVars(InventoryVars):
|
||||
pass
|
||||
|
||||
|
||||
class RoleVars(RoleFile):
|
||||
pass
|
||||
|
||||
|
||||
class Meta(RoleFile):
|
||||
pass
|
||||
|
||||
|
||||
class Inventory(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
class Code(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
class Template(RoleFile):
|
||||
pass
|
||||
|
||||
|
||||
class Doc(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
class Makefile(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
class File(RoleFile):
|
||||
pass
|
||||
|
||||
|
||||
class Rolesfile(Unversioned):
|
||||
pass
|
||||
|
||||
|
||||
class Error(object):
|
||||
"""Default error object created if a rule failed."""
|
||||
|
||||
def __init__(self, lineno, message, error_type=None, **kwargs):
|
||||
"""
|
||||
Initialize a new error object and returns None.
|
||||
|
||||
:param lineno: Line number where the error from de rule occures
|
||||
:param message: Detailed error description provided by the rule
|
||||
|
||||
"""
|
||||
self.lineno = lineno
|
||||
self.message = message
|
||||
self.kwargs = kwargs
|
||||
for (key, value) in iteritems(kwargs):
|
||||
setattr(self, key, value)
|
||||
|
||||
def __repr__(self): # noqa
|
||||
if self.lineno:
|
||||
return "%s: %s" % (self.lineno, self.message)
|
||||
else:
|
||||
return " %s" % (self.message)
|
||||
|
||||
def to_dict(self):
|
||||
result = dict(lineno=self.lineno, message=self.message)
|
||||
for (key, value) in iteritems(self.kwargs):
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
|
||||
class Result(object):
|
||||
def __init__(self, candidate, errors=None):
|
||||
self.candidate = candidate
|
||||
self.errors = errors or []
|
||||
|
||||
def message(self):
|
||||
return "\n".join(["{0}:{1}".format(self.candidate, error)
|
||||
for error in self.errors])
|
||||
|
||||
|
||||
def classify(filename, settings={}, standards=[]):
|
||||
parentdir = os.path.basename(os.path.dirname(filename))
|
||||
basename = os.path.basename(filename)
|
||||
|
||||
if parentdir in ["tasks"]:
|
||||
return Task(filename, settings, standards)
|
||||
if parentdir in ["handlers"]:
|
||||
return Handler(filename, settings, standards)
|
||||
if parentdir in ["vars", "defaults"]:
|
||||
return RoleVars(filename, settings, standards)
|
||||
if "group_vars" in filename.split(os.sep):
|
||||
return GroupVars(filename, settings, standards)
|
||||
if "host_vars" in filename.split(os.sep):
|
||||
return HostVars(filename, settings, standards)
|
||||
if parentdir in ["meta"]:
|
||||
return Meta(filename, settings, standards)
|
||||
if parentdir in ["library", "lookup_plugins", "callback_plugins",
|
||||
"filter_plugins"] or filename.endswith(".py"):
|
||||
return Code(filename, settings, standards)
|
||||
if "inventory" == basename or "hosts" == basename or parentdir in ["inventories"]:
|
||||
return Inventory(filename, settings, standards)
|
||||
if "rolesfile" in basename or "requirements" in basename:
|
||||
return Rolesfile(filename, settings, standards)
|
||||
if "Makefile" in basename:
|
||||
return Makefile(filename, settings, standards)
|
||||
if "templates" in filename.split(os.sep) or basename.endswith(".j2"):
|
||||
return Template(filename, settings, standards)
|
||||
if "files" in filename.split(os.sep):
|
||||
return File(filename, settings, standards)
|
||||
if basename.endswith(".yml") or basename.endswith(".yaml"):
|
||||
return Playbook(filename, settings, standards)
|
||||
if "README" in basename:
|
||||
return Doc(filename, settings, standards)
|
||||
return None
|
|
@ -1,30 +1,30 @@
|
|||
from ansiblelater import Standard
|
||||
"""Example standards definition."""
|
||||
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_empty_lines
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_indent
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_hyphens
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_document_start
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_colons
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_file
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_has_content
|
||||
from ansiblelater.rules.yamlfiles import check_native_yaml
|
||||
from ansiblelater.rules.taskfiles import check_line_between_tasks
|
||||
from ansiblelater.rules.ansiblefiles import check_become_user
|
||||
from ansiblelater.rules.ansiblefiles import check_braces_spaces
|
||||
from ansiblelater.rules.ansiblefiles import check_command_has_changes
|
||||
from ansiblelater.rules.ansiblefiles import check_command_instead_of_module
|
||||
from ansiblelater.rules.ansiblefiles import check_compare_to_literal_bool
|
||||
from ansiblelater.rules.ansiblefiles import check_empty_string_compare
|
||||
from ansiblelater.rules.ansiblefiles import check_filter_separation
|
||||
from ansiblelater.rules.ansiblefiles import check_install_use_latest
|
||||
from ansiblelater.rules.ansiblefiles import check_literal_bool_format
|
||||
from ansiblelater.rules.ansiblefiles import check_name_format
|
||||
from ansiblelater.rules.ansiblefiles import check_named_task
|
||||
from ansiblelater.rules.ansiblefiles import check_shell_instead_command
|
||||
from ansiblelater.rules.ansiblefiles import check_unique_named_task
|
||||
from ansiblelater.rules.rolefiles import check_meta_main
|
||||
from ansiblelater.rules.rolefiles import check_scm_in_src
|
||||
from ansiblelater.rules.ansiblefiles import check_unique_named_task
|
||||
from ansiblelater.rules.ansiblefiles import check_named_task
|
||||
from ansiblelater.rules.ansiblefiles import check_name_format
|
||||
from ansiblelater.rules.ansiblefiles import check_braces_spaces
|
||||
from ansiblelater.rules.ansiblefiles import check_command_instead_of_module
|
||||
from ansiblelater.rules.ansiblefiles import check_install_use_latest
|
||||
from ansiblelater.rules.ansiblefiles import check_shell_instead_command
|
||||
from ansiblelater.rules.ansiblefiles import check_command_has_changes
|
||||
from ansiblelater.rules.ansiblefiles import check_empty_string_compare
|
||||
from ansiblelater.rules.ansiblefiles import check_compare_to_literal_bool
|
||||
from ansiblelater.rules.ansiblefiles import check_literal_bool_format
|
||||
from ansiblelater.rules.ansiblefiles import check_become_user
|
||||
from ansiblelater.rules.ansiblefiles import check_filter_separation
|
||||
|
||||
from ansiblelater.rules.taskfiles import check_line_between_tasks
|
||||
from ansiblelater.rules.yamlfiles import check_native_yaml
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_colons
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_document_start
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_empty_lines
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_file
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_has_content
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_hyphens
|
||||
from ansiblelater.rules.yamlfiles import check_yaml_indent
|
||||
from ansiblelater.standard import Standard
|
||||
|
||||
tasks_should_be_separated = Standard(dict(
|
||||
id="ANSIBLE0001",
|
||||
|
@ -225,8 +225,8 @@ use_yaml_rather_than_key_value = Standard(dict(
|
|||
))
|
||||
|
||||
|
||||
ansible_min_version = '2.1'
|
||||
ansible_review_min_version = '0.1.0'
|
||||
ansible_min_version = "2.5"
|
||||
ansible_review_min_version = "0.1.0"
|
||||
|
||||
|
||||
standards = [
|
|
@ -1,23 +1,25 @@
|
|||
"""Custom exceptions."""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
# Custom exceptions
|
||||
class LaterError(Exception):
|
||||
"""Generic exception for later"""
|
||||
"""Generic exception for later."""
|
||||
|
||||
def __init__(self, msg, original):
|
||||
"""Initialize new exception."""
|
||||
super(LaterError, self).__init__(msg + (": %s" % original))
|
||||
self.original = original
|
||||
|
||||
|
||||
class LaterAnsibleError(Exception):
|
||||
"""Wrapper for ansible syntax errors"""
|
||||
"""Wrapper for ansible syntax errors."""
|
||||
|
||||
def __init__(self, msg, original):
|
||||
lines = original.message.splitlines()
|
||||
|
||||
line_no = re.search('line(.*?),', lines[2])
|
||||
column_no = re.search('column(.*?),', lines[2])
|
||||
line_no = re.search("line(.*?),", lines[2])
|
||||
column_no = re.search("column(.*?),", lines[2])
|
||||
|
||||
self.message = lines[0]
|
||||
self.line = line_no.group(1).strip()
|
|
@ -0,0 +1,183 @@
|
|||
"""Global logging helpers."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
import colorama
|
||||
from ansible.module_utils.parsing.convert_bool import boolean as to_bool
|
||||
from pythonjsonlogger import jsonlogger
|
||||
from six import iteritems
|
||||
|
||||
CONSOLE_FORMAT = "%(levelname)s: %(message)s"
|
||||
JSON_FORMAT = "(asctime) (levelname) (message)"
|
||||
|
||||
|
||||
def _should_do_markup():
|
||||
|
||||
py_colors = os.environ.get("PY_COLORS", None)
|
||||
if py_colors is not None:
|
||||
return to_bool(py_colors, strict=False)
|
||||
|
||||
return sys.stdout.isatty() and os.environ.get("TERM") != "dumb"
|
||||
|
||||
|
||||
colorama.init(autoreset=True, strip=not _should_do_markup())
|
||||
|
||||
|
||||
def flag_extra(extra):
|
||||
"""Ensure extra args are prefixed."""
|
||||
flagged = dict()
|
||||
|
||||
if isinstance(extra, dict):
|
||||
for key, value in iteritems(extra):
|
||||
flagged["later_" + key] = value
|
||||
|
||||
return flagged
|
||||
|
||||
|
||||
class LogFilter(object):
|
||||
"""A custom log filter which excludes log messages above the logged level."""
|
||||
|
||||
def __init__(self, level):
|
||||
"""
|
||||
Initialize a new custom log filter.
|
||||
|
||||
:param level: Log level limit
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
self.__level = level
|
||||
|
||||
def filter(self, logRecord): # noqa
|
||||
# https://docs.python.org/3/library/logging.html#logrecord-attributes
|
||||
return logRecord.levelno <= self.__level
|
||||
|
||||
|
||||
class MultilineFormatter(logging.Formatter):
|
||||
"""Logging Formatter to reset color after newline characters."""
|
||||
|
||||
def format(self, record): # noqa
|
||||
record.msg = record.msg.replace("\n", "\n{}... ".format(colorama.Style.RESET_ALL))
|
||||
return logging.Formatter.format(self, record)
|
||||
|
||||
|
||||
class MultilineJsonFormatter(jsonlogger.JsonFormatter):
|
||||
"""Logging Formatter to remove newline characters."""
|
||||
|
||||
def format(self, record): # noqa
|
||||
record.msg = record.msg.replace("\n", " ")
|
||||
return jsonlogger.JsonFormatter.format(self, record)
|
||||
|
||||
|
||||
def get_logger(name=None, level=logging.DEBUG, json=False):
|
||||
"""
|
||||
Build a logger with the given name and returns the logger.
|
||||
|
||||
:param name: The name for the logger. This is usually the module name, `__name__`.
|
||||
:param level: Initialize the new logger with given log level.
|
||||
:param json: Boolean flag to enable json formatted log output.
|
||||
:return: logger object
|
||||
|
||||
"""
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(level)
|
||||
logger.addHandler(_get_error_handler(json=json))
|
||||
logger.addHandler(_get_warn_handler(json=json))
|
||||
logger.addHandler(_get_info_handler(json=json))
|
||||
logger.addHandler(_get_critical_handler(json=json))
|
||||
logger.propagate = False
|
||||
|
||||
return logger
|
||||
|
||||
|
||||
def update_logger(logger, level=None, json=None):
|
||||
"""Update logger configuration to change logging settings."""
|
||||
for handler in logger.handlers[:]:
|
||||
logger.removeHandler(handler)
|
||||
|
||||
logger.setLevel(level)
|
||||
logger.addHandler(_get_error_handler(json=json))
|
||||
logger.addHandler(_get_warn_handler(json=json))
|
||||
logger.addHandler(_get_info_handler(json=json))
|
||||
logger.addHandler(_get_critical_handler(json=json))
|
||||
|
||||
|
||||
def _get_error_handler(json=False):
|
||||
handler = logging.StreamHandler(sys.stderr)
|
||||
handler.setLevel(logging.ERROR)
|
||||
handler.addFilter(LogFilter(logging.ERROR))
|
||||
handler.setFormatter(MultilineFormatter(error(CONSOLE_FORMAT)))
|
||||
|
||||
if json:
|
||||
handler.setFormatter(MultilineJsonFormatter(JSON_FORMAT))
|
||||
|
||||
return handler
|
||||
|
||||
|
||||
def _get_warn_handler(json=False):
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setLevel(logging.WARN)
|
||||
handler.addFilter(LogFilter(logging.WARN))
|
||||
handler.setFormatter(MultilineFormatter(warn(CONSOLE_FORMAT)))
|
||||
|
||||
if json:
|
||||
handler.setFormatter(MultilineJsonFormatter(JSON_FORMAT))
|
||||
|
||||
return handler
|
||||
|
||||
|
||||
def _get_info_handler(json=False):
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setLevel(logging.INFO)
|
||||
handler.addFilter(LogFilter(logging.INFO))
|
||||
handler.setFormatter(MultilineFormatter(info(CONSOLE_FORMAT)))
|
||||
|
||||
if json:
|
||||
handler.setFormatter(MultilineJsonFormatter(JSON_FORMAT))
|
||||
|
||||
return handler
|
||||
|
||||
|
||||
def _get_critical_handler(json=False):
|
||||
handler = logging.StreamHandler(sys.stderr)
|
||||
handler.setLevel(logging.CRITICAL)
|
||||
handler.addFilter(LogFilter(logging.CRITICAL))
|
||||
handler.setFormatter(MultilineFormatter(critical(CONSOLE_FORMAT)))
|
||||
|
||||
if json:
|
||||
handler.setFormatter(MultilineJsonFormatter(JSON_FORMAT))
|
||||
|
||||
return handler
|
||||
|
||||
|
||||
def critical(message):
|
||||
"""Format critical messages and return string."""
|
||||
return color_text(colorama.Fore.RED, "{}".format(message))
|
||||
|
||||
|
||||
def error(message):
|
||||
"""Format error messages and return string."""
|
||||
return color_text(colorama.Fore.RED, "{}".format(message))
|
||||
|
||||
|
||||
def warn(message):
|
||||
"""Format warn messages and return string."""
|
||||
return color_text(colorama.Fore.YELLOW, "{}".format(message))
|
||||
|
||||
|
||||
def info(message):
|
||||
"""Format info messages and return string."""
|
||||
return color_text(colorama.Fore.BLUE, "{}".format(message))
|
||||
|
||||
|
||||
def color_text(color, msg):
|
||||
"""
|
||||
Colorize strings.
|
||||
|
||||
:param color: colorama color settings
|
||||
:param msg: string to colorize
|
||||
:returns: string
|
||||
|
||||
"""
|
||||
return "{}{}{}".format(color, msg, colorama.Style.RESET_ALL)
|
|
@ -0,0 +1 @@
|
|||
# noqa
|
|
@ -1,16 +1,21 @@
|
|||
import re
|
||||
import os
|
||||
"""Checks related to ansible specific best practices."""
|
||||
|
||||
import os
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from ansiblelater import Result, Error
|
||||
|
||||
from ansiblelater.command.candidates import Error
|
||||
from ansiblelater.command.candidates import Result
|
||||
from ansiblelater.utils import count_spaces
|
||||
from ansiblelater.utils.rulehelper import (get_normalized_tasks,
|
||||
get_normalized_yaml)
|
||||
from ansiblelater.utils.rulehelper import get_normalized_tasks
|
||||
from ansiblelater.utils.rulehelper import get_normalized_yaml
|
||||
|
||||
|
||||
def check_braces_spaces(candidate, settings):
|
||||
yamllines, errors = get_normalized_yaml(candidate, settings)
|
||||
description = "no suitable numbers of spaces (required: 1)"
|
||||
conf = settings["ansible"]["double-braces"]
|
||||
description = "no suitable numbers of spaces (min: {min} max: {max})".format(
|
||||
min=conf["min-spaces-inside"], max=conf["max-spaces-inside"])
|
||||
|
||||
matches = []
|
||||
braces = re.compile("{{(.*?)}}")
|
||||
|
@ -23,25 +28,29 @@ def check_braces_spaces(candidate, settings):
|
|||
matches.append((i, item))
|
||||
|
||||
for i, line in matches:
|
||||
leading, trailing = count_spaces(line)
|
||||
[leading, trailing] = count_spaces(line)
|
||||
sum_spaces = leading + trailing
|
||||
|
||||
if not leading == 1 or not trailing == 1:
|
||||
if (
|
||||
(sum_spaces < conf["min-spaces-inside"] * 2)
|
||||
or (sum_spaces > conf["min-spaces-inside"] * 2)
|
||||
):
|
||||
errors.append(Error(i, description))
|
||||
return Result(candidate.path, errors)
|
||||
|
||||
|
||||
def check_named_task(candidate, settings):
|
||||
tasks, errors = get_normalized_tasks(candidate, settings)
|
||||
nameless_tasks = ['meta', 'debug', 'include_role', 'import_role',
|
||||
'include_tasks', 'import_tasks', 'include_vars',
|
||||
'block']
|
||||
nameless_tasks = ["meta", "debug", "include_role", "import_role",
|
||||
"include_tasks", "import_tasks", "include_vars",
|
||||
"block"]
|
||||
description = "module '%s' used without name attribute"
|
||||
|
||||
if not errors:
|
||||
for task in tasks:
|
||||
module = task["action"]["__ansible_module__"]
|
||||
if 'name' not in task and module not in nameless_tasks:
|
||||
errors.append(Error(task['__line__'], description % module))
|
||||
if "name" not in task and module not in nameless_tasks:
|
||||
errors.append(Error(task["__line__"], description % module))
|
||||
|
||||
return Result(candidate.path, errors)
|
||||
|
||||
|
@ -53,8 +62,8 @@ def check_name_format(candidate, settings):
|
|||
|
||||
if not errors:
|
||||
for task in tasks:
|
||||
if 'name' in task:
|
||||
namelines[task['name']].append(task['__line__'])
|
||||
if "name" in task:
|
||||
namelines[task["name"]].append(task["__line__"])
|
||||
for (name, lines) in namelines.items():
|
||||
if not name[0].isupper():
|
||||
errors.append(Error(lines[-1], description % name))
|
||||
|
@ -70,8 +79,8 @@ def check_unique_named_task(candidate, settings):
|
|||
|
||||
if not errors:
|
||||
for task in tasks:
|
||||
if 'name' in task:
|
||||
namelines[task['name']].append(task['__line__'])
|
||||
if "name" in task:
|
||||
namelines[task["name"]].append(task["__line__"])
|
||||
for (name, lines) in namelines.items():
|
||||
if len(lines) > 1:
|
||||
errors.append(Error(lines[-1], description % name))
|
||||
|
@ -81,28 +90,28 @@ def check_unique_named_task(candidate, settings):
|
|||
|
||||
def check_command_instead_of_module(candidate, settings):
|
||||
tasks, errors = get_normalized_tasks(candidate, settings)
|
||||
commands = ['command', 'shell', 'raw']
|
||||
commands = ["command", "shell", "raw"]
|
||||
modules = {
|
||||
'git': 'git', 'hg': 'hg', 'curl': 'get_url or uri', 'wget': 'get_url or uri',
|
||||
'svn': 'subversion', 'service': 'service', 'mount': 'mount',
|
||||
'rpm': 'yum or rpm_key', 'yum': 'yum', 'apt-get': 'apt-get',
|
||||
'unzip': 'unarchive', 'tar': 'unarchive', 'chkconfig': 'service',
|
||||
'rsync': 'synchronize', 'supervisorctl': 'supervisorctl', 'systemctl': 'systemd',
|
||||
'sed': 'template or lineinfile'
|
||||
"git": "git", "hg": "hg", "curl": "get_url or uri", "wget": "get_url or uri",
|
||||
"svn": "subversion", "service": "service", "mount": "mount",
|
||||
"rpm": "yum or rpm_key", "yum": "yum", "apt-get": "apt-get",
|
||||
"unzip": "unarchive", "tar": "unarchive", "chkconfig": "service",
|
||||
"rsync": "synchronize", "supervisorctl": "supervisorctl", "systemctl": "systemd",
|
||||
"sed": "template or lineinfile"
|
||||
}
|
||||
description = "%s command used in place of %s module"
|
||||
|
||||
if not errors:
|
||||
for task in tasks:
|
||||
if task["action"]["__ansible_module__"] in commands:
|
||||
if 'cmd' in task['action']:
|
||||
if "cmd" in task["action"]:
|
||||
first_cmd_arg = task["action"]["cmd"].split()[0]
|
||||
else:
|
||||
first_cmd_arg = task["action"]["__ansible_arguments__"][0]
|
||||
|
||||
executable = os.path.basename(first_cmd_arg)
|
||||
if (first_cmd_arg and executable in modules
|
||||
and task['action'].get('warn', True) and 'register' not in task):
|
||||
and task["action"].get("warn", True) and "register" not in task):
|
||||
errors.append(
|
||||
Error(task["__line__"], description % (executable, modules[executable])))
|
||||
|
||||
|
@ -111,10 +120,10 @@ def check_command_instead_of_module(candidate, settings):
|
|||
|
||||
def check_install_use_latest(candidate, settings):
|
||||
tasks, errors = get_normalized_tasks(candidate, settings)
|
||||
package_managers = ['yum', 'apt', 'dnf', 'homebrew', 'pacman', 'openbsd_package', 'pkg5',
|
||||
'portage', 'pkgutil', 'slackpkg', 'swdepot', 'zypper', 'bundler', 'pip',
|
||||
'pear', 'npm', 'yarn', 'gem', 'easy_install', 'bower', 'package', 'apk',
|
||||
'openbsd_pkg', 'pkgng', 'sorcery', 'xbps']
|
||||
package_managers = ["yum", "apt", "dnf", "homebrew", "pacman", "openbsd_package", "pkg5",
|
||||
"portage", "pkgutil", "slackpkg", "swdepot", "zypper", "bundler", "pip",
|
||||
"pear", "npm", "yarn", "gem", "easy_install", "bower", "package", "apk",
|
||||
"openbsd_pkg", "pkgng", "sorcery", "xbps"]
|
||||
description = "package installs should use state=present with or without a version"
|
||||
|
||||
if not errors:
|
||||
|
@ -132,14 +141,14 @@ def check_shell_instead_command(candidate, settings):
|
|||
|
||||
if not errors:
|
||||
for task in tasks:
|
||||
if task["action"]["__ansible_module__"] == 'shell':
|
||||
if 'cmd' in task['action']:
|
||||
if task["action"]["__ansible_module__"] == "shell":
|
||||
if "cmd" in task["action"]:
|
||||
cmd = task["action"].get("cmd", [])
|
||||
else:
|
||||
cmd = ' '.join(task["action"].get("__ansible_arguments__", []))
|
||||
cmd = " ".join(task["action"].get("__ansible_arguments__", []))
|
||||
|
||||
unjinja = re.sub(r"\{\{[^\}]*\}\}", "JINJA_VAR", cmd)
|
||||
if not any([ch in unjinja for ch in '&|<>;$\n*[]{}?']):
|
||||
if not any([ch in unjinja for ch in "&|<>;$\n*[]{}?"]):
|
||||
errors.append(Error(task["__line__"], description))
|
||||
|
||||
return Result(candidate.path, errors)
|
||||
|
@ -147,7 +156,7 @@ def check_shell_instead_command(candidate, settings):
|
|||
|
||||
def check_command_has_changes(candidate, settings):
|
||||
tasks, errors = get_normalized_tasks(candidate, settings)
|
||||
commands = ['command', 'shell', 'raw']
|
||||
commands = ["command", "shell", "raw"]
|
||||
description = "commands should either read information (and thus set changed_when) or not " \
|
||||
"do something if it has already been done (using creates/removes) " \
|
||||
"or only do it if another check has a particular result (when)"
|
||||
|
@ -155,10 +164,10 @@ def check_command_has_changes(candidate, settings):
|
|||
if not errors:
|
||||
for task in tasks:
|
||||
if task["action"]["__ansible_module__"] in commands:
|
||||
if ('changed_when' not in task and 'when' not in task
|
||||
and 'when' not in task['__ansible_action_meta__']
|
||||
and 'creates' not in task['action']
|
||||
and 'removes' not in task['action']):
|
||||
if ("changed_when" not in task and "when" not in task
|
||||
and "when" not in task["__ansible_action_meta__"]
|
||||
and "creates" not in task["action"]
|
||||
and "removes" not in task["action"]):
|
||||
errors.append(Error(task["__line__"], description))
|
||||
|
||||
return Result(candidate.path, errors)
|
||||
|
@ -166,8 +175,8 @@ def check_command_has_changes(candidate, settings):
|
|||
|
||||
def check_empty_string_compare(candidate, settings):
|
||||
yamllines, errors = get_normalized_yaml(candidate, settings)
|
||||
description = 'use `when: var` rather than `when: var != ""` (or ' \
|
||||
'conversely `when: not var` rather than `when: var == ""`)'
|
||||
description = "use `when: var` rather than `when: var != ""` (or " \
|
||||
"conversely `when: not var` rather than `when: var == ""`)"
|
||||
|
||||
empty_string_compare = re.compile("[=!]= ?[\"'][\"']")
|
||||
|
||||
|
@ -202,7 +211,7 @@ def check_delegate_to_localhost(candidate, settings):
|
|||
|
||||
if not errors:
|
||||
for task in tasks:
|
||||
if task.get('delegate_to') == 'localhost':
|
||||
if task.get("delegate_to") == "localhost":
|
||||
errors.append(Error(task["__line__"], description))
|
||||
|
||||
return Result(candidate.path, errors)
|
||||
|
@ -225,12 +234,12 @@ def check_literal_bool_format(candidate, settings):
|
|||
def check_become_user(candidate, settings):
|
||||
tasks, errors = get_normalized_tasks(candidate, settings)
|
||||
description = "the task has 'become:' enabled but 'become_user:' is missing"
|
||||
true_value = [True, 'true', 'True', 'TRUE', 'yes', 'Yes', 'YES']
|
||||
true_value = [True, "true", "True", "TRUE", "yes", "Yes", "YES"]
|
||||
|
||||
if not errors:
|
||||
gen = (task for task in tasks if 'become' in task)
|
||||
gen = (task for task in tasks if "become" in task)
|
||||
for task in gen:
|
||||
if task["become"] in true_value and 'become_user' not in task.keys():
|
||||
if task["become"] in true_value and "become_user" not in task.keys():
|
||||
errors.append(Error(task["__line__"], description))
|
||||
|
||||
return Result(candidate.path, errors)
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
"""Checks related to ansible roles files."""
|
||||
|
||||
from nested_lookup import nested_lookup
|
||||
|
||||
from ansiblelater import Error, Result
|
||||
from ansiblelater.utils.rulehelper import get_raw_yaml, get_tasks
|
||||
from ansiblelater.command.candidates import Error
|
||||
from ansiblelater.command.candidates import Result
|
||||
from ansiblelater.utils.rulehelper import get_raw_yaml
|
||||
from ansiblelater.utils.rulehelper import get_tasks
|
||||
|
||||
|
||||
def check_meta_main(candidate, settings):
|
||||
|
@ -23,7 +27,7 @@ def check_scm_in_src(candidate, settings):
|
|||
|
||||
if not errors:
|
||||
for role in roles:
|
||||
if '+' in role.get('src'):
|
||||
errors.append(Error(role['__line__'], description))
|
||||
if "+" in role.get("src"):
|
||||
errors.append(Error(role["__line__"], description))
|
||||
|
||||
return Result(candidate.path, errors)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import re
|
||||
"""Checks related to ansible task files."""
|
||||
|
||||
import re
|
||||
from collections import defaultdict
|
||||
|
||||
from ansiblelater import Error, Result
|
||||
from ansiblelater.command.candidates import Error
|
||||
from ansiblelater.command.candidates import Result
|
||||
from ansiblelater.utils.rulehelper import get_normalized_yaml
|
||||
|
||||
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
"""Checks related to generic YAML syntax (yamllint)."""
|
||||
|
||||
import codecs
|
||||
import yaml
|
||||
import os
|
||||
|
||||
from ansiblelater import Result
|
||||
from ansiblelater import Error
|
||||
import yaml
|
||||
|
||||
from ansiblelater.command.candidates import Error
|
||||
from ansiblelater.command.candidates import Result
|
||||
from ansiblelater.utils.rulehelper import get_action_tasks
|
||||
from ansiblelater.utils.rulehelper import get_normalized_yaml
|
||||
from ansiblelater.utils.rulehelper import get_normalized_task
|
||||
from ansiblelater.utils.rulehelper import get_normalized_yaml
|
||||
from ansiblelater.utils.rulehelper import run_yamllint
|
||||
|
||||
|
||||
|
@ -31,46 +34,50 @@ def check_native_yaml(candidate, settings):
|
|||
errors.extend(error)
|
||||
break
|
||||
|
||||
action = normal_form['action']['__ansible_module__']
|
||||
arguments = normal_form['action']['__ansible_arguments__']
|
||||
# Cope with `set_fact` where task['set_fact'] is None
|
||||
action = normal_form["action"]["__ansible_module__"]
|
||||
arguments = normal_form["action"]["__ansible_arguments__"]
|
||||
# Cope with `set_fact` where task["set_fact"] is None
|
||||
if not task.get(action):
|
||||
continue
|
||||
if isinstance(task[action], dict):
|
||||
continue
|
||||
# strip additional newlines off task[action]
|
||||
if task[action].strip().split() != arguments:
|
||||
errors.append(Error(task['__line__'], description))
|
||||
errors.append(Error(task["__line__"], description))
|
||||
return Result(candidate.path, errors)
|
||||
|
||||
|
||||
def check_yaml_empty_lines(candidate, settings):
|
||||
options = "rules: {empty-lines: {max: 1, max-start: 0, max-end: 1}}"
|
||||
errors = run_yamllint(candidate, settings, options)
|
||||
options = "rules: {{empty-lines: {conf}}}".format(
|
||||
conf=settings["yamllint"]["empty-lines"])
|
||||
errors = run_yamllint(candidate.path, options)
|
||||
return Result(candidate.path, errors)
|
||||
|
||||
|
||||
def check_yaml_indent(candidate, settings):
|
||||
options = "rules: {indentation: {spaces: 2, check-multi-line-strings: false, indent-sequences: true}}"
|
||||
errors = run_yamllint(candidate, settings, options)
|
||||
options = "rules: {{indentation: {conf}}}".format(
|
||||
conf=settings["yamllint"]["indentation"])
|
||||
errors = run_yamllint(candidate.path, options)
|
||||
return Result(candidate.path, errors)
|
||||
|
||||
|
||||
def check_yaml_hyphens(candidate, settings):
|
||||
options = "rules: {hyphens: {max-spaces-after: 1}}"
|
||||
errors = run_yamllint(candidate, settings, options)
|
||||
options = "rules: {{hyphens: {conf}}}".format(
|
||||
conf=settings["yamllint"]["hyphens"])
|
||||
errors = run_yamllint(candidate.path, options)
|
||||
return Result(candidate.path, errors)
|
||||
|
||||
|
||||
def check_yaml_document_start(candidate, settings):
|
||||
options = "rules: {document-start: {present: true}}"
|
||||
errors = run_yamllint(candidate, settings, options)
|
||||
options = "rules: {{document-start: {conf}}}".format(
|
||||
conf=settings["yamllint"]["document-start"])
|
||||
errors = run_yamllint(candidate.path, options)
|
||||
return Result(candidate.path, errors)
|
||||
|
||||
|
||||
def check_yaml_colons(candidate, settings):
|
||||
options = "rules: {colons: {max-spaces-before: 0, max-spaces-after: 1}}"
|
||||
errors = run_yamllint(candidate, settings, options)
|
||||
options = "rules: {{colons: {conf}}}"
|
||||
errors = run_yamllint(candidate.path, options)
|
||||
return Result(candidate.path, errors)
|
||||
|
||||
|
||||
|
@ -82,7 +89,7 @@ def check_yaml_file(candidate, settings):
|
|||
errors.append(
|
||||
Error(None, "file does not have a .yml extension"))
|
||||
elif os.path.isfile(filename) and os.path.splitext(filename)[1][1:] == "yml":
|
||||
with codecs.open(filename, mode='rb', encoding='utf-8') as f:
|
||||
with codecs.open(filename, mode="rb", encoding="utf-8") as f:
|
||||
try:
|
||||
yaml.safe_load(f)
|
||||
except Exception as e:
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
"""Global settings object definition."""
|
||||
|
||||
import os
|
||||
|
||||
import anyconfig
|
||||
import pathspec
|
||||
from appdirs import AppDirs
|
||||
from jsonschema._utils import format_as_index
|
||||
from pkg_resources import resource_filename
|
||||
|
||||
from ansiblelater import utils
|
||||
|
||||
config_dir = AppDirs("ansible-later").user_config_dir
|
||||
default_config_file = os.path.join(config_dir, "config.yml")
|
||||
|
||||
|
||||
class Settings(object):
|
||||
"""
|
||||
Create an object with all necessary settings.
|
||||
|
||||
Settings are loade from multiple locations in defined order (last wins):
|
||||
- default settings defined by `self._get_defaults()`
|
||||
- yaml config file, defaults to OS specific user config dir (https://pypi.org/project/appdirs/)
|
||||
- provides cli parameters
|
||||
"""
|
||||
|
||||
def __init__(self, args={}, config_file=default_config_file):
|
||||
"""
|
||||
Initialize a new settings class.
|
||||
|
||||
:param args: An optional dict of options, arguments and commands from the CLI.
|
||||
:param config_file: An optional path to a yaml config file.
|
||||
:returns: None
|
||||
|
||||
"""
|
||||
self.config_file = config_file
|
||||
self.schema = None
|
||||
self.args_files = False
|
||||
self.args = self._set_args(args)
|
||||
self.config = self._get_config()
|
||||
self._update_filelist()
|
||||
|
||||
def _set_args(self, args):
|
||||
defaults = self._get_defaults()
|
||||
self.config_file = args.get("config_file") or default_config_file
|
||||
|
||||
args.pop("config_file", None)
|
||||
tmp_args = dict(filter(lambda item: item[1] is not None, args.items()))
|
||||
|
||||
tmp_dict = {}
|
||||
for key, value in tmp_args.items():
|
||||
tmp_dict = utils.add_dict_branch(tmp_dict, key.split("."), value)
|
||||
|
||||
# Override correct log level from argparse
|
||||
levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
||||
log_level = levels.index(defaults["logging"]["level"])
|
||||
if tmp_dict.get("logging"):
|
||||
for adjustment in tmp_dict["logging"]["level"]:
|
||||
log_level = min(len(levels) - 1, max(log_level + adjustment, 0))
|
||||
tmp_dict["logging"]["level"] = levels[log_level]
|
||||
|
||||
if len(tmp_dict["rules"]["files"]) == 0:
|
||||
tmp_dict["rules"]["files"] = ["*"]
|
||||
else:
|
||||
tmp_dict["rules"]["files"] = tmp_dict["rules"]["files"]
|
||||
self.args_files = True
|
||||
|
||||
return tmp_dict
|
||||
|
||||
def _get_config(self):
|
||||
defaults = self._get_defaults()
|
||||
source_files = []
|
||||
source_files.append(self.config_file)
|
||||
source_files.append(os.path.relpath(
|
||||
os.path.normpath(os.path.join(os.getcwd(), ".later.yml"))))
|
||||
cli_options = self.args
|
||||
|
||||
for config in source_files:
|
||||
if config and os.path.exists(config):
|
||||
with utils.open_file(config) as stream:
|
||||
s = stream.read()
|
||||
sdict = utils.safe_load(s)
|
||||
if self._validate(sdict):
|
||||
sdict["logging"]["level"] = sdict["logging"]["level"].upper()
|
||||
anyconfig.merge(defaults, sdict, ac_merge=anyconfig.MS_DICTS)
|
||||
|
||||
if cli_options and self._validate(cli_options):
|
||||
anyconfig.merge(defaults, cli_options, ac_merge=anyconfig.MS_DICTS)
|
||||
|
||||
return defaults
|
||||
|
||||
def _get_defaults(self):
|
||||
rules_dir = os.path.join(resource_filename("ansiblelater", "data"))
|
||||
defaults = {
|
||||
"rules": {
|
||||
"standards": rules_dir,
|
||||
"filter": [],
|
||||
"ignore_dotfiles": True,
|
||||
"exclude_files": []
|
||||
},
|
||||
"logging": {
|
||||
"level": "WARNING",
|
||||
"json": False
|
||||
},
|
||||
"ansible": {
|
||||
"custom_modules": [],
|
||||
"double-braces": {
|
||||
"min-spaces-inside": 1,
|
||||
"max-spaces-inside": 1,
|
||||
},
|
||||
},
|
||||
"yamllint": {
|
||||
"empty-lines": {
|
||||
"max": 1,
|
||||
"max-start": 0,
|
||||
"max-end": 1,
|
||||
},
|
||||
"indentation": {
|
||||
"spaces": 2,
|
||||
"check-multi-line-strings": False,
|
||||
"indent-sequences": True,
|
||||
},
|
||||
"hyphens": {
|
||||
"max-spaces-after": 1
|
||||
},
|
||||
"document-start": {
|
||||
"present": True
|
||||
},
|
||||
"colons": {
|
||||
"max-spaces-before": 0,
|
||||
"max-spaces-after": 1
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
self.schema = anyconfig.gen_schema(defaults)
|
||||
|
||||
return defaults
|
||||
|
||||
def _validate(self, config):
|
||||
try:
|
||||
anyconfig.validate(config, self.schema, ac_schema_safe=False)
|
||||
return True
|
||||
except Exception as e:
|
||||
schema_error = "Failed validating '{validator}' in schema{schema}".format(
|
||||
validator=e.validator,
|
||||
schema=format_as_index(list(e.relative_schema_path)[:-1])
|
||||
)
|
||||
utils.sysexit_with_message("{schema}: {msg}".format(schema=schema_error, msg=e.message))
|
||||
|
||||
def _update_filelist(self):
|
||||
includes = self.config["rules"]["files"]
|
||||
excludes = self.config["rules"]["exclude_files"]
|
||||
ignore_dotfiles = self.config["rules"]["ignore_dotfiles"]
|
||||
|
||||
if ignore_dotfiles and not self.args_files:
|
||||
excludes.append(".*")
|
||||
else:
|
||||
del excludes[:]
|
||||
|
||||
filelist = []
|
||||
for root, dirs, files in os.walk("."):
|
||||
for filename in files:
|
||||
filelist.append(
|
||||
os.path.relpath(os.path.normpath(os.path.join(root, filename))))
|
||||
|
||||
valid = []
|
||||
includespec = pathspec.PathSpec.from_lines("gitwildmatch", includes)
|
||||
excludespec = pathspec.PathSpec.from_lines("gitwildmatch", excludes)
|
||||
for item in filelist:
|
||||
if includespec.match_file(item) and not excludespec.match_file(item):
|
||||
valid.append(item)
|
||||
|
||||
self.config["rules"]["files"] = valid
|
|
@ -0,0 +1,28 @@
|
|||
"""Standard definition."""
|
||||
|
||||
|
||||
class Standard(object):
|
||||
"""
|
||||
Standard definition for all defined rules.
|
||||
|
||||
Later lookup the config file for a path to a rules directory
|
||||
or fallback to default `ansiblelater/data/*`.
|
||||
"""
|
||||
|
||||
def __init__(self, standard_dict):
|
||||
"""
|
||||
Initialize a new standard object and returns None.
|
||||
|
||||
:param standard_dict: Dictionary object containing all neseccary attributes
|
||||
|
||||
"""
|
||||
self.id = standard_dict.get("id", "")
|
||||
self.name = standard_dict.get("name")
|
||||
self.version = standard_dict.get("version")
|
||||
self.check = standard_dict.get("check")
|
||||
self.types = standard_dict.get("types")
|
||||
|
||||
|
||||
def __repr__(self): # noqa
|
||||
return "Standard: %s (version: %s, types: %s)" % (
|
||||
self.name, self.version, self.types)
|
|
@ -0,0 +1,90 @@
|
|||
"""Test logging module."""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import colorama
|
||||
|
||||
from ansiblelater import logger
|
||||
|
||||
|
||||
def test_flag_extra():
|
||||
extra = {}
|
||||
extra.update(foo="bar")
|
||||
|
||||
flagged = logger.flag_extra(extra)
|
||||
|
||||
assert flagged.get("later_foo") == "bar"
|
||||
|
||||
|
||||
def test_critical(capsys, mocker):
|
||||
log = logger.get_logger("test_critical")
|
||||
log.critical("foo")
|
||||
_, stderr = capsys.readouterr()
|
||||
|
||||
print("{}{}{}".format(colorama.Fore.RED, "CRITICAL: foo".rstrip(),
|
||||
colorama.Style.RESET_ALL))
|
||||
x, _ = capsys.readouterr()
|
||||
|
||||
assert x == stderr
|
||||
|
||||
|
||||
def test_error(capsys, mocker):
|
||||
log = logger.get_logger("test_error")
|
||||
log.error("foo")
|
||||
_, stderr = capsys.readouterr()
|
||||
|
||||
print("{}{}{}".format(colorama.Fore.RED, "ERROR: foo".rstrip(),
|
||||
colorama.Style.RESET_ALL))
|
||||
x, _ = capsys.readouterr()
|
||||
|
||||
assert x == stderr
|
||||
|
||||
|
||||
def test_warn(capsys, mocker):
|
||||
log = logger.get_logger("test_warn")
|
||||
log.warning("foo")
|
||||
stdout, _ = capsys.readouterr()
|
||||
|
||||
print("{}{}{}".format(colorama.Fore.YELLOW, "WARNING: foo".rstrip(),
|
||||
colorama.Style.RESET_ALL))
|
||||
x, _ = capsys.readouterr()
|
||||
|
||||
assert x == stdout
|
||||
|
||||
|
||||
def test_info(capsys, mocker):
|
||||
log = logger.get_logger("test_info")
|
||||
log.info("foo")
|
||||
stdout, _ = capsys.readouterr()
|
||||
|
||||
print("{}{}{}".format(colorama.Fore.BLUE, "INFO: foo".rstrip(),
|
||||
colorama.Style.RESET_ALL))
|
||||
x, _ = capsys.readouterr()
|
||||
|
||||
assert x == stdout
|
||||
|
||||
|
||||
def test_markup_detection_pycolors0(monkeypatch):
|
||||
monkeypatch.setenv("PY_COLORS", "0")
|
||||
assert not logger._should_do_markup()
|
||||
|
||||
|
||||
def test_markup_detection_pycolors1(monkeypatch):
|
||||
monkeypatch.setenv("PY_COLORS", "1")
|
||||
assert logger._should_do_markup()
|
||||
|
||||
|
||||
def test_markup_detection_tty_yes(mocker):
|
||||
mocker.patch("sys.stdout.isatty", return_value=True)
|
||||
mocker.patch("os.environ", {"TERM": "xterm"})
|
||||
assert logger._should_do_markup()
|
||||
mocker.resetall()
|
||||
mocker.stopall()
|
||||
|
||||
|
||||
def test_markup_detection_tty_no(mocker):
|
||||
mocker.patch("os.environ", {})
|
||||
mocker.patch("sys.stdout.isatty", return_value=False)
|
||||
assert not logger._should_do_markup()
|
||||
mocker.resetall()
|
||||
mocker.stopall()
|
|
@ -0,0 +1,25 @@
|
|||
import pytest
|
||||
|
||||
from ansiblelater import settings
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def settings_instance():
|
||||
c = settings.Settings(args={"rules": {"files": []}})
|
||||
|
||||
return c
|
||||
|
||||
|
||||
def test_args_member(settings_instance):
|
||||
x = {"rules": {"files": ["*"]}}
|
||||
|
||||
assert x == settings_instance.args
|
||||
|
||||
|
||||
def test_args_setter(settings_instance):
|
||||
default = {"rules.files": ["dummy"], "config_file": "conf.yml"}
|
||||
x = {"rules": {"files": ["dummy"]}}
|
||||
|
||||
s = settings_instance._set_args(default)
|
||||
|
||||
assert x == s
|
|
@ -1,53 +1,24 @@
|
|||
"""Global utils collection."""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import importlib
|
||||
import logging
|
||||
import contextlib
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import colorama
|
||||
|
||||
import sys
|
||||
from distutils.version import LooseVersion
|
||||
from ansible.module_utils.parsing.convert_bool import boolean as to_bool
|
||||
|
||||
import yaml
|
||||
|
||||
from ansiblelater import logger
|
||||
|
||||
try:
|
||||
import ConfigParser as configparser
|
||||
import ConfigParser as configparser # noqa
|
||||
except ImportError:
|
||||
import configparser
|
||||
import configparser # noqa
|
||||
|
||||
|
||||
def should_do_markup():
|
||||
py_colors = os.environ.get('PY_COLORS', None)
|
||||
if py_colors is not None:
|
||||
return to_bool(py_colors, strict=False)
|
||||
|
||||
return sys.stdout.isatty() and os.environ.get('TERM') != 'dumb'
|
||||
|
||||
|
||||
colorama.init(autoreset=True, strip=not should_do_markup())
|
||||
|
||||
|
||||
def abort(message, file=sys.stderr):
|
||||
return color_text(colorama.Fore.RED, "FATAL: {}".format(message))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def error(message, file=sys.stderr):
|
||||
return color_text(colorama.Fore.RED, "ERROR: {}".format(message))
|
||||
|
||||
|
||||
def warn(message, settings, file=sys.stdout):
|
||||
if settings.log_level <= logging.WARNING:
|
||||
return color_text(colorama.Fore.YELLOW, "WARN: {}".format(message))
|
||||
|
||||
|
||||
def info(message, settings, file=sys.stdout):
|
||||
if settings.log_level <= logging.INFO:
|
||||
return color_text(colorama.Fore.BLUE, "INFO: {}".format(message))
|
||||
|
||||
|
||||
def color_text(color, msg):
|
||||
print('{}{}{}'.format(color, msg, colorama.Style.RESET_ALL))
|
||||
LOG = logger.get_logger(__name__)
|
||||
|
||||
|
||||
def count_spaces(c_string):
|
||||
|
@ -72,7 +43,7 @@ def get_property(prop):
|
|||
parentdir = os.path.dirname(currentdir)
|
||||
result = re.search(
|
||||
r'{}\s*=\s*[\'"]([^\'"]*)[\'"]'.format(prop),
|
||||
open(os.path.join(parentdir, '__init__.py')).read())
|
||||
open(os.path.join(parentdir, "__init__.py")).read())
|
||||
return result.group(1)
|
||||
|
||||
|
||||
|
@ -95,35 +66,48 @@ def is_line_in_ranges(line, ranges):
|
|||
return not ranges or any([line in r for r in ranges])
|
||||
|
||||
|
||||
def read_standards(settings):
|
||||
if not settings.rulesdir:
|
||||
abort("Standards directory is not set on command line or in configuration file - aborting")
|
||||
sys.path.append(os.path.abspath(os.path.expanduser(settings.rulesdir)))
|
||||
def safe_load(string):
|
||||
"""
|
||||
Parse the provided string returns a dict.
|
||||
|
||||
:param string: A string to be parsed.
|
||||
:returns: dict
|
||||
|
||||
"""
|
||||
try:
|
||||
standards = importlib.import_module('standards')
|
||||
except ImportError as e:
|
||||
abort("Could not import standards from directory %s: %s" % (settings.rulesdir, str(e)))
|
||||
return standards
|
||||
return yaml.safe_load(string) or {}
|
||||
except yaml.scanner.ScannerError as e:
|
||||
print(str(e))
|
||||
|
||||
|
||||
def read_config(config_file):
|
||||
config = configparser.RawConfigParser({'standards': None})
|
||||
config.read(config_file)
|
||||
@contextlib.contextmanager
|
||||
def open_file(filename, mode="r"):
|
||||
"""
|
||||
Open the provide file safely and returns a file type.
|
||||
|
||||
return Settings(config, config_file)
|
||||
:param filename: A string containing an absolute path to the file to open.
|
||||
:param mode: A string describing the way in which the file will be used.
|
||||
:returns: file type
|
||||
|
||||
"""
|
||||
with open(filename, mode) as stream:
|
||||
yield stream
|
||||
|
||||
|
||||
class Settings(object):
|
||||
def __init__(self, config, config_file):
|
||||
self.rulesdir = None
|
||||
self.custom_modules = []
|
||||
self.log_level = None
|
||||
self.standards_filter = None
|
||||
def add_dict_branch(tree, vector, value):
|
||||
key = vector[0]
|
||||
tree[key] = value \
|
||||
if len(vector) == 1 \
|
||||
else add_dict_branch(tree[key] if key in tree else {},
|
||||
vector[1:],
|
||||
value)
|
||||
return tree
|
||||
|
||||
if config.has_section('rules'):
|
||||
self.rulesdir = config.get('rules', 'standards')
|
||||
if config.has_section('ansible'):
|
||||
modules = config.get('ansible', 'custom_modules')
|
||||
self.custom_modules = [x.strip() for x in modules.split(',')]
|
||||
|
||||
self.configfile = config_file
|
||||
def sysexit(code=1):
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
def sysexit_with_message(msg, code=1):
|
||||
LOG.critical(msg)
|
||||
sysexit(code)
|
||||
|
|
|
@ -1,23 +1,26 @@
|
|||
import codecs
|
||||
import yaml
|
||||
"""Abstracted methods to simplify role writeup."""
|
||||
|
||||
import codecs
|
||||
from collections import defaultdict
|
||||
|
||||
import yaml
|
||||
from yamllint import linter
|
||||
from yamllint.config import YamlLintConfig
|
||||
# Workaround for import errors with ansble 2.1 and 2.3
|
||||
from ansible.parsing.dataloader import DataLoader
|
||||
from ansiblelater import Error
|
||||
from .yamlhelper import normalize_task
|
||||
|
||||
from ansiblelater.command.candidates import Error
|
||||
from ansiblelater.exceptions import LaterAnsibleError
|
||||
from ansiblelater.exceptions import LaterError
|
||||
|
||||
from .yamlhelper import action_tasks
|
||||
from .yamlhelper import parse_yaml_linenumbers
|
||||
from .yamlhelper import normalize_task
|
||||
from .yamlhelper import normalized_yaml
|
||||
from .exceptions import LaterError, LaterAnsibleError
|
||||
from .yamlhelper import parse_yaml_linenumbers
|
||||
|
||||
|
||||
def get_tasks(candidate, settings):
|
||||
errors = []
|
||||
try:
|
||||
with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
|
||||
with codecs.open(candidate.path, mode="rb", encoding="utf-8") as f:
|
||||
yamllines = parse_yaml_linenumbers(f, candidate.path)
|
||||
|
||||
except LaterError as ex:
|
||||
|
@ -33,7 +36,7 @@ def get_action_tasks(candidate, settings):
|
|||
tasks = []
|
||||
errors = []
|
||||
try:
|
||||
with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
|
||||
with codecs.open(candidate.path, mode="rb", encoding="utf-8") as f:
|
||||
yamllines = parse_yaml_linenumbers(f, candidate.path)
|
||||
|
||||
if yamllines:
|
||||
|
@ -51,7 +54,7 @@ def get_normalized_task(task, candidate, settings):
|
|||
normalized = None
|
||||
errors = []
|
||||
try:
|
||||
normalized = normalize_task(task, candidate.path, settings.custom_modules)
|
||||
normalized = normalize_task(task, candidate.path, settings["ansible"]["custom_modules"])
|
||||
except LaterError as ex:
|
||||
e = ex.original
|
||||
errors.append(Error(e.problem_mark.line + 1, "syntax error: %s" % (e.problem)))
|
||||
|
@ -65,19 +68,20 @@ def get_normalized_tasks(candidate, settings):
|
|||
normalized = []
|
||||
errors = []
|
||||
try:
|
||||
with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
|
||||
with codecs.open(candidate.path, mode="rb", encoding="utf-8") as f:
|
||||
yamllines = parse_yaml_linenumbers(f, candidate.path)
|
||||
|
||||
if yamllines:
|
||||
tasks = action_tasks(yamllines, candidate)
|
||||
for task in tasks:
|
||||
# An empty `tags` block causes `None` to be returned if
|
||||
# the `or []` is not present - `task.get('tags', [])`
|
||||
# the `or []` is not present - `task.get("tags", [])`
|
||||
# does not suffice.
|
||||
if 'skip_ansible_lint' in (task.get('tags') or []):
|
||||
if "skip_ansible_lint" in (task.get("tags") or []):
|
||||
# No need to normalize_task if we are skipping it.
|
||||
continue
|
||||
normalized.append(normalize_task(task, candidate.path, settings.custom_modules))
|
||||
normalized.append(
|
||||
normalize_task(task, candidate.path, settings["ansible"]["custom_modules"]))
|
||||
|
||||
except LaterError as ex:
|
||||
e = ex.original
|
||||
|
@ -112,7 +116,7 @@ def get_raw_yaml(candidate, settings):
|
|||
errors = []
|
||||
|
||||
try:
|
||||
with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
|
||||
with codecs.open(candidate.path, mode="rb", encoding="utf-8") as f:
|
||||
content = yaml.safe_load(f)
|
||||
|
||||
except LaterError as ex:
|
||||
|
@ -122,10 +126,10 @@ def get_raw_yaml(candidate, settings):
|
|||
return content, errors
|
||||
|
||||
|
||||
def run_yamllint(candidate, settings, options="extends: default"):
|
||||
def run_yamllint(path, options="extends: default"):
|
||||
errors = []
|
||||
try:
|
||||
with codecs.open(candidate.path, mode='rb', encoding='utf-8') as f:
|
||||
with codecs.open(path, mode="rb", encoding="utf-8") as f:
|
||||
for problem in linter.run(f, YamlLintConfig(options)):
|
||||
errors.append(Error(problem.line, problem.desc))
|
||||
except LaterError as ex:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"""Utils for YAML file operations."""
|
||||
|
||||
# Copyright (c) 2013-2014 Will Thames <will@thames.id.au>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
@ -18,17 +20,27 @@
|
|||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
import codecs
|
||||
import glob
|
||||
import imp
|
||||
import os
|
||||
import codecs
|
||||
import inspect
|
||||
import os
|
||||
|
||||
import six
|
||||
import ansible.parsing.mod_args
|
||||
import six
|
||||
import yaml
|
||||
from ansible import constants
|
||||
from ansible.errors import AnsibleError
|
||||
from .exceptions import LaterError, LaterAnsibleError
|
||||
from ansible.errors import AnsibleParserError
|
||||
from ansible.parsing.dataloader import DataLoader
|
||||
from ansible.parsing.mod_args import ModuleArgsParser
|
||||
from ansible.parsing.yaml.constructor import AnsibleConstructor
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
from ansible.template import Templar
|
||||
from yaml.composer import Composer
|
||||
|
||||
from ansiblelater.exceptions import LaterAnsibleError
|
||||
from ansiblelater.exceptions import LaterError
|
||||
|
||||
try:
|
||||
# Try to import the Ansible 2 module first, it's the future-proof one
|
||||
|
@ -38,25 +50,15 @@ except ImportError:
|
|||
# Fallback on the Ansible 1.9 module
|
||||
from ansible.module_utils.splitter import split_args
|
||||
|
||||
import yaml
|
||||
from yaml.composer import Composer
|
||||
|
||||
from ansible.parsing.dataloader import DataLoader
|
||||
from ansible.template import Templar
|
||||
from ansible.parsing.mod_args import ModuleArgsParser
|
||||
from ansible.parsing.yaml.constructor import AnsibleConstructor
|
||||
from ansible.parsing.yaml.loader import AnsibleLoader
|
||||
from ansible.errors import AnsibleParserError
|
||||
|
||||
# ansible-later doesn't need/want to know about encrypted secrets, but it needs
|
||||
# Ansible 2.3+ allows encrypted secrets within yaml files, so we pass a string
|
||||
# as the password to enable such yaml files to be opened and parsed successfully.
|
||||
DEFAULT_VAULT_PASSWORD = 'x'
|
||||
DEFAULT_VAULT_PASSWORD = "x"
|
||||
|
||||
|
||||
def parse_yaml_from_file(filepath):
|
||||
dl = DataLoader()
|
||||
if hasattr(dl, 'set_vault_password'):
|
||||
if hasattr(dl, "set_vault_password"):
|
||||
dl.set_vault_password(DEFAULT_VAULT_PASSWORD)
|
||||
return dl.load_from_file(filepath)
|
||||
|
||||
|
@ -79,28 +81,28 @@ try:
|
|||
except ImportError:
|
||||
from ansible.plugins.loader import module_loader
|
||||
|
||||
LINE_NUMBER_KEY = '__line__'
|
||||
FILENAME_KEY = '__file__'
|
||||
LINE_NUMBER_KEY = "__line__"
|
||||
FILENAME_KEY = "__file__"
|
||||
|
||||
VALID_KEYS = [
|
||||
'name', 'action', 'when', 'async', 'poll', 'notify',
|
||||
'first_available_file', 'include', 'import_playbook',
|
||||
'tags', 'register', 'ignore_errors', 'delegate_to',
|
||||
'local_action', 'transport', 'remote_user', 'sudo',
|
||||
'sudo_user', 'sudo_pass', 'when', 'connection', 'environment', 'args', 'always_run',
|
||||
'any_errors_fatal', 'changed_when', 'failed_when', 'check_mode', 'delay',
|
||||
'retries', 'until', 'su', 'su_user', 'su_pass', 'no_log', 'run_once',
|
||||
'become', 'become_user', 'become_method', FILENAME_KEY,
|
||||
"name", "action", "when", "async", "poll", "notify",
|
||||
"first_available_file", "include", "import_playbook",
|
||||
"tags", "register", "ignore_errors", "delegate_to",
|
||||
"local_action", "transport", "remote_user", "sudo",
|
||||
"sudo_user", "sudo_pass", "when", "connection", "environment", "args", "always_run",
|
||||
"any_errors_fatal", "changed_when", "failed_when", "check_mode", "delay",
|
||||
"retries", "until", "su", "su_user", "su_pass", "no_log", "run_once",
|
||||
"become", "become_user", "become_method", FILENAME_KEY,
|
||||
]
|
||||
|
||||
BLOCK_NAME_TO_ACTION_TYPE_MAP = {
|
||||
'tasks': 'task',
|
||||
'handlers': 'handler',
|
||||
'pre_tasks': 'task',
|
||||
'post_tasks': 'task',
|
||||
'block': 'meta',
|
||||
'rescue': 'meta',
|
||||
'always': 'meta',
|
||||
"tasks": "task",
|
||||
"handlers": "handler",
|
||||
"pre_tasks": "task",
|
||||
"post_tasks": "task",
|
||||
"block": "meta",
|
||||
"rescue": "meta",
|
||||
"always": "meta",
|
||||
}
|
||||
|
||||
|
||||
|
@ -108,9 +110,9 @@ def load_plugins(directory):
|
|||
result = []
|
||||
fh = None
|
||||
|
||||
for pluginfile in glob.glob(os.path.join(directory, '[A-Za-z]*.py')):
|
||||
for pluginfile in glob.glob(os.path.join(directory, "[A-Za-z]*.py")):
|
||||
|
||||
pluginname = os.path.basename(pluginfile.replace('.py', ''))
|
||||
pluginname = os.path.basename(pluginfile.replace(".py", ""))
|
||||
try:
|
||||
fh, filename, desc = imp.find_module(pluginname, [directory])
|
||||
mod = imp.load_module(pluginname, fh, filename, desc)
|
||||
|
@ -124,9 +126,9 @@ def load_plugins(directory):
|
|||
|
||||
def tokenize(line):
|
||||
tokens = line.lstrip().split(" ")
|
||||
if tokens[0] == '-':
|
||||
if tokens[0] == "-":
|
||||
tokens = tokens[1:]
|
||||
if tokens[0] == 'action:' or tokens[0] == 'local_action:':
|
||||
if tokens[0] == "action:" or tokens[0] == "local_action:":
|
||||
tokens = tokens[1:]
|
||||
command = tokens[0].replace(":", "")
|
||||
|
||||
|
@ -155,8 +157,8 @@ def _playbook_items(pb_data):
|
|||
def find_children(playbook, playbook_dir):
|
||||
if not os.path.exists(playbook[0]):
|
||||
return []
|
||||
if playbook[1] == 'role':
|
||||
playbook_ds = {'roles': [{'role': playbook[0]}]}
|
||||
if playbook[1] == "role":
|
||||
playbook_ds = {"roles": [{"role": playbook[0]}]}
|
||||
else:
|
||||
try:
|
||||
playbook_ds = parse_yaml_from_file(playbook[0])
|
||||
|
@ -167,24 +169,24 @@ def find_children(playbook, playbook_dir):
|
|||
items = _playbook_items(playbook_ds)
|
||||
for item in items:
|
||||
for child in play_children(basedir, item, playbook[1], playbook_dir):
|
||||
if "$" in child['path'] or "{{" in child['path']:
|
||||
if "$" in child["path"] or "{{" in child["path"]:
|
||||
continue
|
||||
valid_tokens = list()
|
||||
for token in split_args(child['path']):
|
||||
if '=' in token:
|
||||
for token in split_args(child["path"]):
|
||||
if "=" in token:
|
||||
break
|
||||
valid_tokens.append(token)
|
||||
path = ' '.join(valid_tokens)
|
||||
path = " ".join(valid_tokens)
|
||||
results.append({
|
||||
'path': path_dwim(basedir, path),
|
||||
'type': child['type']
|
||||
"path": path_dwim(basedir, path),
|
||||
"type": child["type"]
|
||||
})
|
||||
return results
|
||||
|
||||
|
||||
def template(basedir, value, vars, fail_on_undefined=False, **kwargs):
|
||||
def template(basedir, value, variables, fail_on_undefined=False, **kwargs):
|
||||
try:
|
||||
value = ansible_template(os.path.abspath(basedir), value, vars,
|
||||
value = ansible_template(os.path.abspath(basedir), value, variables,
|
||||
**dict(kwargs, fail_on_undefined=fail_on_undefined))
|
||||
# Hack to skip the following exception when using to_json filter on a variable.
|
||||
# I guess the filter doesn't like empty vars...
|
||||
|
@ -196,18 +198,18 @@ def template(basedir, value, vars, fail_on_undefined=False, **kwargs):
|
|||
|
||||
def play_children(basedir, item, parent_type, playbook_dir):
|
||||
delegate_map = {
|
||||
'tasks': _taskshandlers_children,
|
||||
'pre_tasks': _taskshandlers_children,
|
||||
'post_tasks': _taskshandlers_children,
|
||||
'block': _taskshandlers_children,
|
||||
'include': _include_children,
|
||||
'import_playbook': _include_children,
|
||||
'roles': _roles_children,
|
||||
'dependencies': _roles_children,
|
||||
'handlers': _taskshandlers_children,
|
||||
"tasks": _taskshandlers_children,
|
||||
"pre_tasks": _taskshandlers_children,
|
||||
"post_tasks": _taskshandlers_children,
|
||||
"block": _taskshandlers_children,
|
||||
"include": _include_children,
|
||||
"import_playbook": _include_children,
|
||||
"roles": _roles_children,
|
||||
"dependencies": _roles_children,
|
||||
"handlers": _taskshandlers_children,
|
||||
}
|
||||
(k, v) = item
|
||||
play_library = os.path.join(os.path.abspath(basedir), 'library')
|
||||
play_library = os.path.join(os.path.abspath(basedir), "library")
|
||||
_load_library_if_exists(play_library)
|
||||
|
||||
if k in delegate_map:
|
||||
|
@ -225,35 +227,35 @@ def _include_children(basedir, k, v, parent_type):
|
|||
(command, args, kwargs) = tokenize("{0}: {1}".format(k, v))
|
||||
|
||||
result = path_dwim(basedir, args[0])
|
||||
if not os.path.exists(result) and not basedir.endswith('tasks'):
|
||||
result = path_dwim(os.path.join(basedir, '..', 'tasks'), v)
|
||||
return [{'path': result, 'type': parent_type}]
|
||||
if not os.path.exists(result) and not basedir.endswith("tasks"):
|
||||
result = path_dwim(os.path.join(basedir, "..", "tasks"), v)
|
||||
return [{"path": result, "type": parent_type}]
|
||||
|
||||
|
||||
def _taskshandlers_children(basedir, k, v, parent_type):
|
||||
results = []
|
||||
for th in v:
|
||||
if 'include' in th:
|
||||
append_children(th['include'], basedir, k, parent_type, results)
|
||||
elif 'include_tasks' in th:
|
||||
append_children(th['include_tasks'], basedir, k, parent_type, results)
|
||||
elif 'import_playbook' in th:
|
||||
append_children(th['import_playbook'], basedir, k, parent_type, results)
|
||||
elif 'import_tasks' in th:
|
||||
append_children(th['import_tasks'], basedir, k, parent_type, results)
|
||||
elif 'import_role' in th:
|
||||
results.extend(_roles_children(basedir, k, [th['import_role'].get('name')], parent_type,
|
||||
main=th['import_role'].get('tasks_from', 'main')))
|
||||
elif 'include_role' in th:
|
||||
results.extend(_roles_children(basedir, k, [th['include_role'].get('name')],
|
||||
if "include" in th:
|
||||
append_children(th["include"], basedir, k, parent_type, results)
|
||||
elif "include_tasks" in th:
|
||||
append_children(th["include_tasks"], basedir, k, parent_type, results)
|
||||
elif "import_playbook" in th:
|
||||
append_children(th["import_playbook"], basedir, k, parent_type, results)
|
||||
elif "import_tasks" in th:
|
||||
append_children(th["import_tasks"], basedir, k, parent_type, results)
|
||||
elif "import_role" in th:
|
||||
results.extend(_roles_children(basedir, k, [th["import_role"].get("name")], parent_type,
|
||||
main=th["import_role"].get("tasks_from", "main")))
|
||||
elif "include_role" in th:
|
||||
results.extend(_roles_children(basedir, k, [th["include_role"].get("name")],
|
||||
parent_type,
|
||||
main=th['include_role'].get('tasks_from', 'main')))
|
||||
elif 'block' in th:
|
||||
results.extend(_taskshandlers_children(basedir, k, th['block'], parent_type))
|
||||
if 'rescue' in th:
|
||||
results.extend(_taskshandlers_children(basedir, k, th['rescue'], parent_type))
|
||||
if 'always' in th:
|
||||
results.extend(_taskshandlers_children(basedir, k, th['always'], parent_type))
|
||||
main=th["include_role"].get("tasks_from", "main")))
|
||||
elif "block" in th:
|
||||
results.extend(_taskshandlers_children(basedir, k, th["block"], parent_type))
|
||||
if "rescue" in th:
|
||||
results.extend(_taskshandlers_children(basedir, k, th["rescue"], parent_type))
|
||||
if "always" in th:
|
||||
results.extend(_taskshandlers_children(basedir, k, th["always"], parent_type))
|
||||
return results
|
||||
|
||||
|
||||
|
@ -261,28 +263,28 @@ def append_children(taskhandler, basedir, k, parent_type, results):
|
|||
# when taskshandlers_children is called for playbooks, the
|
||||
# actual type of the included tasks is the section containing the
|
||||
# include, e.g. tasks, pre_tasks, or handlers.
|
||||
if parent_type == 'playbook':
|
||||
if parent_type == "playbook":
|
||||
playbook_section = k
|
||||
else:
|
||||
playbook_section = parent_type
|
||||
results.append({
|
||||
'path': path_dwim(basedir, taskhandler),
|
||||
'type': playbook_section
|
||||
"path": path_dwim(basedir, taskhandler),
|
||||
"type": playbook_section
|
||||
})
|
||||
|
||||
|
||||
def _roles_children(basedir, k, v, parent_type, main='main'):
|
||||
def _roles_children(basedir, k, v, parent_type, main="main"):
|
||||
results = []
|
||||
for role in v:
|
||||
if isinstance(role, dict):
|
||||
if 'role' in role or 'name' in role:
|
||||
if 'tags' not in role or 'skip_ansible_later' not in role['tags']:
|
||||
if "role" in role or "name" in role:
|
||||
if "tags" not in role or "skip_ansible_later" not in role["tags"]:
|
||||
results.extend(_look_for_role_files(basedir,
|
||||
role.get('role', role.get('name')),
|
||||
role.get("role", role.get("name")),
|
||||
main=main))
|
||||
else:
|
||||
raise SystemExit('role dict {0} does not contain a "role" '
|
||||
'or "name" key'.format(role))
|
||||
raise SystemExit("role dict {0} does not contain a 'role' "
|
||||
"or 'name' key".format(role))
|
||||
else:
|
||||
results.extend(_look_for_role_files(basedir, role, main=main))
|
||||
return results
|
||||
|
@ -298,13 +300,13 @@ def _rolepath(basedir, role):
|
|||
|
||||
possible_paths = [
|
||||
# if included from a playbook
|
||||
path_dwim(basedir, os.path.join('roles', role)),
|
||||
path_dwim(basedir, os.path.join("roles", role)),
|
||||
path_dwim(basedir, role),
|
||||
# if included from roles/[role]/meta/main.yml
|
||||
path_dwim(
|
||||
basedir, os.path.join('..', '..', '..', 'roles', role)
|
||||
basedir, os.path.join("..", "..", "..", "roles", role)
|
||||
),
|
||||
path_dwim(basedir, os.path.join('..', '..', role))
|
||||
path_dwim(basedir, os.path.join("..", "..", role))
|
||||
]
|
||||
|
||||
if constants.DEFAULT_ROLES_PATH:
|
||||
|
@ -321,33 +323,33 @@ def _rolepath(basedir, role):
|
|||
break
|
||||
|
||||
if role_path:
|
||||
_load_library_if_exists(os.path.join(role_path, 'library'))
|
||||
_load_library_if_exists(os.path.join(role_path, "library"))
|
||||
|
||||
return role_path
|
||||
|
||||
|
||||
def _look_for_role_files(basedir, role, main='main'):
|
||||
def _look_for_role_files(basedir, role, main="main"):
|
||||
role_path = _rolepath(basedir, role)
|
||||
if not role_path:
|
||||
return []
|
||||
|
||||
results = []
|
||||
|
||||
for th in ['tasks', 'handlers', 'meta']:
|
||||
for ext in ('.yml', '.yaml'):
|
||||
for th in ["tasks", "handlers", "meta"]:
|
||||
for ext in (".yml", ".yaml"):
|
||||
thpath = os.path.join(role_path, th, main + ext)
|
||||
if os.path.exists(thpath):
|
||||
results.append({'path': thpath, 'type': th})
|
||||
results.append({"path": thpath, "type": th})
|
||||
break
|
||||
return results
|
||||
|
||||
|
||||
def rolename(filepath):
|
||||
idx = filepath.find('roles/')
|
||||
idx = filepath.find("roles/")
|
||||
if idx < 0:
|
||||
return ''
|
||||
return ""
|
||||
role = filepath[idx + 6:]
|
||||
role = role[:role.find('/')]
|
||||
role = role[:role.find("/")]
|
||||
return role
|
||||
|
||||
|
||||
|
@ -357,11 +359,11 @@ def _kv_to_dict(v):
|
|||
|
||||
|
||||
def normalize_task(task, filename, custom_modules=[]):
|
||||
'''Ensures tasks have an action key and strings are converted to python objects'''
|
||||
ansible_action_type = task.get('__ansible_action_type__', 'task')
|
||||
ansible_action_meta = task.get('__ansible_action_meta__', dict())
|
||||
if '__ansible_action_type__' in task:
|
||||
del(task['__ansible_action_type__'])
|
||||
"""Ensure tasks have an action key and strings are converted to python objects."""
|
||||
ansible_action_type = task.get("__ansible_action_type__", "task")
|
||||
ansible_action_meta = task.get("__ansible_action_meta__", dict())
|
||||
if "__ansible_action_type__" in task:
|
||||
del(task["__ansible_action_type__"])
|
||||
|
||||
normalized = dict()
|
||||
# TODO: Workaround for custom modules
|
||||
|
@ -370,53 +372,53 @@ def normalize_task(task, filename, custom_modules=[]):
|
|||
ansible.parsing.mod_args.BUILTIN_TASKS = frozenset(builtin)
|
||||
mod_arg_parser = ModuleArgsParser(task)
|
||||
try:
|
||||
action, arguments, normalized['delegate_to'] = mod_arg_parser.parse()
|
||||
action, arguments, normalized["delegate_to"] = mod_arg_parser.parse()
|
||||
except AnsibleParserError as e:
|
||||
raise LaterAnsibleError("syntax error", e)
|
||||
|
||||
# denormalize shell -> command conversion
|
||||
if '_uses_shell' in arguments:
|
||||
action = 'shell'
|
||||
del(arguments['_uses_shell'])
|
||||
if "_uses_shell" in arguments:
|
||||
action = "shell"
|
||||
del(arguments["_uses_shell"])
|
||||
|
||||
for (k, v) in list(task.items()):
|
||||
if k in ('action', 'local_action', 'args', 'delegate_to') or k == action:
|
||||
# we don't want to re-assign these values, which were
|
||||
if k in ("action", "local_action", "args", "delegate_to") or k == action:
|
||||
# we don"t want to re-assign these values, which were
|
||||
# determined by the ModuleArgsParser() above
|
||||
continue
|
||||
else:
|
||||
normalized[k] = v
|
||||
|
||||
normalized['action'] = dict(__ansible_module__=action)
|
||||
normalized["action"] = dict(__ansible_module__=action)
|
||||
|
||||
if '_raw_params' in arguments:
|
||||
normalized['action']['__ansible_arguments__'] = arguments['_raw_params'].split(' ')
|
||||
del(arguments['_raw_params'])
|
||||
if "_raw_params" in arguments:
|
||||
normalized["action"]["__ansible_arguments__"] = arguments["_raw_params"].split(" ")
|
||||
del(arguments["_raw_params"])
|
||||
else:
|
||||
normalized['action']['__ansible_arguments__'] = list()
|
||||
normalized['action'].update(arguments)
|
||||
normalized["action"]["__ansible_arguments__"] = list()
|
||||
normalized["action"].update(arguments)
|
||||
|
||||
normalized[FILENAME_KEY] = filename
|
||||
normalized['__ansible_action_type__'] = ansible_action_type
|
||||
normalized['__ansible_action_meta__'] = ansible_action_meta
|
||||
normalized["__ansible_action_type__"] = ansible_action_type
|
||||
normalized["__ansible_action_meta__"] = ansible_action_meta
|
||||
return normalized
|
||||
|
||||
|
||||
def action_tasks(yaml, file):
|
||||
tasks = list()
|
||||
if file['filetype'] in ['tasks', 'handlers']:
|
||||
tasks = add_action_type(yaml, file['filetype'])
|
||||
if file["filetype"] in ["tasks", "handlers"]:
|
||||
tasks = add_action_type(yaml, file["filetype"])
|
||||
else:
|
||||
tasks.extend(extract_from_list(yaml, ['tasks', 'handlers', 'pre_tasks', 'post_tasks']))
|
||||
tasks.extend(extract_from_list(yaml, ["tasks", "handlers", "pre_tasks", "post_tasks"]))
|
||||
|
||||
# Add sub-elements of block/rescue/always to tasks list
|
||||
tasks.extend(extract_from_list(tasks, ['block', 'rescue', 'always']))
|
||||
tasks.extend(extract_from_list(tasks, ["block", "rescue", "always"]))
|
||||
# Remove block/rescue/always elements from tasks list
|
||||
block_rescue_always = ('block', 'rescue', 'always')
|
||||
block_rescue_always = ("block", "rescue", "always")
|
||||
tasks[:] = [task for task in tasks if all(k not in task for k in block_rescue_always)]
|
||||
|
||||
return [task for task in tasks if set(
|
||||
['include', 'include_tasks', 'import_playbook', 'import_tasks']).isdisjoint(task.keys())]
|
||||
["include", "include_tasks", "import_playbook", "import_tasks"]).isdisjoint(task.keys())]
|
||||
|
||||
|
||||
def task_to_str(task):
|
||||
|
@ -434,12 +436,12 @@ def extract_from_list(blocks, candidates):
|
|||
results = list()
|
||||
for block in blocks:
|
||||
for candidate in candidates:
|
||||
delete_meta_keys = [candidate, '__line__', '__file__', '__ansible_action_type__']
|
||||
delete_meta_keys = [candidate, "__line__", "__file__", "__ansible_action_type__"]
|
||||
if isinstance(block, dict) and candidate in block:
|
||||
if isinstance(block[candidate], list):
|
||||
meta_data = dict(block)
|
||||
for key in delete_meta_keys:
|
||||
del meta_data[key]
|
||||
meta_data.pop(key, None)
|
||||
results.extend(add_action_type(block[candidate], candidate, meta_data))
|
||||
elif block[candidate] is not None:
|
||||
raise RuntimeError(
|
||||
|
@ -451,19 +453,20 @@ def extract_from_list(blocks, candidates):
|
|||
def add_action_type(actions, action_type, action_meta=None):
|
||||
results = list()
|
||||
for action in actions:
|
||||
action['__ansible_action_type__'] = BLOCK_NAME_TO_ACTION_TYPE_MAP[action_type]
|
||||
action["__ansible_action_type__"] = BLOCK_NAME_TO_ACTION_TYPE_MAP[action_type]
|
||||
if action_meta:
|
||||
action['__ansible_action_meta__'] = action_meta
|
||||
action["__ansible_action_meta__"] = action_meta
|
||||
results.append(action)
|
||||
return results
|
||||
|
||||
|
||||
def parse_yaml_linenumbers(data, filename):
|
||||
"""Parses yaml as ansible.utils.parse_yaml but with linenumbers.
|
||||
"""
|
||||
Parse yaml as ansible.utils.parse_yaml but with linenumbers.
|
||||
|
||||
The line numbers are stored in each node's LINE_NUMBER_KEY key.
|
||||
"""
|
||||
|
||||
"""
|
||||
def compose_node(parent, index):
|
||||
# the line number where the previous token has ended (plus empty lines)
|
||||
line = loader.line
|
||||
|
@ -473,7 +476,7 @@ def parse_yaml_linenumbers(data, filename):
|
|||
|
||||
def construct_mapping(node, deep=False):
|
||||
mapping = AnsibleConstructor.construct_mapping(loader, node, deep=deep)
|
||||
if hasattr(node, '__line__'):
|
||||
if hasattr(node, "__line__"):
|
||||
mapping[LINE_NUMBER_KEY] = node.__line__
|
||||
else:
|
||||
mapping[LINE_NUMBER_KEY] = mapping._line_number
|
||||
|
@ -482,14 +485,17 @@ def parse_yaml_linenumbers(data, filename):
|
|||
|
||||
try:
|
||||
kwargs = {}
|
||||
if 'vault_password' in inspect.getargspec(AnsibleLoader.__init__).args:
|
||||
kwargs['vault_password'] = DEFAULT_VAULT_PASSWORD
|
||||
if "vault_password" in inspect.getargspec(AnsibleLoader.__init__).args:
|
||||
kwargs["vault_password"] = DEFAULT_VAULT_PASSWORD
|
||||
loader = AnsibleLoader(data, **kwargs)
|
||||
loader.compose_node = compose_node
|
||||
loader.construct_mapping = construct_mapping
|
||||
data = loader.get_single_data()
|
||||
except (yaml.parser.ParserError, yaml.scanner.ScannerError) as e:
|
||||
raise LaterError("syntax error", e)
|
||||
except (yaml.composer.ComposerError) as e:
|
||||
e.problem = "{} {}".format(e.context, e.problem)
|
||||
raise LaterError("syntax error", e)
|
||||
return data
|
||||
|
||||
|
||||
|
@ -498,7 +504,7 @@ def normalized_yaml(file, options):
|
|||
removes = []
|
||||
|
||||
try:
|
||||
with codecs.open(file, mode='rb', encoding='utf-8') as f:
|
||||
with codecs.open(file, mode="rb", encoding="utf-8") as f:
|
||||
lines = list(enumerate(f.readlines(), start=1))
|
||||
|
||||
for i, line in lines:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
|
||||
import ansiblelater.__main__
|
||||
|
||||
sys.exit(ansiblelater.__main__.main())
|
|
@ -0,0 +1,78 @@
|
|||
# This file must be used with "source bin/activate" *from bash*
|
||||
# you cannot run it directly
|
||||
|
||||
deactivate () {
|
||||
unset -f pydoc >/dev/null 2>&1
|
||||
|
||||
# reset old environment variables
|
||||
# ! [ -z ${VAR+_} ] returns true if VAR is declared at all
|
||||
if ! [ -z "${_OLD_VIRTUAL_PATH+_}" ] ; then
|
||||
PATH="$_OLD_VIRTUAL_PATH"
|
||||
export PATH
|
||||
unset _OLD_VIRTUAL_PATH
|
||||
fi
|
||||
if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then
|
||||
PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME"
|
||||
export PYTHONHOME
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then
|
||||
hash -r 2>/dev/null
|
||||
fi
|
||||
|
||||
if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then
|
||||
PS1="$_OLD_VIRTUAL_PS1"
|
||||
export PS1
|
||||
unset _OLD_VIRTUAL_PS1
|
||||
fi
|
||||
|
||||
unset VIRTUAL_ENV
|
||||
if [ ! "${1-}" = "nondestructive" ] ; then
|
||||
# Self destruct!
|
||||
unset -f deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
# unset irrelevant variables
|
||||
deactivate nondestructive
|
||||
|
||||
VIRTUAL_ENV="/Users/rkau2905/Devel/python/private/ansible-later/env_27"
|
||||
export VIRTUAL_ENV
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
export PATH
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
if ! [ -z "${PYTHONHOME+_}" ] ; then
|
||||
_OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME"
|
||||
unset PYTHONHOME
|
||||
fi
|
||||
|
||||
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then
|
||||
_OLD_VIRTUAL_PS1="$PS1"
|
||||
if [ "x" != x ] ; then
|
||||
PS1="$PS1"
|
||||
else
|
||||
PS1="(`basename \"$VIRTUAL_ENV\"`) $PS1"
|
||||
fi
|
||||
export PS1
|
||||
fi
|
||||
|
||||
# Make sure to unalias pydoc if it's already there
|
||||
alias pydoc 2>/dev/null >/dev/null && unalias pydoc
|
||||
|
||||
pydoc () {
|
||||
python -m pydoc "$@"
|
||||
}
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH-}" ] || [ -n "${ZSH_VERSION-}" ] ; then
|
||||
hash -r 2>/dev/null
|
||||
fi
|
|
@ -0,0 +1,42 @@
|
|||
# This file must be used with "source bin/activate.csh" *from csh*.
|
||||
# You cannot run it directly.
|
||||
# Created by Davide Di Blasi <davidedb@gmail.com>.
|
||||
|
||||
set newline='\
|
||||
'
|
||||
|
||||
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH:q" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT:q" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc'
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
setenv VIRTUAL_ENV "/Users/rkau2905/Devel/python/private/ansible-later/env_27"
|
||||
|
||||
set _OLD_VIRTUAL_PATH="$PATH:q"
|
||||
setenv PATH "$VIRTUAL_ENV:q/bin:$PATH:q"
|
||||
|
||||
|
||||
|
||||
if ("" != "") then
|
||||
set env_name = ""
|
||||
else
|
||||
set env_name = "$VIRTUAL_ENV:t:q"
|
||||
endif
|
||||
|
||||
# Could be in a non-interactive environment,
|
||||
# in which case, $prompt is undefined and we wouldn't
|
||||
# care about the prompt anyway.
|
||||
if ( $?prompt ) then
|
||||
set _OLD_VIRTUAL_PROMPT="$prompt:q"
|
||||
if ( "$prompt:q" =~ *"$newline:q"* ) then
|
||||
:
|
||||
else
|
||||
set prompt = "[$env_name:q] $prompt:q"
|
||||
endif
|
||||
endif
|
||||
|
||||
unset env_name
|
||||
|
||||
alias pydoc python -m pydoc
|
||||
|
||||
rehash
|
|
@ -0,0 +1,76 @@
|
|||
# This file must be used using `source bin/activate.fish` *within a running fish ( http://fishshell.com ) session*.
|
||||
# Do not run it directly.
|
||||
|
||||
function deactivate -d 'Exit virtualenv mode and return to the normal environment.'
|
||||
# reset old environment variables
|
||||
if test -n "$_OLD_VIRTUAL_PATH"
|
||||
set -gx PATH $_OLD_VIRTUAL_PATH
|
||||
set -e _OLD_VIRTUAL_PATH
|
||||
end
|
||||
|
||||
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
|
||||
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
|
||||
set -e _OLD_VIRTUAL_PYTHONHOME
|
||||
end
|
||||
|
||||
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
|
||||
# Set an empty local `$fish_function_path` to allow the removal of `fish_prompt` using `functions -e`.
|
||||
set -l fish_function_path
|
||||
|
||||
# Erase virtualenv's `fish_prompt` and restore the original.
|
||||
functions -e fish_prompt
|
||||
functions -c _old_fish_prompt fish_prompt
|
||||
functions -e _old_fish_prompt
|
||||
set -e _OLD_FISH_PROMPT_OVERRIDE
|
||||
end
|
||||
|
||||
set -e VIRTUAL_ENV
|
||||
|
||||
if test "$argv[1]" != 'nondestructive'
|
||||
# Self-destruct!
|
||||
functions -e pydoc
|
||||
functions -e deactivate
|
||||
end
|
||||
end
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
set -gx VIRTUAL_ENV "/Users/rkau2905/Devel/python/private/ansible-later/env_27"
|
||||
|
||||
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||
set -gx PATH "$VIRTUAL_ENV/bin" $PATH
|
||||
|
||||
# Unset `$PYTHONHOME` if set.
|
||||
if set -q PYTHONHOME
|
||||
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
|
||||
set -e PYTHONHOME
|
||||
end
|
||||
|
||||
function pydoc
|
||||
python -m pydoc $argv
|
||||
end
|
||||
|
||||
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||
# Copy the current `fish_prompt` function as `_old_fish_prompt`.
|
||||
functions -c fish_prompt _old_fish_prompt
|
||||
|
||||
function fish_prompt
|
||||
# Save the current $status, for fish_prompts that display it.
|
||||
set -l old_status $status
|
||||
|
||||
# Prompt override provided?
|
||||
# If not, just prepend the environment name.
|
||||
if test -n ""
|
||||
printf '%s%s' "" (set_color normal)
|
||||
else
|
||||
printf '%s(%s) ' (set_color normal) (basename "$VIRTUAL_ENV")
|
||||
end
|
||||
|
||||
# Restore the original $status
|
||||
echo "exit $old_status" | source
|
||||
_old_fish_prompt
|
||||
end
|
||||
|
||||
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
|
||||
end
|
|
@ -0,0 +1,150 @@
|
|||
# This file must be dot sourced from PoSh; you cannot run it
|
||||
# directly. Do this: . ./activate.ps1
|
||||
|
||||
# FIXME: clean up unused vars.
|
||||
$script:THIS_PATH = $myinvocation.mycommand.path
|
||||
$script:BASE_DIR = split-path (resolve-path "$THIS_PATH/..") -Parent
|
||||
$script:DIR_NAME = split-path $BASE_DIR -Leaf
|
||||
|
||||
function global:deactivate ( [switch] $NonDestructive ){
|
||||
|
||||
if ( test-path variable:_OLD_VIRTUAL_PATH ) {
|
||||
$env:PATH = $variable:_OLD_VIRTUAL_PATH
|
||||
remove-variable "_OLD_VIRTUAL_PATH" -scope global
|
||||
}
|
||||
|
||||
if ( test-path function:_old_virtual_prompt ) {
|
||||
$function:prompt = $function:_old_virtual_prompt
|
||||
remove-item function:\_old_virtual_prompt
|
||||
}
|
||||
|
||||
if ($env:VIRTUAL_ENV) {
|
||||
$old_env = split-path $env:VIRTUAL_ENV -leaf
|
||||
remove-item env:VIRTUAL_ENV -erroraction silentlycontinue
|
||||
}
|
||||
|
||||
if ( !$NonDestructive ) {
|
||||
# Self destruct!
|
||||
remove-item function:deactivate
|
||||
}
|
||||
}
|
||||
|
||||
# unset irrelevant variables
|
||||
deactivate -nondestructive
|
||||
|
||||
$VIRTUAL_ENV = $BASE_DIR
|
||||
$env:VIRTUAL_ENV = $VIRTUAL_ENV
|
||||
|
||||
$global:_OLD_VIRTUAL_PATH = $env:PATH
|
||||
$env:PATH = "$env:VIRTUAL_ENV/bin:" + $env:PATH
|
||||
if (! $env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
function global:_old_virtual_prompt { "" }
|
||||
$function:_old_virtual_prompt = $function:prompt
|
||||
function global:prompt {
|
||||
# Add a prefix to the current prompt, but don't discard it.
|
||||
write-host "($(split-path $env:VIRTUAL_ENV -leaf)) " -nonewline
|
||||
& $function:_old_virtual_prompt
|
||||
}
|
||||
}
|
||||
|
||||
# SIG # Begin signature block
|
||||
# MIISeAYJKoZIhvcNAQcCoIISaTCCEmUCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
|
||||
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
|
||||
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUS5reBwSg3zOUwhXf2jPChZzf
|
||||
# yPmggg6tMIIGcDCCBFigAwIBAgIBJDANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQG
|
||||
# EwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERp
|
||||
# Z2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2Vy
|
||||
# dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjIwMTQ2WhcNMTcxMDI0MjIw
|
||||
# MTQ2WjCBjDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzAp
|
||||
# BgNVBAsTIlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNV
|
||||
# BAMTL1N0YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgT2JqZWN0
|
||||
# IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyiOLIjUemqAbPJ1J
|
||||
# 0D8MlzgWKbr4fYlbRVjvhHDtfhFN6RQxq0PjTQxRgWzwFQNKJCdU5ftKoM5N4YSj
|
||||
# Id6ZNavcSa6/McVnhDAQm+8H3HWoD030NVOxbjgD/Ih3HaV3/z9159nnvyxQEckR
|
||||
# ZfpJB2Kfk6aHqW3JnSvRe+XVZSufDVCe/vtxGSEwKCaNrsLc9pboUoYIC3oyzWoU
|
||||
# TZ65+c0H4paR8c8eK/mC914mBo6N0dQ512/bkSdaeY9YaQpGtW/h/W/FkbQRT3sC
|
||||
# pttLVlIjnkuY4r9+zvqhToPjxcfDYEf+XD8VGkAqle8Aa8hQ+M1qGdQjAye8OzbV
|
||||
# uUOw7wIDAQABo4IB6TCCAeUwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
|
||||
# AQYwHQYDVR0OBBYEFNBOD0CZbLhLGW87KLjg44gHNKq3MB8GA1UdIwQYMBaAFE4L
|
||||
# 7xqkQFulF2mHMMo0aEPQQa7yMD0GCCsGAQUFBwEBBDEwLzAtBggrBgEFBQcwAoYh
|
||||
# aHR0cDovL3d3dy5zdGFydHNzbC5jb20vc2ZzY2EuY3J0MFsGA1UdHwRUMFIwJ6Al
|
||||
# oCOGIWh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3Nmc2NhLmNybDAnoCWgI4YhaHR0
|
||||
# cDovL2NybC5zdGFydHNzbC5jb20vc2ZzY2EuY3JsMIGABgNVHSAEeTB3MHUGCysG
|
||||
# AQQBgbU3AQIBMGYwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29t
|
||||
# L3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t
|
||||
# L2ludGVybWVkaWF0ZS5wZGYwEQYJYIZIAYb4QgEBBAQDAgABMFAGCWCGSAGG+EIB
|
||||
# DQRDFkFTdGFydENvbSBDbGFzcyAyIFByaW1hcnkgSW50ZXJtZWRpYXRlIE9iamVj
|
||||
# dCBTaWduaW5nIENlcnRpZmljYXRlczANBgkqhkiG9w0BAQUFAAOCAgEAcnMLA3Va
|
||||
# N4OIE9l4QT5OEtZy5PByBit3oHiqQpgVEQo7DHRsjXD5H/IyTivpMikaaeRxIv95
|
||||
# baRd4hoUcMwDj4JIjC3WA9FoNFV31SMljEZa66G8RQECdMSSufgfDYu1XQ+cUKxh
|
||||
# D3EtLGGcFGjjML7EQv2Iol741rEsycXwIXcryxeiMbU2TPi7X3elbwQMc4JFlJ4B
|
||||
# y9FhBzuZB1DV2sN2irGVbC3G/1+S2doPDjL1CaElwRa/T0qkq2vvPxUgryAoCppU
|
||||
# FKViw5yoGYC+z1GaesWWiP1eFKAL0wI7IgSvLzU3y1Vp7vsYaxOVBqZtebFTWRHt
|
||||
# XjCsFrrQBngt0d33QbQRI5mwgzEp7XJ9xu5d6RVWM4TPRUsd+DDZpBHm9mszvi9g
|
||||
# VFb2ZG7qRRXCSqys4+u/NLBPbXi/m/lU00cODQTlC/euwjk9HQtRrXQ/zqsBJS6U
|
||||
# J+eLGw1qOfj+HVBl/ZQpfoLk7IoWlRQvRL1s7oirEaqPZUIWY/grXq9r6jDKAp3L
|
||||
# ZdKQpPOnnogtqlU4f7/kLjEJhrrc98mrOWmVMK/BuFRAfQ5oDUMnVmCzAzLMjKfG
|
||||
# cVW/iMew41yfhgKbwpfzm3LBr1Zv+pEBgcgW6onRLSAn3XHM0eNtz+AkxH6rRf6B
|
||||
# 2mYhLEEGLapH8R1AMAo4BbVFOZR5kXcMCwowggg1MIIHHaADAgECAgIEuDANBgkq
|
||||
# hkiG9w0BAQUFADCBjDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0
|
||||
# ZC4xKzApBgNVBAsTIlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcx
|
||||
# ODA2BgNVBAMTL1N0YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUg
|
||||
# T2JqZWN0IENBMB4XDTExMTIwMzE1MzQxOVoXDTEzMTIwMzE0NTgwN1owgYwxIDAe
|
||||
# BgNVBA0TFzU4MTc5Ni1HaDd4Zkp4a3hRU0lPNEUwMQswCQYDVQQGEwJERTEPMA0G
|
||||
# A1UECBMGQmVybGluMQ8wDQYDVQQHEwZCZXJsaW4xFjAUBgNVBAMTDUphbm5pcyBM
|
||||
# ZWlkZWwxITAfBgkqhkiG9w0BCQEWEmphbm5pc0BsZWlkZWwuaW5mbzCCAiIwDQYJ
|
||||
# KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMcPeABYdN7nPq/AkZ/EkyUBGx/l2Yui
|
||||
# Lfm8ZdLG0ulMb/kQL3fRY7sUjYPyn9S6PhqqlFnNoGHJvbbReCdUC9SIQYmOEjEA
|
||||
# raHfb7MZU10NjO4U2DdGucj2zuO5tYxKizizOJF0e4yRQZVxpUGdvkW/+GLjCNK5
|
||||
# L7mIv3Z1dagxDKHYZT74HXiS4VFUwHF1k36CwfM2vsetdm46bdgSwV+BCMmZICYT
|
||||
# IJAS9UQHD7kP4rik3bFWjUx08NtYYFAVOd/HwBnemUmJe4j3IhZHr0k1+eDG8hDH
|
||||
# KVvPgLJIoEjC4iMFk5GWsg5z2ngk0LLu3JZMtckHsnnmBPHQK8a3opUNd8hdMNJx
|
||||
# gOwKjQt2JZSGUdIEFCKVDqj0FmdnDMPfwy+FNRtpBMl1sz78dUFhSrnM0D8NXrqa
|
||||
# 4rG+2FoOXlmm1rb6AFtpjAKksHRpYcPk2DPGWp/1sWB+dUQkS3gOmwFzyqeTuXpT
|
||||
# 0juqd3iAxOGx1VRFQ1VHLLf3AzV4wljBau26I+tu7iXxesVucSdsdQu293jwc2kN
|
||||
# xK2JyHCoZH+RyytrwS0qw8t7rMOukU9gwP8mn3X6mgWlVUODMcHTULjSiCEtvyZ/
|
||||
# aafcwjUbt4ReEcnmuZtWIha86MTCX7U7e+cnpWG4sIHPnvVTaz9rm8RyBkIxtFCB
|
||||
# nQ3FnoQgyxeJAgMBAAGjggOdMIIDmTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIH
|
||||
# gDAuBgNVHSUBAf8EJDAiBggrBgEFBQcDAwYKKwYBBAGCNwIBFQYKKwYBBAGCNwoD
|
||||
# DTAdBgNVHQ4EFgQUWyCgrIWo8Ifvvm1/YTQIeMU9nc8wHwYDVR0jBBgwFoAU0E4P
|
||||
# QJlsuEsZbzsouODjiAc0qrcwggIhBgNVHSAEggIYMIICFDCCAhAGCysGAQQBgbU3
|
||||
# AQICMIIB/zAuBggrBgEFBQcCARYiaHR0cDovL3d3dy5zdGFydHNzbC5jb20vcG9s
|
||||
# aWN5LnBkZjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5zdGFydHNzbC5jb20vaW50
|
||||
# ZXJtZWRpYXRlLnBkZjCB9wYIKwYBBQUHAgIwgeowJxYgU3RhcnRDb20gQ2VydGlm
|
||||
# aWNhdGlvbiBBdXRob3JpdHkwAwIBARqBvlRoaXMgY2VydGlmaWNhdGUgd2FzIGlz
|
||||
# c3VlZCBhY2NvcmRpbmcgdG8gdGhlIENsYXNzIDIgVmFsaWRhdGlvbiByZXF1aXJl
|
||||
# bWVudHMgb2YgdGhlIFN0YXJ0Q29tIENBIHBvbGljeSwgcmVsaWFuY2Ugb25seSBm
|
||||
# b3IgdGhlIGludGVuZGVkIHB1cnBvc2UgaW4gY29tcGxpYW5jZSBvZiB0aGUgcmVs
|
||||
# eWluZyBwYXJ0eSBvYmxpZ2F0aW9ucy4wgZwGCCsGAQUFBwICMIGPMCcWIFN0YXJ0
|
||||
# Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MAMCAQIaZExpYWJpbGl0eSBhbmQg
|
||||
# d2FycmFudGllcyBhcmUgbGltaXRlZCEgU2VlIHNlY3Rpb24gIkxlZ2FsIGFuZCBM
|
||||
# aW1pdGF0aW9ucyIgb2YgdGhlIFN0YXJ0Q29tIENBIHBvbGljeS4wNgYDVR0fBC8w
|
||||
# LTAroCmgJ4YlaHR0cDovL2NybC5zdGFydHNzbC5jb20vY3J0YzItY3JsLmNybDCB
|
||||
# iQYIKwYBBQUHAQEEfTB7MDcGCCsGAQUFBzABhitodHRwOi8vb2NzcC5zdGFydHNz
|
||||
# bC5jb20vc3ViL2NsYXNzMi9jb2RlL2NhMEAGCCsGAQUFBzAChjRodHRwOi8vYWlh
|
||||
# LnN0YXJ0c3NsLmNvbS9jZXJ0cy9zdWIuY2xhc3MyLmNvZGUuY2EuY3J0MCMGA1Ud
|
||||
# EgQcMBqGGGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tLzANBgkqhkiG9w0BAQUFAAOC
|
||||
# AQEAhrzEV6zwoEtKjnFRhCsjwiPykVpo5Eiye77Ve801rQDiRKgSCCiW6g3HqedL
|
||||
# OtaSs65Sj2pm3Viea4KR0TECLcbCTgsdaHqw2x1yXwWBQWZEaV6EB05lIwfr94P1
|
||||
# SFpV43zkuc+bbmA3+CRK45LOcCNH5Tqq7VGTCAK5iM7tvHwFlbQRl+I6VEL2mjpF
|
||||
# NsuRjDOVrv/9qw/a22YJ9R7Y1D0vUSs3IqZx2KMUaYDP7H2mSRxJO2nADQZBtriF
|
||||
# gTyfD3lYV12MlIi5CQwe3QC6DrrfSMP33i5Wa/OFJiQ27WPxmScYVhiqozpImFT4
|
||||
# PU9goiBv9RKXdgTmZE1PN0NQ5jGCAzUwggMxAgEBMIGTMIGMMQswCQYDVQQGEwJJ
|
||||
# TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0
|
||||
# YWwgQ2VydGlmaWNhdGUgU2lnbmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3Mg
|
||||
# MiBQcmltYXJ5IEludGVybWVkaWF0ZSBPYmplY3QgQ0ECAgS4MAkGBSsOAwIaBQCg
|
||||
# eDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEE
|
||||
# AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJ
|
||||
# BDEWBBRVGw0FDSiaIi38dWteRUAg/9Pr6DANBgkqhkiG9w0BAQEFAASCAgCInvOZ
|
||||
# FdaNFzbf6trmFDZKMojyx3UjKMCqNjHVBbuKY0qXwFC/ElYDV1ShJ2CBZbdurydO
|
||||
# OQ6cIQ0KREOCwmX/xB49IlLHHUxNhEkVv7HGU3EKAFf9IBt9Yr7jikiR9cjIsfHK
|
||||
# 4cjkoKJL7g28yEpLLkHt1eo37f1Ga9lDWEa5Zq3U5yX+IwXhrUBm1h8Xr033FhTR
|
||||
# VEpuSz6LHtbrL/zgJnCzJ2ahjtJoYevdcWiNXffosJHFaSfYDDbiNsPRDH/1avmb
|
||||
# 5j/7BhP8BcBaR6Fp8tFbNGIcWHHGcjqLMnTc4w13b7b4pDhypqElBa4+lCmwdvv9
|
||||
# GydYtRgPz8GHeoBoKj30YBlMzRIfFYaIFGIC4Ai3UEXkuH9TxYohVbGm/W0Kl4Lb
|
||||
# RJ1FwiVcLcTOJdgNId2vQvKc+jtNrjcg5SP9h2v/C4aTx8tyc6tE3TOPh2f9b8DL
|
||||
# S+SbVArJpuJqrPTxDDoO1QNjTgLcdVYeZDE+r/NjaGZ6cMSd8db3EaG3ijD/0bud
|
||||
# SItbm/OlNVbQOFRR76D+ZNgPcU5iNZ3bmvQQIg6aSB9MHUpIE/SeCkNl9YeVk1/1
|
||||
# GFULgNMRmIYP4KLvu9ylh5Gu3hvD5VNhH6+FlXANwFy07uXks5uF8mfZVxVCnodG
|
||||
# xkNCx+6PsrA5Z7WP4pXcmYnMn97npP/Q9EHJWw==
|
||||
# SIG # End signature block
|
|
@ -0,0 +1,36 @@
|
|||
"""By using execfile(this_file, dict(__file__=this_file)) you will
|
||||
activate this virtualenv environment.
|
||||
|
||||
This can be used when you must use an existing Python interpreter, not
|
||||
the virtualenv bin/python
|
||||
"""
|
||||
|
||||
try:
|
||||
__file__
|
||||
except NameError:
|
||||
raise AssertionError(
|
||||
"You must run this like execfile('path/to/activate_this.py', dict(__file__='path/to/activate_this.py'))"
|
||||
)
|
||||
import os
|
||||
import site
|
||||
import sys
|
||||
|
||||
old_os_path = os.environ.get("PATH", "")
|
||||
os.environ["PATH"] = os.path.dirname(os.path.abspath(__file__)) + os.pathsep + old_os_path
|
||||
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if sys.platform == "win32":
|
||||
site_packages = os.path.join(base, "Lib", "site-packages")
|
||||
else:
|
||||
site_packages = os.path.join(base, "lib", "python%s" % sys.version[:3], "site-packages")
|
||||
prev_sys_path = list(sys.path)
|
||||
|
||||
site.addsitedir(site_packages)
|
||||
sys.real_prefix = sys.prefix
|
||||
sys.prefix = base
|
||||
# Move the added items to the front of the path:
|
||||
new_sys_path = []
|
||||
for item in list(sys.path):
|
||||
if item not in prev_sys_path:
|
||||
new_sys_path.append(item)
|
||||
sys.path.remove(item)
|
||||
sys.path[:0] = new_sys_path
|
|
@ -0,0 +1,163 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
########################################################
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
__requires__ = ['ansible']
|
||||
try:
|
||||
import pkg_resources
|
||||
except Exception:
|
||||
# Use pkg_resources to find the correct versions of libraries and set
|
||||
# sys.path appropriately when there are multiversion installs. But we
|
||||
# have code that better expresses the errors in the places where the code
|
||||
# is actually used (the deps are optional for many code paths) so we don't
|
||||
# want to fail here.
|
||||
pass
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
# Used for determining if the system is running a new enough python version
|
||||
# and should only restrict on our documented minimum versions
|
||||
_PY3_MIN = sys.version_info[:2] >= (3, 5)
|
||||
_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
|
||||
_PY_MIN = _PY3_MIN or _PY2_MIN
|
||||
if not _PY_MIN:
|
||||
raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines()))
|
||||
|
||||
|
||||
class LastResort(object):
|
||||
# OUTPUT OF LAST RESORT
|
||||
def display(self, msg, log_only=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def error(self, msg, wrap_text=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
display = LastResort()
|
||||
|
||||
try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace
|
||||
import ansible.constants as C
|
||||
from ansible.utils.display import Display
|
||||
except AnsibleOptionsError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
sys.exit(5)
|
||||
|
||||
cli = None
|
||||
me = os.path.basename(sys.argv[0])
|
||||
|
||||
try:
|
||||
display = Display()
|
||||
display.debug("starting run")
|
||||
|
||||
sub = None
|
||||
target = me.split('-')
|
||||
if target[-1][0].isdigit():
|
||||
# Remove any version or python version info as downstreams
|
||||
# sometimes add that
|
||||
target = target[:-1]
|
||||
|
||||
if len(target) > 1:
|
||||
sub = target[1]
|
||||
myclass = "%sCLI" % sub.capitalize()
|
||||
elif target[0] == 'ansible':
|
||||
sub = 'adhoc'
|
||||
myclass = 'AdHocCLI'
|
||||
else:
|
||||
raise AnsibleError("Unknown Ansible alias: %s" % me)
|
||||
|
||||
try:
|
||||
mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
|
||||
except ImportError as e:
|
||||
# ImportError members have changed in py3
|
||||
if 'msg' in dir(e):
|
||||
msg = e.msg
|
||||
else:
|
||||
msg = e.message
|
||||
if msg.endswith(' %s' % sub):
|
||||
raise AnsibleError("Ansible sub-program not implemented: %s" % me)
|
||||
else:
|
||||
raise
|
||||
|
||||
try:
|
||||
args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv]
|
||||
except UnicodeError:
|
||||
display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8')
|
||||
display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc()))
|
||||
exit_code = 6
|
||||
else:
|
||||
cli = mycli(args)
|
||||
cli.parse()
|
||||
exit_code = cli.run()
|
||||
|
||||
except AnsibleOptionsError as e:
|
||||
cli.parser.print_help()
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 5
|
||||
except AnsibleParserError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 4
|
||||
# TQM takes care of these, but leaving comment to reserve the exit codes
|
||||
# except AnsibleHostUnreachable as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 3
|
||||
# except AnsibleHostFailed as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 2
|
||||
except AnsibleError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 1
|
||||
except KeyboardInterrupt:
|
||||
display.error("User interrupted execution")
|
||||
exit_code = 99
|
||||
except Exception as e:
|
||||
if C.DEFAULT_DEBUG:
|
||||
# Show raw stacktraces in debug mode, It also allow pdb to
|
||||
# enter post mortem mode.
|
||||
raise
|
||||
have_cli_options = cli is not None and cli.options is not None
|
||||
display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False)
|
||||
if not have_cli_options or have_cli_options and cli.options.verbosity > 2:
|
||||
log_only = False
|
||||
if hasattr(e, 'orig_exc'):
|
||||
display.vvv('\nexception type: %s' % to_text(type(e.orig_exc)))
|
||||
why = to_text(e.orig_exc)
|
||||
if to_text(e) != why:
|
||||
display.vvv('\noriginal msg: %s' % why)
|
||||
else:
|
||||
display.display("to see the full traceback, use -vvv")
|
||||
log_only = True
|
||||
display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only)
|
||||
exit_code = 250
|
||||
finally:
|
||||
# Remove ansible tmpdir
|
||||
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
|
||||
|
||||
sys.exit(exit_code)
|
|
@ -0,0 +1,163 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
########################################################
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
__requires__ = ['ansible']
|
||||
try:
|
||||
import pkg_resources
|
||||
except Exception:
|
||||
# Use pkg_resources to find the correct versions of libraries and set
|
||||
# sys.path appropriately when there are multiversion installs. But we
|
||||
# have code that better expresses the errors in the places where the code
|
||||
# is actually used (the deps are optional for many code paths) so we don't
|
||||
# want to fail here.
|
||||
pass
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
# Used for determining if the system is running a new enough python version
|
||||
# and should only restrict on our documented minimum versions
|
||||
_PY3_MIN = sys.version_info[:2] >= (3, 5)
|
||||
_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
|
||||
_PY_MIN = _PY3_MIN or _PY2_MIN
|
||||
if not _PY_MIN:
|
||||
raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines()))
|
||||
|
||||
|
||||
class LastResort(object):
|
||||
# OUTPUT OF LAST RESORT
|
||||
def display(self, msg, log_only=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def error(self, msg, wrap_text=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
display = LastResort()
|
||||
|
||||
try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace
|
||||
import ansible.constants as C
|
||||
from ansible.utils.display import Display
|
||||
except AnsibleOptionsError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
sys.exit(5)
|
||||
|
||||
cli = None
|
||||
me = os.path.basename(sys.argv[0])
|
||||
|
||||
try:
|
||||
display = Display()
|
||||
display.debug("starting run")
|
||||
|
||||
sub = None
|
||||
target = me.split('-')
|
||||
if target[-1][0].isdigit():
|
||||
# Remove any version or python version info as downstreams
|
||||
# sometimes add that
|
||||
target = target[:-1]
|
||||
|
||||
if len(target) > 1:
|
||||
sub = target[1]
|
||||
myclass = "%sCLI" % sub.capitalize()
|
||||
elif target[0] == 'ansible':
|
||||
sub = 'adhoc'
|
||||
myclass = 'AdHocCLI'
|
||||
else:
|
||||
raise AnsibleError("Unknown Ansible alias: %s" % me)
|
||||
|
||||
try:
|
||||
mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
|
||||
except ImportError as e:
|
||||
# ImportError members have changed in py3
|
||||
if 'msg' in dir(e):
|
||||
msg = e.msg
|
||||
else:
|
||||
msg = e.message
|
||||
if msg.endswith(' %s' % sub):
|
||||
raise AnsibleError("Ansible sub-program not implemented: %s" % me)
|
||||
else:
|
||||
raise
|
||||
|
||||
try:
|
||||
args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv]
|
||||
except UnicodeError:
|
||||
display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8')
|
||||
display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc()))
|
||||
exit_code = 6
|
||||
else:
|
||||
cli = mycli(args)
|
||||
cli.parse()
|
||||
exit_code = cli.run()
|
||||
|
||||
except AnsibleOptionsError as e:
|
||||
cli.parser.print_help()
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 5
|
||||
except AnsibleParserError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 4
|
||||
# TQM takes care of these, but leaving comment to reserve the exit codes
|
||||
# except AnsibleHostUnreachable as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 3
|
||||
# except AnsibleHostFailed as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 2
|
||||
except AnsibleError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 1
|
||||
except KeyboardInterrupt:
|
||||
display.error("User interrupted execution")
|
||||
exit_code = 99
|
||||
except Exception as e:
|
||||
if C.DEFAULT_DEBUG:
|
||||
# Show raw stacktraces in debug mode, It also allow pdb to
|
||||
# enter post mortem mode.
|
||||
raise
|
||||
have_cli_options = cli is not None and cli.options is not None
|
||||
display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False)
|
||||
if not have_cli_options or have_cli_options and cli.options.verbosity > 2:
|
||||
log_only = False
|
||||
if hasattr(e, 'orig_exc'):
|
||||
display.vvv('\nexception type: %s' % to_text(type(e.orig_exc)))
|
||||
why = to_text(e.orig_exc)
|
||||
if to_text(e) != why:
|
||||
display.vvv('\noriginal msg: %s' % why)
|
||||
else:
|
||||
display.display("to see the full traceback, use -vvv")
|
||||
log_only = True
|
||||
display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only)
|
||||
exit_code = 250
|
||||
finally:
|
||||
# Remove ansible tmpdir
|
||||
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
|
||||
|
||||
sys.exit(exit_code)
|
|
@ -0,0 +1,328 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# Copyright: (c) 2017, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
|
||||
__metaclass__ = type
|
||||
__requires__ = ['ansible']
|
||||
|
||||
try:
|
||||
import pkg_resources
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
import fcntl
|
||||
import hashlib
|
||||
import os
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
import errno
|
||||
import json
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
from ansible import constants as C
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.module_utils.six import PY3
|
||||
from ansible.module_utils.six.moves import cPickle, StringIO
|
||||
from ansible.module_utils.connection import Connection, ConnectionError, send_data, recv_data
|
||||
from ansible.module_utils.service import fork_process
|
||||
from ansible.playbook.play_context import PlayContext
|
||||
from ansible.plugins.loader import connection_loader
|
||||
from ansible.utils.path import unfrackpath, makedirs_safe
|
||||
from ansible.utils.display import Display
|
||||
from ansible.utils.jsonrpc import JsonRpcServer
|
||||
|
||||
|
||||
def read_stream(byte_stream):
|
||||
size = int(byte_stream.readline().strip())
|
||||
|
||||
data = byte_stream.read(size)
|
||||
if len(data) < size:
|
||||
raise Exception("EOF found before data was complete")
|
||||
|
||||
data_hash = to_text(byte_stream.readline().strip())
|
||||
if data_hash != hashlib.sha1(data).hexdigest():
|
||||
raise Exception("Read {0} bytes, but data did not match checksum".format(size))
|
||||
|
||||
# restore escaped loose \r characters
|
||||
data = data.replace(br'\r', b'\r')
|
||||
|
||||
return data
|
||||
|
||||
|
||||
@contextmanager
|
||||
def file_lock(lock_path):
|
||||
"""
|
||||
Uses contextmanager to create and release a file lock based on the
|
||||
given path. This allows us to create locks using `with file_lock()`
|
||||
to prevent deadlocks related to failure to unlock properly.
|
||||
"""
|
||||
|
||||
lock_fd = os.open(lock_path, os.O_RDWR | os.O_CREAT, 0o600)
|
||||
fcntl.lockf(lock_fd, fcntl.LOCK_EX)
|
||||
yield
|
||||
fcntl.lockf(lock_fd, fcntl.LOCK_UN)
|
||||
os.close(lock_fd)
|
||||
|
||||
|
||||
class ConnectionProcess(object):
|
||||
'''
|
||||
The connection process wraps around a Connection object that manages
|
||||
the connection to a remote device that persists over the playbook
|
||||
'''
|
||||
def __init__(self, fd, play_context, socket_path, original_path, ansible_playbook_pid=None):
|
||||
self.play_context = play_context
|
||||
self.socket_path = socket_path
|
||||
self.original_path = original_path
|
||||
|
||||
self.fd = fd
|
||||
self.exception = None
|
||||
|
||||
self.srv = JsonRpcServer()
|
||||
self.sock = None
|
||||
|
||||
self.connection = None
|
||||
self._ansible_playbook_pid = ansible_playbook_pid
|
||||
|
||||
def start(self, variables):
|
||||
try:
|
||||
messages = list()
|
||||
result = {}
|
||||
|
||||
messages.append('control socket path is %s' % self.socket_path)
|
||||
|
||||
# If this is a relative path (~ gets expanded later) then plug the
|
||||
# key's path on to the directory we originally came from, so we can
|
||||
# find it now that our cwd is /
|
||||
if self.play_context.private_key_file and self.play_context.private_key_file[0] not in '~/':
|
||||
self.play_context.private_key_file = os.path.join(self.original_path, self.play_context.private_key_file)
|
||||
self.connection = connection_loader.get(self.play_context.connection, self.play_context, '/dev/null',
|
||||
ansible_playbook_pid=self._ansible_playbook_pid)
|
||||
self.connection.set_options(var_options=variables)
|
||||
self.connection._connect()
|
||||
|
||||
self.connection._socket_path = self.socket_path
|
||||
self.srv.register(self.connection)
|
||||
messages.extend(sys.stdout.getvalue().splitlines())
|
||||
messages.append('connection to remote device started successfully')
|
||||
|
||||
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self.sock.bind(self.socket_path)
|
||||
self.sock.listen(1)
|
||||
messages.append('local domain socket listeners started successfully')
|
||||
except Exception as exc:
|
||||
result['error'] = to_text(exc)
|
||||
result['exception'] = traceback.format_exc()
|
||||
finally:
|
||||
result['messages'] = messages
|
||||
self.fd.write(json.dumps(result))
|
||||
self.fd.close()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
while self.connection.connected:
|
||||
signal.signal(signal.SIGALRM, self.connect_timeout)
|
||||
signal.signal(signal.SIGTERM, self.handler)
|
||||
signal.alarm(self.connection.get_option('persistent_connect_timeout'))
|
||||
|
||||
self.exception = None
|
||||
(s, addr) = self.sock.accept()
|
||||
signal.alarm(0)
|
||||
|
||||
signal.signal(signal.SIGALRM, self.command_timeout)
|
||||
while True:
|
||||
data = recv_data(s)
|
||||
if not data:
|
||||
break
|
||||
|
||||
signal.alarm(self.connection.get_option('persistent_command_timeout'))
|
||||
resp = self.srv.handle_request(data)
|
||||
signal.alarm(0)
|
||||
|
||||
send_data(s, to_bytes(resp))
|
||||
|
||||
s.close()
|
||||
|
||||
except Exception as e:
|
||||
# socket.accept() will raise EINTR if the socket.close() is called
|
||||
if hasattr(e, 'errno'):
|
||||
if e.errno != errno.EINTR:
|
||||
self.exception = traceback.format_exc()
|
||||
else:
|
||||
self.exception = traceback.format_exc()
|
||||
|
||||
finally:
|
||||
# allow time for any exception msg send over socket to receive at other end before shutting down
|
||||
time.sleep(0.1)
|
||||
|
||||
# when done, close the connection properly and cleanup the socket file so it can be recreated
|
||||
self.shutdown()
|
||||
|
||||
def connect_timeout(self, signum, frame):
|
||||
msg = 'persistent connection idle timeout triggered, timeout value is %s secs.\nSee the timeout setting options in the Network Debug and ' \
|
||||
'Troubleshooting Guide.' % self.connection.get_option('persistent_connect_timeout')
|
||||
display.display(msg, log_only=True)
|
||||
raise Exception(msg)
|
||||
|
||||
def command_timeout(self, signum, frame):
|
||||
msg = 'command timeout triggered, timeout value is %s secs.\nSee the timeout setting options in the Network Debug and Troubleshooting Guide.'\
|
||||
% self.connection.get_option('persistent_command_timeout')
|
||||
display.display(msg, log_only=True)
|
||||
raise Exception(msg)
|
||||
|
||||
def handler(self, signum, frame):
|
||||
msg = 'signal handler called with signal %s.' % signum
|
||||
display.display(msg, log_only=True)
|
||||
raise Exception(msg)
|
||||
|
||||
def shutdown(self):
|
||||
""" Shuts down the local domain socket
|
||||
"""
|
||||
lock_path = unfrackpath("%s/.ansible_pc_lock_%s" % os.path.split(self.socket_path))
|
||||
if os.path.exists(self.socket_path):
|
||||
try:
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
if self.connection:
|
||||
self.connection.close()
|
||||
except:
|
||||
pass
|
||||
finally:
|
||||
if os.path.exists(self.socket_path):
|
||||
os.remove(self.socket_path)
|
||||
setattr(self.connection, '_socket_path', None)
|
||||
setattr(self.connection, '_connected', False)
|
||||
|
||||
if os.path.exists(lock_path):
|
||||
os.remove(lock_path)
|
||||
|
||||
display.display('shutdown complete', log_only=True)
|
||||
|
||||
|
||||
def main():
|
||||
""" Called to initiate the connect to the remote device
|
||||
"""
|
||||
rc = 0
|
||||
result = {}
|
||||
messages = list()
|
||||
socket_path = None
|
||||
|
||||
# Need stdin as a byte stream
|
||||
if PY3:
|
||||
stdin = sys.stdin.buffer
|
||||
else:
|
||||
stdin = sys.stdin
|
||||
|
||||
# Note: update the below log capture code after Display.display() is refactored.
|
||||
saved_stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
|
||||
try:
|
||||
# read the play context data via stdin, which means depickling it
|
||||
vars_data = read_stream(stdin)
|
||||
init_data = read_stream(stdin)
|
||||
|
||||
if PY3:
|
||||
pc_data = cPickle.loads(init_data, encoding='bytes')
|
||||
variables = cPickle.loads(vars_data, encoding='bytes')
|
||||
else:
|
||||
pc_data = cPickle.loads(init_data)
|
||||
variables = cPickle.loads(vars_data)
|
||||
|
||||
play_context = PlayContext()
|
||||
play_context.deserialize(pc_data)
|
||||
display.verbosity = play_context.verbosity
|
||||
|
||||
except Exception as e:
|
||||
rc = 1
|
||||
result.update({
|
||||
'error': to_text(e),
|
||||
'exception': traceback.format_exc()
|
||||
})
|
||||
|
||||
if rc == 0:
|
||||
ssh = connection_loader.get('ssh', class_only=True)
|
||||
ansible_playbook_pid = sys.argv[1]
|
||||
cp = ssh._create_control_path(play_context.remote_addr, play_context.port, play_context.remote_user, play_context.connection, ansible_playbook_pid)
|
||||
|
||||
# create the persistent connection dir if need be and create the paths
|
||||
# which we will be using later
|
||||
tmp_path = unfrackpath(C.PERSISTENT_CONTROL_PATH_DIR)
|
||||
makedirs_safe(tmp_path)
|
||||
|
||||
socket_path = unfrackpath(cp % dict(directory=tmp_path))
|
||||
lock_path = unfrackpath("%s/.ansible_pc_lock_%s" % os.path.split(socket_path))
|
||||
|
||||
with file_lock(lock_path):
|
||||
if not os.path.exists(socket_path):
|
||||
messages.append('local domain socket does not exist, starting it')
|
||||
original_path = os.getcwd()
|
||||
r, w = os.pipe()
|
||||
pid = fork_process()
|
||||
|
||||
if pid == 0:
|
||||
try:
|
||||
os.close(r)
|
||||
wfd = os.fdopen(w, 'w')
|
||||
process = ConnectionProcess(wfd, play_context, socket_path, original_path, ansible_playbook_pid)
|
||||
process.start(variables)
|
||||
except Exception:
|
||||
messages.append(traceback.format_exc())
|
||||
rc = 1
|
||||
|
||||
if rc == 0:
|
||||
process.run()
|
||||
else:
|
||||
process.shutdown()
|
||||
|
||||
sys.exit(rc)
|
||||
|
||||
else:
|
||||
os.close(w)
|
||||
rfd = os.fdopen(r, 'r')
|
||||
data = json.loads(rfd.read())
|
||||
messages.extend(data.pop('messages'))
|
||||
result.update(data)
|
||||
|
||||
else:
|
||||
messages.append('found existing local domain socket, using it!')
|
||||
conn = Connection(socket_path)
|
||||
conn.set_options(var_options=variables)
|
||||
pc_data = to_text(init_data)
|
||||
try:
|
||||
messages.extend(conn.update_play_context(pc_data))
|
||||
except Exception as exc:
|
||||
# Only network_cli has update_play context, so missing this is
|
||||
# not fatal e.g. netconf
|
||||
if isinstance(exc, ConnectionError) and getattr(exc, 'code', None) == -32601:
|
||||
pass
|
||||
else:
|
||||
result.update({
|
||||
'error': to_text(exc),
|
||||
'exception': traceback.format_exc()
|
||||
})
|
||||
|
||||
messages.append(sys.stdout.getvalue())
|
||||
result.update({
|
||||
'messages': messages,
|
||||
'socket_path': socket_path
|
||||
})
|
||||
|
||||
sys.stdout = saved_stdout
|
||||
if 'exception' in result:
|
||||
rc = 1
|
||||
sys.stderr.write(json.dumps(result))
|
||||
else:
|
||||
rc = 0
|
||||
sys.stdout.write(json.dumps(result))
|
||||
|
||||
sys.exit(rc)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
display = Display()
|
||||
main()
|
|
@ -0,0 +1,163 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
########################################################
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
__requires__ = ['ansible']
|
||||
try:
|
||||
import pkg_resources
|
||||
except Exception:
|
||||
# Use pkg_resources to find the correct versions of libraries and set
|
||||
# sys.path appropriately when there are multiversion installs. But we
|
||||
# have code that better expresses the errors in the places where the code
|
||||
# is actually used (the deps are optional for many code paths) so we don't
|
||||
# want to fail here.
|
||||
pass
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
# Used for determining if the system is running a new enough python version
|
||||
# and should only restrict on our documented minimum versions
|
||||
_PY3_MIN = sys.version_info[:2] >= (3, 5)
|
||||
_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
|
||||
_PY_MIN = _PY3_MIN or _PY2_MIN
|
||||
if not _PY_MIN:
|
||||
raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines()))
|
||||
|
||||
|
||||
class LastResort(object):
|
||||
# OUTPUT OF LAST RESORT
|
||||
def display(self, msg, log_only=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def error(self, msg, wrap_text=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
display = LastResort()
|
||||
|
||||
try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace
|
||||
import ansible.constants as C
|
||||
from ansible.utils.display import Display
|
||||
except AnsibleOptionsError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
sys.exit(5)
|
||||
|
||||
cli = None
|
||||
me = os.path.basename(sys.argv[0])
|
||||
|
||||
try:
|
||||
display = Display()
|
||||
display.debug("starting run")
|
||||
|
||||
sub = None
|
||||
target = me.split('-')
|
||||
if target[-1][0].isdigit():
|
||||
# Remove any version or python version info as downstreams
|
||||
# sometimes add that
|
||||
target = target[:-1]
|
||||
|
||||
if len(target) > 1:
|
||||
sub = target[1]
|
||||
myclass = "%sCLI" % sub.capitalize()
|
||||
elif target[0] == 'ansible':
|
||||
sub = 'adhoc'
|
||||
myclass = 'AdHocCLI'
|
||||
else:
|
||||
raise AnsibleError("Unknown Ansible alias: %s" % me)
|
||||
|
||||
try:
|
||||
mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
|
||||
except ImportError as e:
|
||||
# ImportError members have changed in py3
|
||||
if 'msg' in dir(e):
|
||||
msg = e.msg
|
||||
else:
|
||||
msg = e.message
|
||||
if msg.endswith(' %s' % sub):
|
||||
raise AnsibleError("Ansible sub-program not implemented: %s" % me)
|
||||
else:
|
||||
raise
|
||||
|
||||
try:
|
||||
args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv]
|
||||
except UnicodeError:
|
||||
display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8')
|
||||
display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc()))
|
||||
exit_code = 6
|
||||
else:
|
||||
cli = mycli(args)
|
||||
cli.parse()
|
||||
exit_code = cli.run()
|
||||
|
||||
except AnsibleOptionsError as e:
|
||||
cli.parser.print_help()
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 5
|
||||
except AnsibleParserError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 4
|
||||
# TQM takes care of these, but leaving comment to reserve the exit codes
|
||||
# except AnsibleHostUnreachable as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 3
|
||||
# except AnsibleHostFailed as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 2
|
||||
except AnsibleError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 1
|
||||
except KeyboardInterrupt:
|
||||
display.error("User interrupted execution")
|
||||
exit_code = 99
|
||||
except Exception as e:
|
||||
if C.DEFAULT_DEBUG:
|
||||
# Show raw stacktraces in debug mode, It also allow pdb to
|
||||
# enter post mortem mode.
|
||||
raise
|
||||
have_cli_options = cli is not None and cli.options is not None
|
||||
display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False)
|
||||
if not have_cli_options or have_cli_options and cli.options.verbosity > 2:
|
||||
log_only = False
|
||||
if hasattr(e, 'orig_exc'):
|
||||
display.vvv('\nexception type: %s' % to_text(type(e.orig_exc)))
|
||||
why = to_text(e.orig_exc)
|
||||
if to_text(e) != why:
|
||||
display.vvv('\noriginal msg: %s' % why)
|
||||
else:
|
||||
display.display("to see the full traceback, use -vvv")
|
||||
log_only = True
|
||||
display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only)
|
||||
exit_code = 250
|
||||
finally:
|
||||
# Remove ansible tmpdir
|
||||
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
|
||||
|
||||
sys.exit(exit_code)
|
|
@ -0,0 +1,163 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
########################################################
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
__requires__ = ['ansible']
|
||||
try:
|
||||
import pkg_resources
|
||||
except Exception:
|
||||
# Use pkg_resources to find the correct versions of libraries and set
|
||||
# sys.path appropriately when there are multiversion installs. But we
|
||||
# have code that better expresses the errors in the places where the code
|
||||
# is actually used (the deps are optional for many code paths) so we don't
|
||||
# want to fail here.
|
||||
pass
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
# Used for determining if the system is running a new enough python version
|
||||
# and should only restrict on our documented minimum versions
|
||||
_PY3_MIN = sys.version_info[:2] >= (3, 5)
|
||||
_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
|
||||
_PY_MIN = _PY3_MIN or _PY2_MIN
|
||||
if not _PY_MIN:
|
||||
raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines()))
|
||||
|
||||
|
||||
class LastResort(object):
|
||||
# OUTPUT OF LAST RESORT
|
||||
def display(self, msg, log_only=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def error(self, msg, wrap_text=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
display = LastResort()
|
||||
|
||||
try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace
|
||||
import ansible.constants as C
|
||||
from ansible.utils.display import Display
|
||||
except AnsibleOptionsError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
sys.exit(5)
|
||||
|
||||
cli = None
|
||||
me = os.path.basename(sys.argv[0])
|
||||
|
||||
try:
|
||||
display = Display()
|
||||
display.debug("starting run")
|
||||
|
||||
sub = None
|
||||
target = me.split('-')
|
||||
if target[-1][0].isdigit():
|
||||
# Remove any version or python version info as downstreams
|
||||
# sometimes add that
|
||||
target = target[:-1]
|
||||
|
||||
if len(target) > 1:
|
||||
sub = target[1]
|
||||
myclass = "%sCLI" % sub.capitalize()
|
||||
elif target[0] == 'ansible':
|
||||
sub = 'adhoc'
|
||||
myclass = 'AdHocCLI'
|
||||
else:
|
||||
raise AnsibleError("Unknown Ansible alias: %s" % me)
|
||||
|
||||
try:
|
||||
mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
|
||||
except ImportError as e:
|
||||
# ImportError members have changed in py3
|
||||
if 'msg' in dir(e):
|
||||
msg = e.msg
|
||||
else:
|
||||
msg = e.message
|
||||
if msg.endswith(' %s' % sub):
|
||||
raise AnsibleError("Ansible sub-program not implemented: %s" % me)
|
||||
else:
|
||||
raise
|
||||
|
||||
try:
|
||||
args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv]
|
||||
except UnicodeError:
|
||||
display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8')
|
||||
display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc()))
|
||||
exit_code = 6
|
||||
else:
|
||||
cli = mycli(args)
|
||||
cli.parse()
|
||||
exit_code = cli.run()
|
||||
|
||||
except AnsibleOptionsError as e:
|
||||
cli.parser.print_help()
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 5
|
||||
except AnsibleParserError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 4
|
||||
# TQM takes care of these, but leaving comment to reserve the exit codes
|
||||
# except AnsibleHostUnreachable as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 3
|
||||
# except AnsibleHostFailed as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 2
|
||||
except AnsibleError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 1
|
||||
except KeyboardInterrupt:
|
||||
display.error("User interrupted execution")
|
||||
exit_code = 99
|
||||
except Exception as e:
|
||||
if C.DEFAULT_DEBUG:
|
||||
# Show raw stacktraces in debug mode, It also allow pdb to
|
||||
# enter post mortem mode.
|
||||
raise
|
||||
have_cli_options = cli is not None and cli.options is not None
|
||||
display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False)
|
||||
if not have_cli_options or have_cli_options and cli.options.verbosity > 2:
|
||||
log_only = False
|
||||
if hasattr(e, 'orig_exc'):
|
||||
display.vvv('\nexception type: %s' % to_text(type(e.orig_exc)))
|
||||
why = to_text(e.orig_exc)
|
||||
if to_text(e) != why:
|
||||
display.vvv('\noriginal msg: %s' % why)
|
||||
else:
|
||||
display.display("to see the full traceback, use -vvv")
|
||||
log_only = True
|
||||
display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only)
|
||||
exit_code = 250
|
||||
finally:
|
||||
# Remove ansible tmpdir
|
||||
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
|
||||
|
||||
sys.exit(exit_code)
|
|
@ -0,0 +1,163 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
########################################################
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
__requires__ = ['ansible']
|
||||
try:
|
||||
import pkg_resources
|
||||
except Exception:
|
||||
# Use pkg_resources to find the correct versions of libraries and set
|
||||
# sys.path appropriately when there are multiversion installs. But we
|
||||
# have code that better expresses the errors in the places where the code
|
||||
# is actually used (the deps are optional for many code paths) so we don't
|
||||
# want to fail here.
|
||||
pass
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
# Used for determining if the system is running a new enough python version
|
||||
# and should only restrict on our documented minimum versions
|
||||
_PY3_MIN = sys.version_info[:2] >= (3, 5)
|
||||
_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
|
||||
_PY_MIN = _PY3_MIN or _PY2_MIN
|
||||
if not _PY_MIN:
|
||||
raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines()))
|
||||
|
||||
|
||||
class LastResort(object):
|
||||
# OUTPUT OF LAST RESORT
|
||||
def display(self, msg, log_only=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def error(self, msg, wrap_text=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
display = LastResort()
|
||||
|
||||
try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace
|
||||
import ansible.constants as C
|
||||
from ansible.utils.display import Display
|
||||
except AnsibleOptionsError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
sys.exit(5)
|
||||
|
||||
cli = None
|
||||
me = os.path.basename(sys.argv[0])
|
||||
|
||||
try:
|
||||
display = Display()
|
||||
display.debug("starting run")
|
||||
|
||||
sub = None
|
||||
target = me.split('-')
|
||||
if target[-1][0].isdigit():
|
||||
# Remove any version or python version info as downstreams
|
||||
# sometimes add that
|
||||
target = target[:-1]
|
||||
|
||||
if len(target) > 1:
|
||||
sub = target[1]
|
||||
myclass = "%sCLI" % sub.capitalize()
|
||||
elif target[0] == 'ansible':
|
||||
sub = 'adhoc'
|
||||
myclass = 'AdHocCLI'
|
||||
else:
|
||||
raise AnsibleError("Unknown Ansible alias: %s" % me)
|
||||
|
||||
try:
|
||||
mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
|
||||
except ImportError as e:
|
||||
# ImportError members have changed in py3
|
||||
if 'msg' in dir(e):
|
||||
msg = e.msg
|
||||
else:
|
||||
msg = e.message
|
||||
if msg.endswith(' %s' % sub):
|
||||
raise AnsibleError("Ansible sub-program not implemented: %s" % me)
|
||||
else:
|
||||
raise
|
||||
|
||||
try:
|
||||
args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv]
|
||||
except UnicodeError:
|
||||
display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8')
|
||||
display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc()))
|
||||
exit_code = 6
|
||||
else:
|
||||
cli = mycli(args)
|
||||
cli.parse()
|
||||
exit_code = cli.run()
|
||||
|
||||
except AnsibleOptionsError as e:
|
||||
cli.parser.print_help()
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 5
|
||||
except AnsibleParserError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 4
|
||||
# TQM takes care of these, but leaving comment to reserve the exit codes
|
||||
# except AnsibleHostUnreachable as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 3
|
||||
# except AnsibleHostFailed as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 2
|
||||
except AnsibleError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 1
|
||||
except KeyboardInterrupt:
|
||||
display.error("User interrupted execution")
|
||||
exit_code = 99
|
||||
except Exception as e:
|
||||
if C.DEFAULT_DEBUG:
|
||||
# Show raw stacktraces in debug mode, It also allow pdb to
|
||||
# enter post mortem mode.
|
||||
raise
|
||||
have_cli_options = cli is not None and cli.options is not None
|
||||
display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False)
|
||||
if not have_cli_options or have_cli_options and cli.options.verbosity > 2:
|
||||
log_only = False
|
||||
if hasattr(e, 'orig_exc'):
|
||||
display.vvv('\nexception type: %s' % to_text(type(e.orig_exc)))
|
||||
why = to_text(e.orig_exc)
|
||||
if to_text(e) != why:
|
||||
display.vvv('\noriginal msg: %s' % why)
|
||||
else:
|
||||
display.display("to see the full traceback, use -vvv")
|
||||
log_only = True
|
||||
display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only)
|
||||
exit_code = 250
|
||||
finally:
|
||||
# Remove ansible tmpdir
|
||||
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
|
||||
|
||||
sys.exit(exit_code)
|
|
@ -0,0 +1,163 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
########################################################
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
__requires__ = ['ansible']
|
||||
try:
|
||||
import pkg_resources
|
||||
except Exception:
|
||||
# Use pkg_resources to find the correct versions of libraries and set
|
||||
# sys.path appropriately when there are multiversion installs. But we
|
||||
# have code that better expresses the errors in the places where the code
|
||||
# is actually used (the deps are optional for many code paths) so we don't
|
||||
# want to fail here.
|
||||
pass
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
# Used for determining if the system is running a new enough python version
|
||||
# and should only restrict on our documented minimum versions
|
||||
_PY3_MIN = sys.version_info[:2] >= (3, 5)
|
||||
_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
|
||||
_PY_MIN = _PY3_MIN or _PY2_MIN
|
||||
if not _PY_MIN:
|
||||
raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines()))
|
||||
|
||||
|
||||
class LastResort(object):
|
||||
# OUTPUT OF LAST RESORT
|
||||
def display(self, msg, log_only=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def error(self, msg, wrap_text=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
display = LastResort()
|
||||
|
||||
try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace
|
||||
import ansible.constants as C
|
||||
from ansible.utils.display import Display
|
||||
except AnsibleOptionsError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
sys.exit(5)
|
||||
|
||||
cli = None
|
||||
me = os.path.basename(sys.argv[0])
|
||||
|
||||
try:
|
||||
display = Display()
|
||||
display.debug("starting run")
|
||||
|
||||
sub = None
|
||||
target = me.split('-')
|
||||
if target[-1][0].isdigit():
|
||||
# Remove any version or python version info as downstreams
|
||||
# sometimes add that
|
||||
target = target[:-1]
|
||||
|
||||
if len(target) > 1:
|
||||
sub = target[1]
|
||||
myclass = "%sCLI" % sub.capitalize()
|
||||
elif target[0] == 'ansible':
|
||||
sub = 'adhoc'
|
||||
myclass = 'AdHocCLI'
|
||||
else:
|
||||
raise AnsibleError("Unknown Ansible alias: %s" % me)
|
||||
|
||||
try:
|
||||
mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
|
||||
except ImportError as e:
|
||||
# ImportError members have changed in py3
|
||||
if 'msg' in dir(e):
|
||||
msg = e.msg
|
||||
else:
|
||||
msg = e.message
|
||||
if msg.endswith(' %s' % sub):
|
||||
raise AnsibleError("Ansible sub-program not implemented: %s" % me)
|
||||
else:
|
||||
raise
|
||||
|
||||
try:
|
||||
args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv]
|
||||
except UnicodeError:
|
||||
display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8')
|
||||
display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc()))
|
||||
exit_code = 6
|
||||
else:
|
||||
cli = mycli(args)
|
||||
cli.parse()
|
||||
exit_code = cli.run()
|
||||
|
||||
except AnsibleOptionsError as e:
|
||||
cli.parser.print_help()
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 5
|
||||
except AnsibleParserError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 4
|
||||
# TQM takes care of these, but leaving comment to reserve the exit codes
|
||||
# except AnsibleHostUnreachable as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 3
|
||||
# except AnsibleHostFailed as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 2
|
||||
except AnsibleError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 1
|
||||
except KeyboardInterrupt:
|
||||
display.error("User interrupted execution")
|
||||
exit_code = 99
|
||||
except Exception as e:
|
||||
if C.DEFAULT_DEBUG:
|
||||
# Show raw stacktraces in debug mode, It also allow pdb to
|
||||
# enter post mortem mode.
|
||||
raise
|
||||
have_cli_options = cli is not None and cli.options is not None
|
||||
display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False)
|
||||
if not have_cli_options or have_cli_options and cli.options.verbosity > 2:
|
||||
log_only = False
|
||||
if hasattr(e, 'orig_exc'):
|
||||
display.vvv('\nexception type: %s' % to_text(type(e.orig_exc)))
|
||||
why = to_text(e.orig_exc)
|
||||
if to_text(e) != why:
|
||||
display.vvv('\noriginal msg: %s' % why)
|
||||
else:
|
||||
display.display("to see the full traceback, use -vvv")
|
||||
log_only = True
|
||||
display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only)
|
||||
exit_code = 250
|
||||
finally:
|
||||
# Remove ansible tmpdir
|
||||
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
|
||||
|
||||
sys.exit(exit_code)
|
|
@ -0,0 +1,12 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# EASY-INSTALL-ENTRY-SCRIPT: 'ansible-later','console_scripts','ansible-later'
|
||||
__requires__ = 'ansible-later'
|
||||
import re
|
||||
import sys
|
||||
from pkg_resources import load_entry_point
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(
|
||||
load_entry_point('ansible-later', 'console_scripts', 'ansible-later')()
|
||||
)
|
|
@ -0,0 +1,163 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
########################################################
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
__requires__ = ['ansible']
|
||||
try:
|
||||
import pkg_resources
|
||||
except Exception:
|
||||
# Use pkg_resources to find the correct versions of libraries and set
|
||||
# sys.path appropriately when there are multiversion installs. But we
|
||||
# have code that better expresses the errors in the places where the code
|
||||
# is actually used (the deps are optional for many code paths) so we don't
|
||||
# want to fail here.
|
||||
pass
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
# Used for determining if the system is running a new enough python version
|
||||
# and should only restrict on our documented minimum versions
|
||||
_PY3_MIN = sys.version_info[:2] >= (3, 5)
|
||||
_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
|
||||
_PY_MIN = _PY3_MIN or _PY2_MIN
|
||||
if not _PY_MIN:
|
||||
raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines()))
|
||||
|
||||
|
||||
class LastResort(object):
|
||||
# OUTPUT OF LAST RESORT
|
||||
def display(self, msg, log_only=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def error(self, msg, wrap_text=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
display = LastResort()
|
||||
|
||||
try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace
|
||||
import ansible.constants as C
|
||||
from ansible.utils.display import Display
|
||||
except AnsibleOptionsError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
sys.exit(5)
|
||||
|
||||
cli = None
|
||||
me = os.path.basename(sys.argv[0])
|
||||
|
||||
try:
|
||||
display = Display()
|
||||
display.debug("starting run")
|
||||
|
||||
sub = None
|
||||
target = me.split('-')
|
||||
if target[-1][0].isdigit():
|
||||
# Remove any version or python version info as downstreams
|
||||
# sometimes add that
|
||||
target = target[:-1]
|
||||
|
||||
if len(target) > 1:
|
||||
sub = target[1]
|
||||
myclass = "%sCLI" % sub.capitalize()
|
||||
elif target[0] == 'ansible':
|
||||
sub = 'adhoc'
|
||||
myclass = 'AdHocCLI'
|
||||
else:
|
||||
raise AnsibleError("Unknown Ansible alias: %s" % me)
|
||||
|
||||
try:
|
||||
mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
|
||||
except ImportError as e:
|
||||
# ImportError members have changed in py3
|
||||
if 'msg' in dir(e):
|
||||
msg = e.msg
|
||||
else:
|
||||
msg = e.message
|
||||
if msg.endswith(' %s' % sub):
|
||||
raise AnsibleError("Ansible sub-program not implemented: %s" % me)
|
||||
else:
|
||||
raise
|
||||
|
||||
try:
|
||||
args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv]
|
||||
except UnicodeError:
|
||||
display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8')
|
||||
display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc()))
|
||||
exit_code = 6
|
||||
else:
|
||||
cli = mycli(args)
|
||||
cli.parse()
|
||||
exit_code = cli.run()
|
||||
|
||||
except AnsibleOptionsError as e:
|
||||
cli.parser.print_help()
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 5
|
||||
except AnsibleParserError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 4
|
||||
# TQM takes care of these, but leaving comment to reserve the exit codes
|
||||
# except AnsibleHostUnreachable as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 3
|
||||
# except AnsibleHostFailed as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 2
|
||||
except AnsibleError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 1
|
||||
except KeyboardInterrupt:
|
||||
display.error("User interrupted execution")
|
||||
exit_code = 99
|
||||
except Exception as e:
|
||||
if C.DEFAULT_DEBUG:
|
||||
# Show raw stacktraces in debug mode, It also allow pdb to
|
||||
# enter post mortem mode.
|
||||
raise
|
||||
have_cli_options = cli is not None and cli.options is not None
|
||||
display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False)
|
||||
if not have_cli_options or have_cli_options and cli.options.verbosity > 2:
|
||||
log_only = False
|
||||
if hasattr(e, 'orig_exc'):
|
||||
display.vvv('\nexception type: %s' % to_text(type(e.orig_exc)))
|
||||
why = to_text(e.orig_exc)
|
||||
if to_text(e) != why:
|
||||
display.vvv('\noriginal msg: %s' % why)
|
||||
else:
|
||||
display.display("to see the full traceback, use -vvv")
|
||||
log_only = True
|
||||
display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only)
|
||||
exit_code = 250
|
||||
finally:
|
||||
# Remove ansible tmpdir
|
||||
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
|
||||
|
||||
sys.exit(exit_code)
|
|
@ -0,0 +1,163 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
########################################################
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
__requires__ = ['ansible']
|
||||
try:
|
||||
import pkg_resources
|
||||
except Exception:
|
||||
# Use pkg_resources to find the correct versions of libraries and set
|
||||
# sys.path appropriately when there are multiversion installs. But we
|
||||
# have code that better expresses the errors in the places where the code
|
||||
# is actually used (the deps are optional for many code paths) so we don't
|
||||
# want to fail here.
|
||||
pass
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
# Used for determining if the system is running a new enough python version
|
||||
# and should only restrict on our documented minimum versions
|
||||
_PY3_MIN = sys.version_info[:2] >= (3, 5)
|
||||
_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
|
||||
_PY_MIN = _PY3_MIN or _PY2_MIN
|
||||
if not _PY_MIN:
|
||||
raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines()))
|
||||
|
||||
|
||||
class LastResort(object):
|
||||
# OUTPUT OF LAST RESORT
|
||||
def display(self, msg, log_only=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def error(self, msg, wrap_text=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
display = LastResort()
|
||||
|
||||
try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace
|
||||
import ansible.constants as C
|
||||
from ansible.utils.display import Display
|
||||
except AnsibleOptionsError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
sys.exit(5)
|
||||
|
||||
cli = None
|
||||
me = os.path.basename(sys.argv[0])
|
||||
|
||||
try:
|
||||
display = Display()
|
||||
display.debug("starting run")
|
||||
|
||||
sub = None
|
||||
target = me.split('-')
|
||||
if target[-1][0].isdigit():
|
||||
# Remove any version or python version info as downstreams
|
||||
# sometimes add that
|
||||
target = target[:-1]
|
||||
|
||||
if len(target) > 1:
|
||||
sub = target[1]
|
||||
myclass = "%sCLI" % sub.capitalize()
|
||||
elif target[0] == 'ansible':
|
||||
sub = 'adhoc'
|
||||
myclass = 'AdHocCLI'
|
||||
else:
|
||||
raise AnsibleError("Unknown Ansible alias: %s" % me)
|
||||
|
||||
try:
|
||||
mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
|
||||
except ImportError as e:
|
||||
# ImportError members have changed in py3
|
||||
if 'msg' in dir(e):
|
||||
msg = e.msg
|
||||
else:
|
||||
msg = e.message
|
||||
if msg.endswith(' %s' % sub):
|
||||
raise AnsibleError("Ansible sub-program not implemented: %s" % me)
|
||||
else:
|
||||
raise
|
||||
|
||||
try:
|
||||
args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv]
|
||||
except UnicodeError:
|
||||
display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8')
|
||||
display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc()))
|
||||
exit_code = 6
|
||||
else:
|
||||
cli = mycli(args)
|
||||
cli.parse()
|
||||
exit_code = cli.run()
|
||||
|
||||
except AnsibleOptionsError as e:
|
||||
cli.parser.print_help()
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 5
|
||||
except AnsibleParserError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 4
|
||||
# TQM takes care of these, but leaving comment to reserve the exit codes
|
||||
# except AnsibleHostUnreachable as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 3
|
||||
# except AnsibleHostFailed as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 2
|
||||
except AnsibleError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 1
|
||||
except KeyboardInterrupt:
|
||||
display.error("User interrupted execution")
|
||||
exit_code = 99
|
||||
except Exception as e:
|
||||
if C.DEFAULT_DEBUG:
|
||||
# Show raw stacktraces in debug mode, It also allow pdb to
|
||||
# enter post mortem mode.
|
||||
raise
|
||||
have_cli_options = cli is not None and cli.options is not None
|
||||
display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False)
|
||||
if not have_cli_options or have_cli_options and cli.options.verbosity > 2:
|
||||
log_only = False
|
||||
if hasattr(e, 'orig_exc'):
|
||||
display.vvv('\nexception type: %s' % to_text(type(e.orig_exc)))
|
||||
why = to_text(e.orig_exc)
|
||||
if to_text(e) != why:
|
||||
display.vvv('\noriginal msg: %s' % why)
|
||||
else:
|
||||
display.display("to see the full traceback, use -vvv")
|
||||
log_only = True
|
||||
display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only)
|
||||
exit_code = 250
|
||||
finally:
|
||||
# Remove ansible tmpdir
|
||||
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
|
||||
|
||||
sys.exit(exit_code)
|
|
@ -0,0 +1,163 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
########################################################
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
__requires__ = ['ansible']
|
||||
try:
|
||||
import pkg_resources
|
||||
except Exception:
|
||||
# Use pkg_resources to find the correct versions of libraries and set
|
||||
# sys.path appropriately when there are multiversion installs. But we
|
||||
# have code that better expresses the errors in the places where the code
|
||||
# is actually used (the deps are optional for many code paths) so we don't
|
||||
# want to fail here.
|
||||
pass
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
|
||||
# Used for determining if the system is running a new enough python version
|
||||
# and should only restrict on our documented minimum versions
|
||||
_PY3_MIN = sys.version_info[:2] >= (3, 5)
|
||||
_PY2_MIN = (2, 6) <= sys.version_info[:2] < (3,)
|
||||
_PY_MIN = _PY3_MIN or _PY2_MIN
|
||||
if not _PY_MIN:
|
||||
raise SystemExit('ERROR: Ansible requires a minimum of Python2 version 2.6 or Python3 version 3.5. Current version: %s' % ''.join(sys.version.splitlines()))
|
||||
|
||||
|
||||
class LastResort(object):
|
||||
# OUTPUT OF LAST RESORT
|
||||
def display(self, msg, log_only=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
def error(self, msg, wrap_text=None):
|
||||
print(msg, file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
display = LastResort()
|
||||
|
||||
try: # bad ANSIBLE_CONFIG or config options can force ugly stacktrace
|
||||
import ansible.constants as C
|
||||
from ansible.utils.display import Display
|
||||
except AnsibleOptionsError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
sys.exit(5)
|
||||
|
||||
cli = None
|
||||
me = os.path.basename(sys.argv[0])
|
||||
|
||||
try:
|
||||
display = Display()
|
||||
display.debug("starting run")
|
||||
|
||||
sub = None
|
||||
target = me.split('-')
|
||||
if target[-1][0].isdigit():
|
||||
# Remove any version or python version info as downstreams
|
||||
# sometimes add that
|
||||
target = target[:-1]
|
||||
|
||||
if len(target) > 1:
|
||||
sub = target[1]
|
||||
myclass = "%sCLI" % sub.capitalize()
|
||||
elif target[0] == 'ansible':
|
||||
sub = 'adhoc'
|
||||
myclass = 'AdHocCLI'
|
||||
else:
|
||||
raise AnsibleError("Unknown Ansible alias: %s" % me)
|
||||
|
||||
try:
|
||||
mycli = getattr(__import__("ansible.cli.%s" % sub, fromlist=[myclass]), myclass)
|
||||
except ImportError as e:
|
||||
# ImportError members have changed in py3
|
||||
if 'msg' in dir(e):
|
||||
msg = e.msg
|
||||
else:
|
||||
msg = e.message
|
||||
if msg.endswith(' %s' % sub):
|
||||
raise AnsibleError("Ansible sub-program not implemented: %s" % me)
|
||||
else:
|
||||
raise
|
||||
|
||||
try:
|
||||
args = [to_text(a, errors='surrogate_or_strict') for a in sys.argv]
|
||||
except UnicodeError:
|
||||
display.error('Command line args are not in utf-8, unable to continue. Ansible currently only understands utf-8')
|
||||
display.display(u"The full traceback was:\n\n%s" % to_text(traceback.format_exc()))
|
||||
exit_code = 6
|
||||
else:
|
||||
cli = mycli(args)
|
||||
cli.parse()
|
||||
exit_code = cli.run()
|
||||
|
||||
except AnsibleOptionsError as e:
|
||||
cli.parser.print_help()
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 5
|
||||
except AnsibleParserError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 4
|
||||
# TQM takes care of these, but leaving comment to reserve the exit codes
|
||||
# except AnsibleHostUnreachable as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 3
|
||||
# except AnsibleHostFailed as e:
|
||||
# display.error(str(e))
|
||||
# exit_code = 2
|
||||
except AnsibleError as e:
|
||||
display.error(to_text(e), wrap_text=False)
|
||||
exit_code = 1
|
||||
except KeyboardInterrupt:
|
||||
display.error("User interrupted execution")
|
||||
exit_code = 99
|
||||
except Exception as e:
|
||||
if C.DEFAULT_DEBUG:
|
||||
# Show raw stacktraces in debug mode, It also allow pdb to
|
||||
# enter post mortem mode.
|
||||
raise
|
||||
have_cli_options = cli is not None and cli.options is not None
|
||||
display.error("Unexpected Exception, this is probably a bug: %s" % to_text(e), wrap_text=False)
|
||||
if not have_cli_options or have_cli_options and cli.options.verbosity > 2:
|
||||
log_only = False
|
||||
if hasattr(e, 'orig_exc'):
|
||||
display.vvv('\nexception type: %s' % to_text(type(e.orig_exc)))
|
||||
why = to_text(e.orig_exc)
|
||||
if to_text(e) != why:
|
||||
display.vvv('\noriginal msg: %s' % why)
|
||||
else:
|
||||
display.display("to see the full traceback, use -vvv")
|
||||
log_only = True
|
||||
display.display(u"the full traceback was:\n\n%s" % to_text(traceback.format_exc()), log_only=log_only)
|
||||
exit_code = 250
|
||||
finally:
|
||||
# Remove ansible tmpdir
|
||||
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
|
||||
|
||||
sys.exit(exit_code)
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from anyconfig.cli import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from bandit.cli.main import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from bandit.cli.baseline import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from bandit.cli.config_generator import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from coverage.cmdline import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from coverage.cmdline import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from coverage.cmdline import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,11 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from setuptools.command.easy_install import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,11 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from setuptools.command.easy_install import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from flake8.main.cli import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from isort.main import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from jsonschema.cli import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pbr.cmd.main import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,11 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pip._internal import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,11 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pip._internal import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,11 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pip._internal import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pytest import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pycodestyle import _main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(_main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pydocstyle.cli import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pyflakes.api import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from pytest import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
Binary file not shown.
|
@ -0,0 +1,78 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
import sys
|
||||
import getopt
|
||||
import sysconfig
|
||||
|
||||
valid_opts = ['prefix', 'exec-prefix', 'includes', 'libs', 'cflags',
|
||||
'ldflags', 'help']
|
||||
|
||||
if sys.version_info >= (3, 2):
|
||||
valid_opts.insert(-1, 'extension-suffix')
|
||||
valid_opts.append('abiflags')
|
||||
if sys.version_info >= (3, 3):
|
||||
valid_opts.append('configdir')
|
||||
|
||||
|
||||
def exit_with_usage(code=1):
|
||||
sys.stderr.write("Usage: {0} [{1}]\n".format(
|
||||
sys.argv[0], '|'.join('--'+opt for opt in valid_opts)))
|
||||
sys.exit(code)
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], '', valid_opts)
|
||||
except getopt.error:
|
||||
exit_with_usage()
|
||||
|
||||
if not opts:
|
||||
exit_with_usage()
|
||||
|
||||
pyver = sysconfig.get_config_var('VERSION')
|
||||
getvar = sysconfig.get_config_var
|
||||
|
||||
opt_flags = [flag for (flag, val) in opts]
|
||||
|
||||
if '--help' in opt_flags:
|
||||
exit_with_usage(code=0)
|
||||
|
||||
for opt in opt_flags:
|
||||
if opt == '--prefix':
|
||||
print(sysconfig.get_config_var('prefix'))
|
||||
|
||||
elif opt == '--exec-prefix':
|
||||
print(sysconfig.get_config_var('exec_prefix'))
|
||||
|
||||
elif opt in ('--includes', '--cflags'):
|
||||
flags = ['-I' + sysconfig.get_path('include'),
|
||||
'-I' + sysconfig.get_path('platinclude')]
|
||||
if opt == '--cflags':
|
||||
flags.extend(getvar('CFLAGS').split())
|
||||
print(' '.join(flags))
|
||||
|
||||
elif opt in ('--libs', '--ldflags'):
|
||||
abiflags = getattr(sys, 'abiflags', '')
|
||||
libs = ['-lpython' + pyver + abiflags]
|
||||
libs += getvar('LIBS').split()
|
||||
libs += getvar('SYSLIBS').split()
|
||||
# add the prefix/lib/pythonX.Y/config dir, but only if there is no
|
||||
# shared library in prefix/lib/.
|
||||
if opt == '--ldflags':
|
||||
if not getvar('Py_ENABLE_SHARED'):
|
||||
libs.insert(0, '-L' + getvar('LIBPL'))
|
||||
if not getvar('PYTHONFRAMEWORK'):
|
||||
libs.extend(getvar('LINKFORSHARED').split())
|
||||
print(' '.join(libs))
|
||||
|
||||
elif opt == '--extension-suffix':
|
||||
ext_suffix = sysconfig.get_config_var('EXT_SUFFIX')
|
||||
if ext_suffix is None:
|
||||
ext_suffix = sysconfig.get_config_var('SO')
|
||||
print(ext_suffix)
|
||||
|
||||
elif opt == '--abiflags':
|
||||
if not getattr(sys, 'abiflags', None):
|
||||
exit_with_usage()
|
||||
print(sys.abiflags)
|
||||
|
||||
elif opt == '--configdir':
|
||||
print(sysconfig.get_config_var('LIBPL'))
|
|
@ -0,0 +1 @@
|
|||
python
|
|
@ -0,0 +1 @@
|
|||
python
|
|
@ -0,0 +1,68 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import argparse
|
||||
import codecs
|
||||
import sys
|
||||
|
||||
from unidiff import DEFAULT_ENCODING, PatchSet
|
||||
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
DESCRIPTION = """Unified diff metadata.
|
||||
|
||||
Examples:
|
||||
$ git diff | unidiff
|
||||
$ hg diff | unidiff --show-diff
|
||||
$ unidiff -f patch.diff
|
||||
|
||||
"""
|
||||
|
||||
def get_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=DESCRIPTION)
|
||||
parser.add_argument('--show-diff', action="store_true", default=False,
|
||||
dest='show_diff', help='output diff to stdout')
|
||||
parser.add_argument('-f', '--file', dest='diff_file',
|
||||
type=argparse.FileType('r'),
|
||||
help='if not specified, read diff data from stdin')
|
||||
return parser
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = get_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
encoding = DEFAULT_ENCODING
|
||||
if args.diff_file:
|
||||
diff_file = args.diff_file
|
||||
else:
|
||||
encoding = sys.stdin.encoding or encoding
|
||||
diff_file = sys.stdin
|
||||
|
||||
if PY2:
|
||||
diff_file = codecs.getreader(encoding)(diff_file)
|
||||
|
||||
patch = PatchSet(diff_file)
|
||||
|
||||
if args.show_diff:
|
||||
print(patch)
|
||||
print()
|
||||
|
||||
print('Summary')
|
||||
print('-------')
|
||||
additions = 0
|
||||
deletions = 0
|
||||
for f in patch:
|
||||
additions += f.added
|
||||
deletions += f.removed
|
||||
print('%s:' % f.path, '+%d additions,' % f.added,
|
||||
'-%d deletions' % f.removed)
|
||||
|
||||
print()
|
||||
print('%d modified file(s), %d added file(s), %d removed file(s)' % (
|
||||
len(patch.modified_files), len(patch.added_files),
|
||||
len(patch.removed_files)))
|
||||
print('Total: %d addition(s), %d deletion(s)' % (additions, deletions))
|
|
@ -0,0 +1,11 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from wheel.cli import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -0,0 +1,10 @@
|
|||
#!/Users/rkau2905/Devel/python/private/ansible-later/env_27/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
from yamllint.cli import run
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(run())
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/UserDict.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_abcoll.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/_weakrefset.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/abc.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/codecs.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy_reg.py
|
|
@ -0,0 +1,116 @@
|
|||
import imp
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
# opcode is not a virtualenv module, so we can use it to find the stdlib
|
||||
# Important! To work on pypy, this must be a module that resides in the
|
||||
# lib-python/modified-x.y.z directory
|
||||
import opcode
|
||||
|
||||
dirname = os.path.dirname
|
||||
|
||||
distutils_path = os.path.join(os.path.dirname(opcode.__file__), "distutils")
|
||||
if os.path.normpath(distutils_path) == os.path.dirname(os.path.normpath(__file__)):
|
||||
warnings.warn("The virtualenv distutils package at %s appears to be in the same location as the system distutils?")
|
||||
else:
|
||||
__path__.insert(0, distutils_path) # noqa: F821
|
||||
real_distutils = imp.load_module("_virtualenv_distutils", None, distutils_path, ("", "", imp.PKG_DIRECTORY))
|
||||
# Copy the relevant attributes
|
||||
try:
|
||||
__revision__ = real_distutils.__revision__
|
||||
except AttributeError:
|
||||
pass
|
||||
__version__ = real_distutils.__version__
|
||||
|
||||
from distutils import dist, sysconfig # isort:skip
|
||||
|
||||
try:
|
||||
basestring
|
||||
except NameError:
|
||||
basestring = str
|
||||
|
||||
# patch build_ext (distutils doesn't know how to get the libs directory
|
||||
# path on windows - it hardcodes the paths around the patched sys.prefix)
|
||||
|
||||
if sys.platform == "win32":
|
||||
from distutils.command.build_ext import build_ext as old_build_ext
|
||||
|
||||
class build_ext(old_build_ext):
|
||||
def finalize_options(self):
|
||||
if self.library_dirs is None:
|
||||
self.library_dirs = []
|
||||
elif isinstance(self.library_dirs, basestring):
|
||||
self.library_dirs = self.library_dirs.split(os.pathsep)
|
||||
|
||||
self.library_dirs.insert(0, os.path.join(sys.real_prefix, "Libs"))
|
||||
old_build_ext.finalize_options(self)
|
||||
|
||||
from distutils.command import build_ext as build_ext_module
|
||||
|
||||
build_ext_module.build_ext = build_ext
|
||||
|
||||
# distutils.dist patches:
|
||||
|
||||
old_find_config_files = dist.Distribution.find_config_files
|
||||
|
||||
|
||||
def find_config_files(self):
|
||||
found = old_find_config_files(self)
|
||||
if os.name == "posix":
|
||||
user_filename = ".pydistutils.cfg"
|
||||
else:
|
||||
user_filename = "pydistutils.cfg"
|
||||
user_filename = os.path.join(sys.prefix, user_filename)
|
||||
if os.path.isfile(user_filename):
|
||||
for item in list(found):
|
||||
if item.endswith("pydistutils.cfg"):
|
||||
found.remove(item)
|
||||
found.append(user_filename)
|
||||
return found
|
||||
|
||||
|
||||
dist.Distribution.find_config_files = find_config_files
|
||||
|
||||
# distutils.sysconfig patches:
|
||||
|
||||
old_get_python_inc = sysconfig.get_python_inc
|
||||
|
||||
|
||||
def sysconfig_get_python_inc(plat_specific=0, prefix=None):
|
||||
if prefix is None:
|
||||
prefix = sys.real_prefix
|
||||
return old_get_python_inc(plat_specific, prefix)
|
||||
|
||||
|
||||
sysconfig_get_python_inc.__doc__ = old_get_python_inc.__doc__
|
||||
sysconfig.get_python_inc = sysconfig_get_python_inc
|
||||
|
||||
old_get_python_lib = sysconfig.get_python_lib
|
||||
|
||||
|
||||
def sysconfig_get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
|
||||
if standard_lib and prefix is None:
|
||||
prefix = sys.real_prefix
|
||||
return old_get_python_lib(plat_specific, standard_lib, prefix)
|
||||
|
||||
|
||||
sysconfig_get_python_lib.__doc__ = old_get_python_lib.__doc__
|
||||
sysconfig.get_python_lib = sysconfig_get_python_lib
|
||||
|
||||
old_get_config_vars = sysconfig.get_config_vars
|
||||
|
||||
|
||||
def sysconfig_get_config_vars(*args):
|
||||
real_vars = old_get_config_vars(*args)
|
||||
if sys.platform == "win32":
|
||||
lib_dir = os.path.join(sys.real_prefix, "libs")
|
||||
if isinstance(real_vars, dict) and "LIBDIR" not in real_vars:
|
||||
real_vars["LIBDIR"] = lib_dir # asked for all
|
||||
elif isinstance(real_vars, list) and "LIBDIR" in args:
|
||||
real_vars = real_vars + [lib_dir] # asked for list
|
||||
return real_vars
|
||||
|
||||
|
||||
sysconfig_get_config_vars.__doc__ = old_get_config_vars.__doc__
|
||||
sysconfig.get_config_vars = sysconfig_get_config_vars
|
|
@ -0,0 +1,6 @@
|
|||
# This is a config file local to this virtualenv installation
|
||||
# You may include options that will be used by all distutils commands,
|
||||
# and by easy_install. For instance:
|
||||
#
|
||||
# [easy_install]
|
||||
# find_links = http://mylocalsite
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/fnmatch.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/genericpath.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/linecache.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/locale.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/ntpath.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/posixpath.py
|
|
@ -0,0 +1 @@
|
|||
/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py
|
|
@ -0,0 +1 @@
|
|||
pip
|
|
@ -0,0 +1,31 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: GitPython
|
||||
Version: 2.1.11
|
||||
Summary: Python Git Library
|
||||
Home-page: https://github.com/gitpython-developers/GitPython
|
||||
Author: Sebastian Thiel, Michael Trier
|
||||
Author-email: byronimo@gmail.com, mtrier@gmail.com
|
||||
License: BSD License
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Console
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Operating System :: POSIX
|
||||
Classifier: Operating System :: Microsoft :: Windows
|
||||
Classifier: Operating System :: MacOS :: MacOS X
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Requires: gitdb2 (>=2.0.0)
|
||||
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
|
||||
Requires-Dist: gitdb2 (>=2.0.0)
|
||||
|
||||
GitPython is a python library used to interact with Git repositories
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue