diff --git a/debops b/debops new file mode 100755 index 0000000..9e41312 --- /dev/null +++ b/debops @@ -0,0 +1,180 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +debops: run ansible-playbook with some customization +""" +# Copyright (C) 2014-2015 Hartmut Goebel +# Part of the DebOps - https://debops.org/ + +# This program is free software; you can redistribute +# it and/or modify it under the terms of the +# GNU General Public License as published by the Free +# Software Foundation; either version 3 of the License, +# or (at your option) any later version. +# +# This program is distributed in the hope that it will +# be useful, but WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General +# Public License along with this program; if not, +# write to the Free Software Foundation, Inc., 59 +# Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# An on-line copy of the GNU General Public License can +# be downloaded from the FSF web page at: +# https://www.gnu.org/copyleft/gpl.html + +from __future__ import print_function + +import sys +import os +import ConfigParser + +import ansible + +from debops import * +from debops.cmds import * + +__author__ = "Hartmut Goebel " +__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " +__licence__ = "GNU General Public License version 3 (GPL v3) or later" + + +PATHSEP = ':' + +ConfigFileHeader = """\ +# Ansible configuration file generated by DebOps, all changes will be lost. +# You can manipulate the contents of this file via `.debops.cfg`. +""" + +def write_config(filename, config): + cfgparser = ConfigParser.ConfigParser() + for section, pairs in config.items(): + cfgparser.add_section(section) + for option, value in pairs.items(): + cfgparser.set(section, option, value) + + with open(filename, "w") as fh: + print(ConfigFileHeader, file=fh) + cfgparser.write(fh) + + +def gen_ansible_cfg(filename, config, project_root, playbooks_path, + inventory_path): + # Generate Ansible configuration file + + def custom_paths(type): + if type in defaults: + # prepend value from .debops.cfg + yield defaults[type] + yield os.path.join(project_root, "ansible", type) + yield os.path.join(playbooks_path, type) + yield os.path.join("/usr/share/ansible/", type) + + # Add custom configuration options to ansible.cfg: Take values + # from [ansible ...] sections of the .debops.cfg file + # Note: To set debops default values, use debops.config.DEFAULTS + cfg = dict((sect.split(None, 1)[1], pairs) + for sect, pairs in config.items() if sect.startswith('ansible ')) + + defaults = cfg.setdefault('defaults', {}) + defaults['inventory'] = inventory_path + defaults['roles_path'] = PATHSEP.join(filter(None, ( + defaults.get('roles_path'), # value from .debops.cfg or None + os.path.join(project_root, "roles"), + os.path.join(project_root, "ansible", "roles"), + os.path.join(playbooks_path, "..", "roles"), + os.path.join(playbooks_path, "roles"), + "/etc/ansible/roles"))) + for plugin_type in ('action', 'callback', 'connection', + 'filter', 'lookup', 'vars'): + plugin_type = plugin_type+"_plugins" + defaults[plugin_type] = PATHSEP.join(custom_paths(plugin_type)) + + if ansible.__version__ >= "1.7": + # work around a bug obviously introduced in 1.7, see + # https://github.com/ansible/ansible/issues/8555 + if ' ' in defaults[plugin_type]: + defaults[plugin_type] = PATHSEP.join( + '"%s"' % p for p in defaults[plugin_type].split(PATHSEP)) + + defaults['library'] = PATHSEP.join(custom_paths('library')) + + write_config(filename, cfg) + + +def main(cmd_args): + project_root = find_debops_project(required=True) + config = read_config(project_root) + playbooks_path = find_playbookpath(config, project_root, required=True) + + # Make sure required commands are present + require_commands('ansible-playbook') + + def find_playbook(playbook): + tries = [ + (project_root, "playbooks", playbook), + (project_root, "ansible", "playbooks", playbook), + (playbooks_path, playbook), + ] + + if 'playbooks_path' in config['paths']: + tries += [(custom_path, playbook) for custom_path in + config['paths']['playbooks_path'].split(PATHSEP)] + + for parts in tries: + play = os.path.join(*parts) + if os.path.isfile(play): + return play + + # Check if user specified a potential playbook name as the first + # argument. If yes, use it as the playbook name and remove it from + # the argument list + play = None + if len(cmd_args) > 0: + maybe_play = cmd_args[0] + if os.path.isfile(maybe_play): + play = maybe_play + else: + play = find_playbook(maybe_play + ".yml") + if play: + cmd_args.pop(0) + del maybe_play + if not play: + play = find_playbook("site.yml") + + inventory_path = find_inventorypath(project_root) + os.environ['ANSIBLE_HOSTS'] = inventory_path + + ansible_config_file = os.path.join(project_root, ANSIBLE_CONFIG_FILE) + os.environ['ANSIBLE_CONFIG'] = os.path.abspath(ansible_config_file) + gen_ansible_cfg(ansible_config_file, config, project_root, playbooks_path, + inventory_path) + + # Allow insecure SSH connections if requested + if INSECURE: + os.environ['ANSIBLE_HOST_KEY_CHECKING'] = 'False' + + # Create path to EncFS encrypted directory, based on inventory name + encfs_encrypted = os.path.join(os.path.dirname(inventory_path), + ENCFS_PREFIX + SECRET_NAME) + + # Check if encrypted secret directory exists and use it + revert_unlock = padlock_unlock(encfs_encrypted) + try: + # Run ansible-playbook with custom environment + print("Running Ansible playbook from:") + print(play, "...") + return subprocess.call(['ansible-playbook', play] + cmd_args) + finally: + if revert_unlock: + padlock_lock(encfs_encrypted) + + +try: + sys.exit(main(sys.argv[1:])) +except KeyboardInterrupt: + raise SystemExit('... aborted') diff --git a/debops-defaults b/debops-defaults new file mode 100755 index 0000000..3e543ac --- /dev/null +++ b/debops-defaults @@ -0,0 +1,111 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +debops-defaults: aggregate all defaults from Ansible roles into one stream +""" +# Copyright (C) 2014-2015 Hartmut Goebel +# Part of the DebOps - https://debops.org/ + +# This program is free software; you can redistribute +# it and/or modify it under the terms of the +# GNU General Public License as published by the Free +# Software Foundation; either version 3 of the License, +# or (at your option) any later version. +# +# This program is distributed in the hope that it will +# be useful, but WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General +# Public License along with this program; if not, +# write to the Free Software Foundation, Inc., 59 +# Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# An on-line copy of the GNU General Public License can +# be downloaded from the FSF web page at: +# https://www.gnu.org/copyleft/gpl.html + +from __future__ import print_function + +import os +import sys +import codecs +import subprocess +import glob +import argparse +import errno + +reload(sys) +sys.setdefaultencoding('utf-8') + +from debops import * +from debops.cmds import * + +__author__ = "Hartmut Goebel " +__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " +__licence__ = "GNU General Public License version 3 (GPL v3) or later" + +def cat(filename, outstream): + try: + fh = codecs.open(filename, encoding=sys.getdefaultencoding()) + except IOError, e: + # This should only happen if the user listed a unknown role. + outstream.write('%s: %s\n' % (e.strerror, e.filename)) + return + try: + # Read input file as Unicode object and pass it to outstream. + outstream.write(fh.read()) + finally: + fh.close() + +def aggregate_defaults(playbooks_path, role_list, outstream): + # Aggregate role/defaults/main.yml files from all roles into one stream + roles_path = os.path.normpath(os.path.join(playbooks_path, '..', 'roles')) + if role_list: + for role in role_list: + if not '.' in role: + role = ROLE_PREFIX + '.' + role + fn = os.path.join(roles_path, role, 'defaults', 'main.yml') + cat(fn, outstream=outstream) + else: + for fn in glob.glob(os.path.join(roles_path, + '*', 'defaults', 'main.yml')): + cat(fn, outstream=outstream) + +# ---- DebOps environment setup ---- + +def main(role_list): + project_root = find_debops_project(required=False) + config = read_config(project_root) + playbooks_path = find_playbookpath(config, project_root, required=True) + + # Make sure required commands are present + require_commands('view') + + if sys.stdout.isatty(): + # if script is run as standalone, redirect to view + view = subprocess.Popen(['view', '+set ft=yaml', '-'], + stdin=subprocess.PIPE) + try: + aggregate_defaults(playbooks_path, role_list, view.stdin) + except IOError, e: + if e.errno not in (errno.EPIPE, errno.EINVAL): + # "Invalid pipe" or "Invalid argument" + raise + finally: + view.communicate() + else: + # else, send everything to stdout + aggregate_defaults(playbooks_path, role_list, sys.stdout) + + +parser = argparse.ArgumentParser() +parser.add_argument('role', nargs='*') +args = parser.parse_args() + +try: + main(args.role) +except KeyboardInterrupt: + raise SystemExit('... aborted') diff --git a/debops-init b/debops-init new file mode 100755 index 0000000..46cc158 --- /dev/null +++ b/debops-init @@ -0,0 +1,205 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +debops-init: create a new DebOps project directory +""" +# Copyright (C) 2014-2015 Hartmut Goebel +# Part of the DebOps - https://debops.org/ + +# This program is free software; you can redistribute +# it and/or modify it under the terms of the +# GNU General Public License as published by the Free +# Software Foundation; either version 3 of the License, +# or (at your option) any later version. +# +# This program is distributed in the hope that it will +# be useful, but WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General +# Public License along with this program; if not, +# write to the Free Software Foundation, Inc., 59 +# Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# An on-line copy of the GNU General Public License can +# be downloaded from the FSF web page at: +# https://www.gnu.org/copyleft/gpl.html + +from __future__ import print_function + +import os +import codecs +import subprocess +import glob +import argparse + +from debops import * +from debops.cmds import * + +__author__ = "Hartmut Goebel " +__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " +__licence__ = "GNU General Public License version 3 (GPL v3) or later" + +SKEL_DIRS = ( + os.path.join("ansible", INVENTORY, "group_vars", "all"), + os.path.join("ansible", INVENTORY, "host_vars"), + os.path.join("ansible", "playbooks"), + os.path.join("ansible", "roles"), +) + +DEFAULT_DEBOPS_CONFIG = """ +# -*- conf -*- + +[paths] +;data-home: /opt/debops + +[ansible defaults] +display_skipped_hosts = False +retry_files_enabled = False +;callback_plugins = /my/plugins/callback +;roles_path = /my/roles + +[ansible paramiko] +;record_host_keys=True + +[ansible ssh_connection] +;ssh_args = -o ControlMaster=auto -o ControlPersist=60s +""" + +DEFAULT_GITIGNORE = """\ +ansible/{SECRET_NAME} +{SECRET_NAME} +{ENCFS_PREFIX}{SECRET_NAME} +ansible.cfg + +#-- python +*.py[co] + +#-- vim +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ + +#-- Emacs +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +#-- SublimeText +*.sublime-workspace +#*.sublime-project + +#-- sftp configuration file +sftp-config.json +""" + +HOSTS_FILE_HEADER = """\ +# This is an Ansible inventory file in INI format. You can define a list of +# hosts and groups to be managed by this particular inventory. + +# Hosts listed under [debops_all_hosts] will have common DebOps plays +# ran against them. It will include services such as iptables, DNS, Postfix, +# sshd configuration and more. +# +# View the list here: +# https://github.com/debops/debops-playbooks/blob/master/playbooks/common.yml +# +# You should check Getting Started guide for useful suggestions: +# https://docs.debops.org/en/latest/debops-playbooks/docs/guides/getting-started.html +""" + +HOSTS_FILE_CONTENT_CONTROLER = """ +# Your host is eligible to be managed by DebOps' common playbook. If you want +# that functionality and more, then uncomment your hostname below. + +[debops_all_hosts] +#%s ansible_connection=local +""" % platform.node() + +HOSTS_FILE_CONTENT_NO_CONTROLER = """ +# Your host was not detected as compatible with DebOps playbooks, so you will +# not be able to leverage the above features on your current operating system. +# You can however use a virtual machine as the Ansible Controller. + +[debops_all_hosts] + +""" + + +def write_file(filename, *content): + """ + If file:`filename` does not exist, create it and write + var:`content` into it. + """ + if not os.path.exists(filename): + with open(filename, "w") as fh: + fh.writelines(content) + + +def write_config_files(project_root): + """ + Create the default debops-config files in the dir:`project_root` + directory. + """ + # Create .debops.cfg + write_file(os.path.join(project_root, DEBOPS_CONFIG), DEFAULT_DEBOPS_CONFIG) + # Create .gitignore + write_file(os.path.join(project_root, '.gitignore'), + DEFAULT_GITIGNORE.format(SECRET_NAME=SECRET_NAME, ENCFS_PREFIX=ENCFS_PREFIX)) + + hosts_filename = os.path.join(project_root, "ansible", INVENTORY, "hosts") + # Swap in different hosts file content depending on the host's OS/distro + if (platform.system() == "Linux" and + platform.linux_distribution()[0].lower() in ("debian", "ubuntu")): + write_file(hosts_filename, + HOSTS_FILE_HEADER, HOSTS_FILE_CONTENT_CONTROLER) + else: + write_file(hosts_filename, + HOSTS_FILE_HEADER, HOSTS_FILE_CONTENT_NO_CONTROLER) + + +def main(project_root): + orig_project_root = project_root + project_root = os.path.abspath(project_root) + + #-- Check for existing debops project directory + debops_project_root = find_debops_project(project_root, required=False) + + # Exit if DebOps configuration file has been found in project_dir + if os.path.exists(os.path.join(project_root, DEBOPS_CONFIG)): + error_msg("%s is already a DebOps project directory" % project_root) + + # Exit if we are in a DebOps project dir and nested project would be created + if debops_project_root: + error_msg("You are inside %s project already" % debops_project_root) + + #-- Main script + + print("Creating new DebOps project directory in", orig_project_root, "...") + + # Create base project directories + for skel_dir in SKEL_DIRS: + skel_dir = os.path.join(project_root, skel_dir) + if not os.path.isdir(skel_dir): + os.makedirs(skel_dir) + # Write default config files + write_config_files(project_root) + + +parser = argparse.ArgumentParser() +parser.add_argument('project_dir', default=os.curdir) +args = parser.parse_args() + +try: + main(args.project_dir) +except KeyboardInterrupt: + raise SystemExit('... aborted') diff --git a/debops-padlock b/debops-padlock new file mode 100755 index 0000000..7b7ceb6 --- /dev/null +++ b/debops-padlock @@ -0,0 +1,187 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +debops-padlock: encrypt secret directory with EncFS and GPG +""" +# Copyright (C) 2014-2015 Hartmut Goebel +# Part of the DebOps - https://debops.org/ + +# This program is free software; you can redistribute +# it and/or modify it under the terms of the +# GNU General Public License as published by the Free +# Software Foundation; either version 3 of the License, +# or (at your option) any later version. +# +# This program is distributed in the hope that it will +# be useful, but WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General +# Public License along with this program; if not, +# write to the Free Software Foundation, Inc., 59 +# Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# An on-line copy of the GNU General Public License can +# be downloaded from the FSF web page at: +# https://www.gnu.org/copyleft/gpl.html + +from __future__ import print_function + +import os +import shutil +import argparse +import itertools +import stat +import sys +import time +from pkg_resources import resource_filename + +from debops import * +from debops.cmds import * + +__author__ = "Hartmut Goebel " +__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " +__licence__ = "GNU General Public License version 3 (GPL v3) or later" + +def gen_pwd(): + from string import ascii_letters, digits, punctuation + import random + ALLCHARS = digits + ascii_letters + punctuation + ALLCHARS = digits + ascii_letters + '-_!@#$%^&*()_+{}|:<>?=' + pwd = ''.join(random.choice(ALLCHARS) for i in range(ENCFS_KEYFILE_LENGTH)) + return pwd + + +# Randomness source for EncFS keyfile generation +devrandom = os.environ.get('DEVRANDOM', "/dev/urandom") + +SCRIPT_FILENAME = 'padlock-script' + +# ---- DebOps environment setup ---- + +def main(subcommand_func, **kwargs): + project_root = find_debops_project(required=True) + # :todo: Source DebOps configuration file + #[ -r ${debops_config} ] && source ${debops_config} + + # ---- Main script ---- + + # Make sure required commands are present + require_commands('encfs', 'find', 'fusermount', 'gpg') + + inventory_path = find_inventorypath(project_root, required=False) + # If inventory hasn't been found automatically, assume it's the default + if not inventory_path: + inventory_path = os.path.join(project_root, 'ansible', INVENTORY) + + # Create names of EncFS encrypted and decrypted directories, based on + # inventory name (absolute paths are specified) + encfs_encrypted = os.path.join(os.path.dirname(inventory_path), + ENCFS_PREFIX + SECRET_NAME) + encfs_decrypted = os.path.join(os.path.dirname(inventory_path), + SECRET_NAME) + subcommand_func(encfs_decrypted, encfs_encrypted, **kwargs) + + +def init(encfs_decrypted, encfs_encrypted, recipients): + # EncFS cannot create encrypted directory if directory with + # decrypted data is not empty + if not os.path.exists(encfs_decrypted): + os.makedirs(encfs_decrypted) + elif os.listdir(encfs_decrypted): + error_msg("secret directory not empty") + + # Quit if encrypted directory already exists. + if os.path.isdir(encfs_encrypted): + error_msg("EncFS directory already exists") + os.makedirs(encfs_encrypted) + + encfs_keyfile = os.path.join(encfs_encrypted, ENCFS_KEYFILE) + encfs_configfile = os.path.join(encfs_encrypted, ENCFS_CONFIGFILE) + + # put a `-r` in front of each recipient for passing as args to gpg + recipients = list(itertools.chain.from_iterable(['-r', r] + for r in recipients)) + + # Generate a random password and encrypt it with GPG keys of recipients. + print("Generating a random", ENCFS_KEYFILE_LENGTH, "char password") + pwd = gen_pwd() + gpg = subprocess.Popen(['gpg', '--encrypt', '--armor', + '--output', encfs_keyfile] + recipients, + stdin=subprocess.PIPE) + gpg.communicate(pwd) + + # Mount the encfs to the config file will be written. Tell encfs + # it to ask gpg for the password. + # NB1: Alternativly we could use --stdinpass, but using --extpass makes + # the user check if she has the correct passphrase early. + # NB2: We can not use padlock_unlock here, because the config file + # does not yet exist. + encfs = subprocess.Popen([ + 'encfs', encfs_encrypted, encfs_decrypted, + '--extpass', 'gpg --no-mdc-warning --output - '+shquote(encfs_keyfile)], + stdin=subprocess.PIPE) + encfs.communicate('p\n'+pwd) + + # Create padlock-script + padlock_script = os.path.join(encfs_encrypted, PADLOCK_CMD) + + # :todo: use resource_stream + shutil.copy(resource_filename('debops', SCRIPT_FILENAME), padlock_script) + os.chmod(padlock_script, + os.stat(padlock_script).st_mode|stat.S_IXUSR|stat.S_IXGRP|stat.S_IXOTH) + + # Lock the EncFS directory after creation + time.sleep(0.5) # :fixme: why sleeping here? + padlock_lock(encfs_encrypted) + + # Protect the EncFS configuration file by also encrypting it with + # the GPG keys of recipients. + subprocess.call(['gpg', '--encrypt', '--armor', + '--output', encfs_configfile+'.asc'] + + recipients + [encfs_configfile]) + os.remove(encfs_configfile) + + +def lock(encfs_decrypted, encfs_encrypted, verbose): + # Unmount the directory if it is mounted + if padlock_lock(encfs_encrypted): + if verbose: print("Locked!") + else: + if verbose: print("Is already locked.") + + +def unlock(encfs_decrypted, encfs_encrypted, verbose): + # Mount the directory it if it is unmounted + if padlock_unlock(encfs_encrypted): + if verbose: print("Unlocked!") + else: + if verbose: print("Is already unlocked.") + + +parser = argparse.ArgumentParser() +subparsers = parser.add_subparsers( + help='action to perform. Use `%(prog)s --help ` for further help.') + +p = subparsers.add_parser('init') +p.add_argument('recipients', nargs='*', + help=("GPG recipients for which the secret key should be " + "encrypted for (name, e-mail or key-id)")) +p.set_defaults(subcommand_func=init) + +p = subparsers.add_parser('unlock') +p.add_argument('-v', '--verbose', action='store_true', help="be verbose") +p.set_defaults(subcommand_func=unlock) + +p = subparsers.add_parser('lock') +p.add_argument('-v', '--verbose', action='store_true', help="be verbose") +p.set_defaults(subcommand_func=lock) + +args = parser.parse_args() + +try: + main(**vars(args)) +except KeyboardInterrupt: + raise SystemExit('... aborted') diff --git a/debops-task b/debops-task new file mode 100755 index 0000000..f0aaff8 --- /dev/null +++ b/debops-task @@ -0,0 +1,74 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +debops-task: run ansible with some customization +""" +# Copyright (C) 2014-2015 Hartmut Goebel +# Part of the DebOps - https://debops.org/ + +# This program is free software; you can redistribute +# it and/or modify it under the terms of the +# GNU General Public License as published by the Free +# Software Foundation; either version 3 of the License, +# or (at your option) any later version. +# +# This program is distributed in the hope that it will +# be useful, but WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General +# Public License along with this program; if not, +# write to the Free Software Foundation, Inc., 59 +# Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# An on-line copy of the GNU General Public License can +# be downloaded from the FSF web page at: +# https://www.gnu.org/copyleft/gpl.html + + +from __future__ import print_function + +from debops import * +from debops.cmds import * + +__author__ = "Hartmut Goebel " +__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " +__licence__ = "GNU General Public License version 3 (GPL v3) or later" + + +DEBOPS_RESERVED_NAMES = ["task", "init", "update", "defaults", "padlock"] + +# ---- DebOps environment setup ---- + +# Find DebOps configuration file +project_root = find_debops_project(required=True) +# Source DebOps configuration file +###----- todo: need to decide on semantics! +#config = read_config(project_root) + + +# ---- Main script ---- + +# Make sure required commands are present +require_commands('ansible') + +ansible_inventory = find_inventorypath(project_root) + +# Get module name from the script name if script is named 'debops-*' +module_name = SCRIPT_NAME.rsplit('-', 1)[-1] +if module_name not in DEBOPS_RESERVED_NAMES: + module = ["-m", module_name] +else: + module = [] + +os.environ['ANSIBLE_HOSTS'] = os.path.abspath(ansible_inventory) + +# Allow insecure SSH connections if requested +if INSECURE: + os.environ['ANSIBLE_HOST_KEY_CHECKING'] = 'False' + +# Run ansible with custom environment +cmd = ['ansible'] + module + sys.argv[1:] +subprocess.call(cmd) diff --git a/debops-update b/debops-update new file mode 100755 index 0000000..ab4ca84 --- /dev/null +++ b/debops-update @@ -0,0 +1,247 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +(""" +debops-update: install or update DebOps playbooks and roles +""" +# Copyright (C) 2014-2015 Hartmut Goebel +# Part of the DebOps - https://debops.org/ + + +# This program is free software; you can redistribute +# it and/or modify it under the terms of the +# GNU General Public License as published by the Free +# Software Foundation; either version 3 of the License, +# or (at your option) any later version. +# +# This program is distributed in the hope that it will +# be useful, but WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General +# Public License along with this program; if not, +# write to the Free Software Foundation, Inc., 59 +# Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# An on-line copy of the GNU General Public License can +# be downloaded from the FSF web page at: +# https://www.gnu.org/copyleft/gpl.html + +""" +This script can be used to install or update installed DebOps playbooks and +roles to current or specified version. By default it works on the installed +playbook in users $HOME/.local/share/debops directory, but it can also be +used on locally installed playbooks and roles in current directory. + +Short usage guide: + +- 'debops-update' will check if we are in DebOps project directory + ('.debops.cfg' exists) + * if yes, it will check if 'debops-playbooks/playbooks/site.yml' exists + * if yes, update playbooks and roles in $PWD + * if no, check if DebOps playbooks are installed in known places, + like ~/.local/share/debops + * if yes, update playbooks in a place that they are installed at + * if no, install DebOps playbooks in + ~/.local/share/debops/debops-playbooks + +- 'debops-update path/to/dir' will check if specified directory exists + * if no, create it + * if yes, check if DebOps playbooks are installed at $path/debops-playbooks + * if yes, update them + * if no, install DebOps playbooks at $path/debops-playbooks + +""") + +from __future__ import print_function + +import os +import subprocess +import argparse + +from debops import * +from debops.cmds import * + +__author__ = "Hartmut Goebel " +__copyright__ = "Copyright 2014-2015 by Hartmut Goebel " +__licence__ = "GNU General Public License version 3 (GPL v3) or later" + + +# ---- Configuration variables ---- + +# Default URI of DebOps (user https for server authentication) +GIT_URI = "https://github.com/debops" + +# Default git sources for debops-playbooks +PLAYBOOKS_GIT_URI = GIT_URI + "/debops-playbooks" + +# Default slug prefix for roles +GIT_ROLE_PREFIX = "ansible-" + +# Ansible Galaxy requirements file to use by default to download or update +GALAXY_REQUIREMENTS = "galaxy/requirements.txt" + +# Default Ansible Galaxy user account name +GALAXY_ACCOUNT = "debops" + + +# ---- Functions ---- + +def fetch_or_clone_roles(roles_path, requirements_file, dry_run=False): + """ + Efficiently fetch or clone a role + """ + with open(requirements_file) as fh: + requirements = [r.strip().split() for r in fh.readlines()] + num_roles = len(requirements) + + for cnt, role_name in enumerate(requirements, 1): + # Parse the requirements.txt file to extract the role name and version + try: + role_name, role_version = role_name[:2] + except: + role_name = role_name[0] + role_version = 'master' + + # :todo: rethink if we really want this + if role_name.startswith(GALAXY_ACCOUNT + '.'): + galaxy_name = role_name + role_name = role_name.split('.', 1)[1] + else: + galaxy_name = GALAXY_ACCOUNT + '.' + role_name + + remote_uri = GIT_URI + '/' + GIT_ROLE_PREFIX + role_name + destination_dir = os.path.join(roles_path, galaxy_name) + progress_label="[{role_version}] ({cnt}/{num_roles})".format(**locals()) + + # Either update or clone the role + if os.path.exists(destination_dir): + print("Updating", remote_uri, progress_label) + update_git_repository(destination_dir, dry_run, remote_uri) + else: + print() + print("Installing", remote_uri, progress_label) + clone_git_repository(remote_uri, role_version, destination_dir, dry_run) + print() + + +def clone_git_repository(repo_uri, branch, destination, dry_run=False): + if dry_run: + print("Cloning '%s' to %s..." % (repo_uri, destination)) + else: + subprocess.call(['git', 'clone', '--quiet', '--branch', branch, + repo_uri, destination]) + +def update_git_repository(path, dry_run=False, remote_uri=False): + """ + Update an exiting git repository. + + To get nice output, merge only of origin as updates. + """ + # Move into the role's directory + old_pwd = os.getcwd() + os.chdir(path) + + if dry_run: + subprocess.call(['git', 'fetch']) + subprocess.call(['git', 'diff', 'HEAD', 'origin', '--stat']) + else: + # Get the current sha of the head branch + current_sha = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip() + + # Fetch it silently and store the new sha + subprocess.call(['git', 'fetch', '--quiet']) + fetch_sha = subprocess.check_output(['git', 'rev-parse', 'FETCH_HEAD']).strip() + + if current_sha != fetch_sha: + print() + print('--') + subprocess.call(['git', 'merge', fetch_sha]) + + if remote_uri: + compare_uri = remote_uri + '/compare/' + current_sha[:7] + '...' + fetch_sha[:7] + print() + print("Compare:", compare_uri) + + print('--') + print() + + # Move back to the initial directory + os.chdir(old_pwd) + + +# ---- Main script ---- + +def main(project_dir=None, dry_run=False): + + # Check if user specified a directory as a parameter, if yes, use it as + # a project directory and clone DebOps playbooks inside + if project_dir: + # If it's a new project, create the directory for it + if not os.path.exists(project_dir): + print ("Creating project directory in", project_dir) + if not dry_run: + os.makedirs(project_dir) + + # Make sure that playbooks and roles will be installed in project + # directory if it's specified + install_path = os.path.join(project_dir, "debops-playbooks") + + # If playbooks already are installed in specified location, set them as + # currently used for eventual update + if os.path.isfile(os.path.join(install_path, DEBOPS_SITE_PLAYBOOK)): + playbooks_path = install_path + else: + playbooks_path = None + + else: + # If there's no project specified, look for playbooks in known locations + project_root = find_debops_project(required=False) + config = read_config(project_root) + playbooks_path = find_playbookpath(config, project_root, required=False) + if playbooks_path: + install_path = os.path.dirname(playbooks_path) + else: + install_path = config['paths']['install-path'] + + roles_path = os.path.join(install_path, 'roles') + + # ---- Create or update the playbooks and roles ---- + + # Playbooks have not been found, at this point assume playbooks are not + # installed. Install them in user home directory + if not playbooks_path: + if dry_run: + raise SystemExit("--dry-run requires DebOps playbooks.\n" \ + "Run debops-update without --dry-run first.") + + # Download/clone main debops-playbooks repository + print("DebOps playbooks have not been found, installing into", + install_path) + print() + + clone_git_repository(PLAYBOOKS_GIT_URI, 'master', install_path, dry_run) + os.chdir(install_path) + os.makedirs(roles_path) + else: + # Update found debops-playbooks repository + print("DebOps playbooks have been found in", install_path) + update_git_repository(install_path, dry_run) + print() + os.chdir(install_path) + + # Now install or update the roles into roles_path + fetch_or_clone_roles(roles_path, GALAXY_REQUIREMENTS, dry_run) + + +parser = argparse.ArgumentParser() +parser.add_argument('--dry-run', action='store_true', + help='perform a trial run with no changes made') +parser.add_argument('project_dir', nargs='?') +args = parser.parse_args() + +try: + main(args.project_dir, args.dry_run) +except KeyboardInterrupt: + raise SystemExit('... aborted') diff --git a/docker-compose b/docker-compose new file mode 100755 index 0000000..feafd8e Binary files /dev/null and b/docker-compose differ diff --git a/exa b/exa deleted file mode 120000 index fb8fe63..0000000 --- a/exa +++ /dev/null @@ -1 +0,0 @@ -exa-linux-x86_64-0.7.0 \ No newline at end of file diff --git a/exa b/exa new file mode 100755 index 0000000..d7c4f75 Binary files /dev/null and b/exa differ diff --git a/loran_warez.sh b/loran_warez.sh new file mode 100755 index 0000000..e6d5e27 --- /dev/null +++ b/loran_warez.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +#----------------------------------------------------------------------- +# VARIABLES +#----------------------------------------------------------------------- + +# the warez dir +WAREZ="lib" +# remote +R_USER="warez" +R_HOST="ks3367494.wezee.eu" +R_PATH="/usr/local/warez" +# rsync +# bwlimit peutêtre défini à 2000 pour limiter la conso de bande passante +RSYNC_DAEMON="/usr/local/bin/rsync" +RSYNC_OPT="--quiet --recursive --update --compress --stats --human-readable --ipv4" +RSYNC_BWLIMIT="--bwlimit=6000" +# ssh +SSH_PORT="22420" +SSH_RSA="/home/lsm/.ssh/warez_id_rsa" +#local +L_USER="lsm" +L_GROUP="lsm" +L_PATH="/usr/local/download" + +#----------------------------------------------------------------------- +# JOURNAL +#----------------------------------------------------------------------- + +LOG_DIR="/var/log/warez" +if [ ! -d ${LOG_DIR} ]; then + mkdir ${LOG_DIR} +fi +LOG="${LOG_DIR}/`date +%Y%m%d%H%M`.log" +touch ${LOG} + +#----------------------------------------------------------------------- +# TESTS +#----------------------------------------------------------------------- + +# le script est-il déjà en cours d'exécution ? +if [ -f "${LOCK}" ]; then + printf "\n Warez job's is already in progress, we'll try again later...\n\n" >> ${LOG} + exit 1 +fi + +# es-ce que $R_HOST est reachable ? +ping -c 3 ${R_HOST} +if [ "$?" -eq "0" ]; then + printf "\n ${R_HOST} is reachable, let's go for the warez job's \n" >> ${LOG} + else + printf "\n HOUSTON we've lost ${R_HOST}, no warez job's for now :( \n" >> ${LOG} + exit 1 +fi + +# +#----------------------------------------------------------------------- +# GRAB MOI TOUT CE VILAIN STUFF BRO' +#----------------------------------------------------------------------- +# + +# we're starting bro' +LOCK="/tmp/frw.run" +touch ${LOCK} + +# grab all of 'em +SSH="ssh -l ${R_USER} -i ${SSH_RSA} -p ${SSH_PORT}" +RSYNC="${RSYNC_DAEMON} ${RSYNC_OPT} ${RSYNC_BWLIMIT}" +${RSYNC} -e "${SSH}" ${R_USER}@${R_HOST}:${R_PATH}/${WAREZ} ${L_PATH} >> ${LOG} 2>&1 + +# btw fix owner and permissions +chown -R ${L_USER}:${L_GROUP} ${L_PATH}/${WAREZ} >> ${LOG} 2>&1 +find ${L_PATH}/${WAREZ} -type d -exec chmod 755 {} \; >> ${LOG} 2>&1 +find ${L_PATH}/${WAREZ} -type f -exec chmod 644 {} \; >> ${LOG} 2>&1 + +# we're done bro' +rm ${LOCK} +exit 0 + +#-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_- +#entrée dans le /etc/crontab +# 1st line is for normal days of the week +# 2nd line have to be commented if you're at home and need full Internet bandwidth! +# 3rd line is for week-end time +#23 1-10 * * mon,tue,wed,thu root /bin/sh /root/fetch_remote_warez.sh +#32 11-17 * * mon,tue,wed,thu root /bin/sh /root/fetch_remote_warez.sh +#23 5-10 * * fri,sat,sun root /bin/sh /root/fetch_remote_warez.sh + +#mais je vais modifier ça, et faire tester au script la présence d'un fichier nowarez dans un rep accessible via samba, if true = no warez job else diff --git a/netaddr b/netaddr new file mode 100755 index 0000000..2a0382d --- /dev/null +++ b/netaddr @@ -0,0 +1,37 @@ +#!/usr/bin/python +#----------------------------------------------------------------------------- +# Copyright (c) 2008 by David P. D. Moss. All rights reserved. +# +# Released under the BSD license. See the LICENSE file for details. +#----------------------------------------------------------------------------- +"""an interactive shell for the netaddr library""" + +import os +import sys +import netaddr +from netaddr import * + +# aliases to save some typing ... +from netaddr import IPAddress as IP, IPNetwork as CIDR +from netaddr import EUI as MAC + +argv = sys.argv[1:] + +banner = "\nnetaddr shell %s - %s\n" % (netaddr.__version__, __doc__) +exit_msg = "\nShare and enjoy!" +rc_override = None + +try: + try: + # ipython >= 0.11 + from IPython.terminal.embed import InteractiveShellEmbed + ipshell = InteractiveShellEmbed(banner1=banner, exit_msg=exit_msg) + except ImportError: + # ipython < 0.11 + from IPython.Shell import IPShellEmbed + ipshell = IPShellEmbed(argv, banner, exit_msg, rc_override) +except ImportError: + sys.stderr.write('IPython (http://ipython.scipy.org/) not found!\n') + sys.exit(1) + +ipshell() diff --git a/qutebrowser-git b/qutebrowser-git new file mode 100755 index 0000000..429e911 --- /dev/null +++ b/qutebrowser-git @@ -0,0 +1,2 @@ +#!/bin/sh +~/depot/qutebrowser/.venv/bin/python3 -m qutebrowser --backend webengine "$@" diff --git a/rocketchat b/rocketchat new file mode 120000 index 0000000..66eb8a7 --- /dev/null +++ b/rocketchat @@ -0,0 +1 @@ +/opt/Rocket.Chat+/rocketchat \ No newline at end of file diff --git a/update-motd.d/10-uname b/update-motd.d/10-uname new file mode 100755 index 0000000..92773ce --- /dev/null +++ b/update-motd.d/10-uname @@ -0,0 +1,2 @@ +#!/bin/sh +uname -snrvm