|
1 | | -#!/bin/bash |
| 1 | +#!/usr/bin/env python |
| 2 | +# -*- coding: utf-8 -*- |
2 | 3 |
|
3 | 4 | # debops-defaults: aggregate all defaults from Ansible roles into one stream |
4 | | -# Copyright (C) 2014 Maciej Delmanowski <drybjed@gmail.com> |
| 5 | +# Copyright (C) 2014 Hartmut Goebel <h.goebel@crazy-compilers.com> |
5 | 6 | # Part of the DebOps project - http://debops.org/ |
6 | 7 |
|
7 | 8 |
|
|
26 | 27 | # be downloaded from the FSF web page at: |
27 | 28 | # http://www.gnu.org/copyleft/gpl.html |
28 | 29 |
|
29 | | - |
30 | | -set -e |
31 | | - |
32 | | -# ---- Global constants ---- |
33 | | - |
34 | | -declare -r DEBOPS_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/debops" |
35 | | -declare -r DEBOPS_CONFIG=".debops.cfg" |
36 | | -declare -r DEBOPS_INVENTORY="inventory" |
37 | | -declare -r SCRIPT_NAME="$(basename ${0})" |
38 | | - |
39 | | - |
40 | | -# ---- Configuration variables ---- |
41 | | - |
42 | | -# Locations where DebOps playbooks might be found |
43 | | -DEBOPS_PLAYBOOKS_PATHS=( |
44 | | - "${DEBOPS_DATA_HOME}/debops-playbooks/playbooks" |
45 | | - "/usr/local/share/debops/debops-playbooks/playbooks" |
46 | | - "/usr/share/debops/debops-playbooks/playbooks" |
47 | | -) |
48 | | - |
49 | | -# List of possible inventory directories, relative to DebOps root project directory |
50 | | -ANSIBLE_INVENTORY_PATHS=( "ansible/${DEBOPS_INVENTORY}" "${DEBOPS_INVENTORY}" ) |
51 | | - |
52 | | -# Default role prefix if no roles with prefixes are specified |
53 | | -ROLE_PREFIX="debops" |
54 | | - |
55 | | - |
56 | | -# ---- Functions ---- |
57 | | - |
58 | | -# Find specified file or directory in parent dir (if not specified, finds $DEBOPS_CONFIG) |
59 | | -# Source: https://unix.stackexchange.com/questions/13464 |
60 | | -find_up () { |
61 | | - local name=${1:-$DEBOPS_CONFIG} |
62 | | - local slashes="${PWD//[^\/]/}" |
63 | | - local directory="${PWD}" |
64 | | - |
65 | | - for (( n=${#slashes}; n>0; --n )) ; do |
66 | | - test -e "${directory}/${name}" && echo "$(readlink -f ${directory}/${name})" && return |
67 | | - directory="$directory/.." |
68 | | - done |
69 | | -} |
70 | | - |
71 | | -# Display error message and exit |
72 | | -error_msg () { |
73 | | - local severity="${2:-Error}" |
74 | | - local message="${1}" |
75 | | - |
76 | | - echo >&2 "${SCRIPT_NAME}: ${severity}: ${message}" |
77 | | - set +e |
78 | | - [[ "${severity}" == "Error" ]] && exit 1 |
79 | | - set -e |
80 | | -} |
81 | | - |
82 | | -# Check if required commands exist |
83 | | -require_commands () { |
84 | | - for name in ${@} ; do |
85 | | - if ! type ${name} > /dev/null 2>&1 ; then |
86 | | - error_msg "${name}: command not found" |
87 | | - fi |
88 | | - done |
89 | | -} |
90 | | - |
91 | | -# Aggregate role/defaults/main.yml files from all roles into one stream |
92 | | -function aggregate_defaults() { |
93 | | - if [ -n "${role_list}" ]; then |
94 | | - for role in ${role_list}; do |
95 | | - if [[ $role != *.* ]] ; then |
96 | | - cat ${debops_playbooks}/../roles/${ROLE_PREFIX}.${role}/defaults/main.yml |
97 | | - else |
98 | | - cat ${debops_playbooks}/../roles/${role}/defaults/main.yml |
99 | | - fi |
100 | | - done |
101 | | - else |
102 | | - cat ${debops_playbooks}/../roles/*/defaults/main.yml |
103 | | - fi |
104 | | -} |
105 | | - |
| 30 | +from __future__ import print_function |
| 31 | + |
| 32 | +import os |
| 33 | +import sys |
| 34 | +import codecs |
| 35 | +import subprocess |
| 36 | +import glob |
| 37 | +import argparse |
| 38 | +import errno |
| 39 | + |
| 40 | +from debops import * |
| 41 | +from debops.cmds import * |
| 42 | + |
| 43 | +def cat(filename, outstream): |
| 44 | + try: |
| 45 | + fh = codecs.open(filename, encoding=sys.getdefaultencoding()) |
| 46 | + except IOError, e: |
| 47 | + # This should only happen if the user listed a unknown role. |
| 48 | + outstream.write('%s: %s\n' % (e.strerror, e.filename)) |
| 49 | + return |
| 50 | + try: |
| 51 | + outstream.writelines(fh) |
| 52 | + finally: |
| 53 | + fh.close() |
| 54 | + |
| 55 | +def aggregate_defaults(playbooks_path, role_list, outstream): |
| 56 | + # Aggregate role/defaults/main.yml files from all roles into one stream |
| 57 | + roles_path = os.path.normpath(os.path.join(playbooks_path, '..', 'roles')) |
| 58 | + if role_list: |
| 59 | + for role in role_list: |
| 60 | + if not '.' in role: |
| 61 | + role = ROLE_PREFIX + '.' + role |
| 62 | + fn = os.path.join(roles_path, role, 'defaults', 'main.yml') |
| 63 | + cat(fn, outstream=outstream) |
| 64 | + else: |
| 65 | + for fn in glob.glob(os.path.join(roles_path, |
| 66 | + '*', 'defaults', 'main.yml')): |
| 67 | + cat(fn, outstream=outstream) |
106 | 68 |
|
107 | 69 | # ---- DebOps environment setup ---- |
108 | 70 |
|
109 | | -# Find DebOps configuration file |
110 | | -debops_config="$(find_up)" |
111 | | - |
112 | | -# Find root of the DebOps project dir |
113 | | -[ -n "${debops_config}" ] && debops_root="$(dirname ${debops_config})" |
114 | | - |
115 | | -# Source DebOps configuration file |
116 | | -[ -n "${debops_config}" ] && [ -r ${debops_config} ] && source ${debops_config} |
117 | | - |
118 | | - |
119 | | -# ---- Main script ---- |
120 | | - |
121 | | -# Make sure required commands are present |
122 | | -require_commands view |
123 | | - |
124 | | -# Check if playbooks are installed in various locations |
125 | | -for playbook_path in ${debops_root}/debops-playbooks/playbooks ${DEBOPS_PLAYBOOKS_PATHS[@]} ; do |
126 | | - if [ -f ${playbook_path}/site.yml ] ; then |
127 | | - debops_playbooks="${playbook_path}" |
128 | | - break |
129 | | - fi |
130 | | -done |
131 | | - |
132 | | -[ -z "${debops_playbooks}" ] && error_msg "DebOps playbooks not installed" |
133 | | - |
134 | | -# Get list of roles from the script arguments |
135 | | -if [ $# -gt 0 ]; then |
136 | | - role_list=${@} |
137 | | -fi |
138 | | - |
139 | | -if [ -t 1 ]; then |
140 | | - # if script is run as standalone, redirect to view |
141 | | - ( aggregate_defaults ) | view '+set ft=yaml' - |
142 | | -else |
143 | | - # else, send everything to stdout |
144 | | - aggregate_defaults |
145 | | -fi |
146 | | - |
| 71 | +def main(role_list): |
| 72 | + debops_root = find_debops_project(required=False) |
| 73 | + # :todo: Source DebOps configuration file |
| 74 | + #[ -r ${debops_config} ] && source ${debops_config} |
| 75 | + |
| 76 | + # Make sure required commands are present |
| 77 | + require_commands('view') |
| 78 | + |
| 79 | + playbooks_path = find_playbookpath(debops_root, required=True) |
| 80 | + |
| 81 | + if sys.stdout.isatty(): |
| 82 | + # if script is run as standalone, redirect to view |
| 83 | + #out = subprocess.Popen(['view', '+set ft=yaml', '-'], |
| 84 | + view = subprocess.Popen(['less', '-'], |
| 85 | + stdin=subprocess.PIPE) |
| 86 | + try: |
| 87 | + aggregate_defaults(playbooks_path, role_list, view.stdin) |
| 88 | + except IOError, e: |
| 89 | + if e.errno not in (errno.EPIPE, errno.EINVAL): |
| 90 | + # "Invalid pipe" or "Invalid argument" |
| 91 | + raise |
| 92 | + finally: |
| 93 | + view.communicate() |
| 94 | + else: |
| 95 | + # else, send everything to stdout |
| 96 | + aggregate_defaults(playbooks_path, role_list, sys.stdout) |
| 97 | + |
| 98 | + |
| 99 | +parser = argparse.ArgumentParser() |
| 100 | +parser.add_argument('role', nargs='*') |
| 101 | +args = parser.parse_args() |
| 102 | + |
| 103 | +try: |
| 104 | + main(args.role) |
| 105 | +except KeyboardInterrupt: |
| 106 | + raise SystemExit('... aborted') |
0 commit comments