Started yet another new scheme for managing commands.

development
Shawn Davis 3 years ago
parent 8f7a6c5647
commit b2903c1490
  1. 6
      scripttease/exceptions.py
  2. 0
      scripttease/lib/commands/__init__.py
  3. 389
      scripttease/lib/commands/base.py
  4. 279
      scripttease/lib/commands/centos.py
  5. 92
      scripttease/lib/commands/django.py
  6. 51
      scripttease/lib/commands/messages.py
  7. 173
      scripttease/lib/commands/mysql.py
  8. 155
      scripttease/lib/commands/pgsql.py
  9. 532
      scripttease/lib/commands/posix.py
  10. 39
      scripttease/lib/commands/python.py
  11. 337
      scripttease/lib/commands/ubuntu.py
  12. 62
      scripttease/lib/loaders/base.py
  13. 2
      scripttease/lib/loaders/ini.py

@ -0,0 +1,6 @@
class InvalidInput(Exception):
pass
class UnknownCommand(Exception):
pass

@ -0,0 +1,389 @@
# Imports
from commonkit import parse_jinja_template, read_file
from jinja2 import TemplateNotFound, TemplateError
import logging
import os
log = logging.getLogger(__name__)
# Exports
__all__ = (
"EXCLUDED_KWARGS",
"run",
"Command",
"ItemizedCommand",
"Sudo",
"Template",
)
# Constants
EXCLUDED_KWARGS = [
"cd",
"comment",
"condition",
"prefix",
"register",
"stop",
"sudo",
"tags",
]
# Functions
def run(statement, **kwargs):
"""Run any statement.
- statement (str): The statement to be executed.
"""
kwargs.setdefault("comment", "run statement")
return Command(statement, **kwargs)
# Classes
class Command(object):
def __init__(self, statement, cd=None, comment=None, condition=None, prefix=None, register=None, stop=False, sudo=None, tags=None, **kwargs):
self.cd = cd
self.comment = comment
self.condition = condition
self.prefix = prefix
self.register = register
self.statement = statement
self.stop = stop
self.tags = tags or list()
if isinstance(sudo, Sudo):
self.sudo = sudo
elif type(sudo) is str:
self.sudo = Sudo(enabled=True, user=sudo)
elif sudo is True:
self.sudo = Sudo(enabled=True)
else:
self.sudo = Sudo()
self.options = kwargs
def __getattr__(self, item):
return self.options.get(item)
def __repr__(self):
if self.comment:
return "<%s %s>" % (self.__class__.__name__, self.comment)
return "<%s>" % self.__class__.__name__
def get_statement(self, cd=True, include_comment=True, include_register=True, include_stop=True):
"""Get the full statement.
:param cd: Include the directory change, if given.
:type cd: bool
:param suppress_comment: Don't include the comment.
:type suppress_comment: bool
:rtype: str
"""
a = list()
if cd and self.cd is not None:
a.append("( cd %s &&" % self.cd)
if self.prefix is not None:
a.append("%s &&" % self.prefix)
if self.sudo:
statement = "%s %s" % (self.sudo, self._get_statement())
else:
statement = self._get_statement()
a.append("%s" % statement)
if cd and self.cd is not None:
a.append(")")
b = list()
if self.comment is not None and include_comment:
b.append("# %s" % self.comment)
if self.condition is not None:
b.append("if [[ %s ]]; then %s; fi;" % (self.condition, " ".join(a)))
else:
b.append(" ".join(a))
if self.register is not None and include_register:
b.append("%s=$?;" % self.register)
if self.stop and include_stop:
b.append("if [[ $%s -gt 0 ]]; exit 1; fi;" % self.register)
elif self.stop and include_stop:
b.append("if [[ $? -gt 0 ]]; exit 1; fi;")
else:
pass
return "\n".join(b)
@property
def is_itemized(self):
"""Always returns ``False``."""
return False
def _get_statement(self):
"""By default, get the statement passed upon command initialization.
:rtype: str
"""
return self.statement
class Sudo(object):
"""Helper class for defining sudo options."""
def __init__(self, enabled=False, user="root"):
"""Initialize the helper.
:param enabled: Indicates sudo is enabled.
:type enabled: bool
:param user: The user to be invoked.
:type user: str
"""
self.enabled = enabled
self.user = user
def __bool__(self):
return self.enabled
def __str__(self):
if self.enabled:
return "sudo -u %s" % self.user
return ""
class ItemizedCommand(object):
"""An itemized command represents multiple commands of with the same statement but different parameters."""
def __init__(self, callback, items, *args, name=None, **kwargs):
"""Initialize the command.
:param callback: The function to be used to generate the command.
:param items: The command arguments.
:type items: list[str]
:param name: The name of the command from the mapping. Not used and not required for programmatic use, but
automatically assigned during factory instantiation.
:type name: str
:param args: The itemized arguments. ``$item`` should be included.
Keyword arguments are passed to the command class upon instantiation.
"""
self.args = args
self.callback = callback
self.items = items
self.kwargs = kwargs
self.name = name
# Set defaults for when ItemizedCommand is referenced directly before individual commands are instantiated. For
# example, when command filtering occurs.
self.kwargs.setdefault("tags", list())
def __getattr__(self, item):
return self.kwargs.get(item)
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, self.callback.__name__)
def get_commands(self):
"""Get the commands to be executed.
:rtype: list[BaseType(Command)]
"""
kwargs = self.kwargs.copy()
a = list()
for item in self.items:
args = list()
for arg in self.args:
args.append(arg.replace("$item", item))
command = self.callback(*args, **kwargs)
a.append(command)
return a
def get_statement(self, cd=True, include_comment=True, include_register=True, include_stop=True):
"""Override to get multiple commands."""
kwargs = self.kwargs.copy()
comment = kwargs.pop("comment", "execute multiple commands")
a = list()
# a.append("# %s" % comment)
commands = self.get_commands()
for c in commands:
a.append(c.get_statement(cd=cd, include_comment=False, include_register=include_register, include_stop=include_stop))
a.append("")
return "\n".join(a)
@property
def is_itemized(self):
"""Always returns ``True``."""
return True
class Template(object):
PARSER_JINJA = "jinja2"
PARSER_PYTHON = "python"
PARSER_SIMPLE = "simple"
def __init__(self, source, target, backup=True, parser=PARSER_JINJA, **kwargs):
self.backup_enabled = backup
self.context = kwargs.pop("context", dict())
self.name = "template"
self.parser = parser
self.language = kwargs.pop("lang", None)
self.locations = kwargs.pop("locations", list())
self.source = os.path.expanduser(source)
self.target = target
sudo = kwargs.pop("sudo", None)
if isinstance(sudo, Sudo):
self.sudo = sudo
elif type(sudo) is str:
self.sudo = Sudo(enabled=True, user=sudo)
elif sudo is True:
self.sudo = Sudo(enabled=True)
else:
self.sudo = Sudo()
self.kwargs = kwargs
def __getattr__(self, item):
return self.kwargs.get(item)
def __str__(self):
return "template"
def get_content(self):
"""Parse the template.
:rtype: str | None
"""
template = self.get_template()
if self.parser == self.PARSER_SIMPLE:
content = read_file(template)
for key, value in self.context.items():
replace = "$%s$" % key
content = content.replace(replace, str(value))
return content
if self.parser == self.PARSER_PYTHON:
content = read_file(template)
return content % self.context
try:
return parse_jinja_template(template, self.context)
except TemplateNotFound:
log.error("Template not found: %s" % template)
return None
except TemplateError as e:
log.error("Could not parse %s template: %s" % (template, e))
return None
# noinspection PyUnusedLocal
def get_statement(self, cd=True, include_comment=True, include_register=True, include_stop=True):
lines = list()
if include_comment and self.comment is not None:
lines.append("# %s" % self.comment)
# TODO: Backing up a template's target is currently specific to bash.
if self.backup_enabled:
command = "%s mv %s %s.b" % (self.sudo, self.target, self.target)
lines.append('if [[ -f "%s" ]]; then %s; fi;' % (self.target, command.lstrip()))
# Get the content; e.g. parse the template.
content = self.get_content()
# Templates that are bash scripts will fail to write because of the shebang.
if content.startswith("#!"):
_content = content.split("\n")
first_line = _content.pop(0)
command = '%s echo "%s" > %s' % (self.sudo, first_line, self.target)
lines.append(command.lstrip())
command = "%s cat > %s << EOF" % (self.sudo, self.target)
lines.append(command.lstrip())
lines.append("\n".join(_content))
lines.append("EOF")
else:
command = "%s cat > %s << EOF" % (self.sudo, self.target)
lines.append(command.lstrip())
lines.append(content)
lines.append("EOF")
if include_register and self.register is not None:
lines.append("%s=$?;" % self.register)
if include_stop and self.stop:
lines.append("if [[ $%s -gt 0 ]]; exit 1; fi;" % self.register)
elif include_stop and self.stop:
lines.append("if [[ $? -gt 0 ]]; exit 1; fi;")
else:
pass
return "\n".join(lines)
def get_target_language(self):
if self.language is not None:
return self.language
if self.target.endswith(".conf"):
return "conf"
elif self.target.endswith(".ini"):
return "ini"
elif self.target.endswith(".php"):
return "php"
elif self.target.endswith(".py"):
return "python"
elif self.target.endswith(".sh"):
return "bash"
elif self.target.endswith(".yml"):
return "yaml"
else:
return "text"
def get_template(self):
"""Get the template path.
:rtype: str
"""
source = self.source
for location in self.locations:
_source = os.path.join(location, self.source)
if os.path.exists(_source):
return _source
return source
@property
def is_itemized(self):
# return "$item" in self.target
return False

@ -0,0 +1,279 @@
# Imports
from commonkit import split_csv
from .base import Command, Template
from .django import DJANGO_MAPPINGS
from .mysql import MYSQL_MAPPINGS
from .pgsql import PGSQL_MAPPINGS
from .posix import POSIX_MAPPINGS
# Exports
__all__ = (
"MAPPINGS",
"apache",
"apache_reload",
"apache_restart",
"apache_start",
"apache_stop",
"apache_test",
"command_exists",
"service_reload",
"service_restart",
"service_start",
"service_stop",
"system",
"system_install",
"system_reboot",
"system_update",
"system_upgrade",
"system_uninstall",
"template",
"user",
)
def command_exists(name):
"""Indicates whether a given command exists in this overlay.
:param name: The name of the command.
:type name: str
:rtype: bool
"""
return name in MAPPINGS
def apache(op, **kwargs):
"""Execute an Apache-related command.
- op (str): The operation to perform; reload, restart, start, stop, test.
"""
if op == "reload":
return apache_reload(**kwargs)
elif op == "restart":
return apache_restart(**kwargs)
elif op == "start":
return apache_start(**kwargs)
elif op == "stop":
return apache_stop(**kwargs)
elif op == "test":
return apache_test(**kwargs)
else:
raise NameError("Unrecognized or unsupported apache operation: %s" % op)
def apache_reload(**kwargs):
kwargs.setdefault("comment", "reload apache")
kwargs.setdefault("register", "apache_reloaded")
return Command("apachectl –k reload", **kwargs)
def apache_restart(**kwargs):
kwargs.setdefault("comment", "restart apache")
kwargs.setdefault("register", "apache_restarted")
return Command("apachectl –k restart", **kwargs)
def apache_start(**kwargs):
kwargs.setdefault("comment", "start apache")
kwargs.setdefault("register", "apache_started")
return Command("apachectl –k start", **kwargs)
def apache_stop(**kwargs):
kwargs.setdefault("comment", "stop apache")
return Command("apachectl –k stop", **kwargs)
def apache_test(**kwargs):
kwargs.setdefault("comment", "check apache configuration")
kwargs.setdefault("register", "apache_checks_out")
return Command("apachectl configtest", **kwargs)
def service_reload(name, **kwargs):
"""Reload a service.
- name (str): The service name.
"""
kwargs.setdefault("comment", "reload %s service" % name)
kwargs.setdefault("register", "%s_reloaded" % name)
return Command("systemctl reload %s" % name, **kwargs)
def service_restart(name, **kwargs):
"""Restart a service.
- name (str): The service name.
"""
kwargs.setdefault("comment", "restart %s service" % name)
kwargs.setdefault("register", "%s_restarted" % name)
return Command("ssystemctl restart %s" % name, **kwargs)
def service_start(name, **kwargs):
"""Start a service.
- name (str): The service name.
"""
kwargs.setdefault("comment", "start %s service" % name)
kwargs.setdefault("register", "%s_started" % name)
return Command("systemctl start %s" % name, **kwargs)
def service_stop(name, **kwargs):
"""Stop a service.
- name (str): The service name.
"""
kwargs.setdefault("comment", "stop %s service" % name)
kwargs.setdefault("register", "%s_stopped" % name)
return Command("systemctl stop %s" % name, **kwargs)
def system(op, **kwargs):
"""Perform a system operation.
- op (str): The operation to perform; reboot, update, upgrade.
"""
if op == "reboot":
return system_reboot(**kwargs)
elif op == "update":
return system_update(**kwargs)
elif op == "upgrade":
return system_upgrade(**kwargs)
else:
raise NameError("Unrecognized or unsupported system operation: %s" % op)
def system_install(name, **kwargs):
"""Install a system-level package.
- name (str): The name of the package to install.
"""
kwargs.setdefault("comment", "install system package %s" % name)
return Command("yum install -y %s" % name, **kwargs)
def system_reboot(**kwargs):
kwargs.setdefault("comment", "reboot the system")
return Command("reboot", **kwargs)
def system_uninstall(name, **kwargs):
"""Uninstall a system-level package.
- name (str): The name of the package to uninstall.
"""
kwargs.setdefault("comment", "remove system package %s" % name)
return Command("yum remove -y %s" % name, **kwargs)
def system_update(**kwargs):
kwargs.setdefault("comment", "update system package info")
return Command("yum check-update", **kwargs)
def system_upgrade(**kwargs):
kwargs.setdefault("comment", "upgrade the system")
return Command("yum update -y", **kwargs)
def template(source, target, backup=True, parser=None, **kwargs):
"""Create a file from a template.
- source (str): The path to the template file.
- target (str): The path to where the new file should be created.
- backup (bool): Indicates whether a backup should be made if the target file already exists.
- parser (str): The parser to use ``jinja`` (the default) or ``simple``.
"""
return Template(source, target, backup=backup, parser=parser, **kwargs)
def user(name, groups=None, home=None, op="add", password=None, **kwargs):
"""Create or remove a user.
- name (str): The user name.
- groups (str | list): A list of groups to which the user should belong.
- home (str): The path to the user's home directory.
- op (str); The operation to perform; ``add`` or ``remove``.
- password (str): The user's password. (NOT IMPLEMENTED)
"""
if op == "add":
kwargs.setdefault("comment", "create a user named %s" % name)
commands = list()
a = list()
a.append('adduser %s' % name)
if home is not None:
a.append("--home %s" % home)
commands.append(Command(" ".join(a), **kwargs))
if type(groups) is str:
groups = split_csv(groups, smart=False)
if type(groups) in [list, tuple]:
for group in groups:
commands.append(Command("gpasswd -a %s %s" % (name, group), **kwargs))
a = list()
for c in commands:
a.append(c.get_statement(include_comment=True))
return Command("\n".join(a), **kwargs)
elif op == "remove":
kwargs.setdefault("comment", "remove a user named %s" % name)
return Command("userdel -r %s" % name, **kwargs)
else:
raise NameError("Unsupported or unrecognized operation: %s" % op)
MAPPINGS = {
'apache': apache,
'install': system_install,
'reboot': system_reboot,
'reload': service_reload,
'restart': service_restart,
'start': service_start,
'stop': service_stop,
'system': system,
'template': template,
'update': system_update,
'uninstall': system_uninstall,
'upgrade': system_upgrade,
'user': user,
}
MAPPINGS.update(COMMON_MAPPINGS)
MAPPINGS.update(DJANGO_MAPPINGS)
MAPPINGS.update(MYSQL_MAPPINGS)
MAPPINGS.update(PGSQL_MAPPINGS)
MAPPINGS.update(POSIX_MAPPINGS)

@ -0,0 +1,92 @@
"""
[run django checks]
django: check
[export fixtures]
django: dump lookups.Category
[import fixtures]
django: load lookups.Category
[migrate the database]
django: migrate
[collect static files]
django: static
[create super user (ad hoc command)]
django: createsuperuser root
"""
from .base import EXCLUDED_KWARGS, Command
def django(management_command, *args, excluded_kwargs=None, **kwargs):
# The excluded parameters (filtered below) may vary based on implementation. We do, however, need a default.
excluded_kwargs = excluded_kwargs or EXCLUDED_KWARGS
# Django's management commands can have a number of options. We need to filter out internal parameters so that these
# are not used as options for the management command.
_kwargs = dict()
for key in excluded_kwargs:
if key in kwargs:
_kwargs[key] = kwargs.pop(key)
if 'comment' not in _kwargs:
_kwargs['comment'] = "run %s django management command" % management_command
a = list()
a.append("./manage.py %s" % management_command)
for key, value in kwargs.items():
key = key.replace("_", "-")
if type(value) is bool and value is True:
a.append("--%s" % key)
elif type(value) is str:
a.append('--%s="%s"' % (key, value))
else:
a.append('--%s=%s' % (key, value))
_args = list(args)
if len(_args) > 0:
a.append(" ".join(_args))
statement = " ".join(a)
return Command(statement, **_kwargs)
def django_check(**kwargs):
kwargs.setdefault("comment", "run django checks")
kwargs.setdefault("register", "django_checks_out")
return django("check", **kwargs)
def django_dump(target, path=None, **kwargs):
kwargs.setdefault("comment", "dump app/model data")
kwargs.setdefault("format", "json")
kwargs.setdefault("indent", 4)
if path is None:
path = "../deploy/fixtures/%s.%s" % (target, kwargs['format'])
return django("dumpdata", target, "> %s" % path, **kwargs)
def django_load(target, path=None, **kwargs):
kwargs.setdefault("comment", "load app/model data")
input_format = kwargs.pop("format", "json")
if path is None:
path = "../deploy/fixtures/%s.%s" % (target, input_format)
return django("loaddata", path, **kwargs)
def django_migrate(**kwargs):
kwargs.setdefault("comment", "apply database migrations")
return django("migrate", **kwargs)
def django_static(**kwargs):
kwargs.setdefault("comment", "collect static files")
kwargs.setdefault("noinput", True)
return django("collectstatic", **kwargs)

@ -0,0 +1,51 @@
from .base import Command
from ...exceptions import InvalidInput
def dialog(message, height=15, title="Message", width=100, **kwargs):
statement = list()
statement.append("dialog --clear")
statement.append('--backtitle "%s"' % title)
statement.append('--msgbox "%s" %s %s;' % (message, height, width))
statement.append("clear;")
return Command(" ".join(statement), **kwargs)
def echo(message, **kwargs):
return Command('echo "%s"' % message, **kwargs)
def explain(message, heading=None, **kwargs):
kwargs['heading'] = heading
return Command(message, **kwargs)
def screenshot(image, caption=None, **kwargs):
kwargs['caption'] = caption
return Command(image, **kwargs)
def slack(message, url=None, **kwargs):
if url is None:
raise InvalidInput("Slack command requires a url parameter.")
statement = list()
statement.append("curl -X POST -H 'Content-type: application/json' --data")
statement.append('{"text": "%s"}' % message)
statement.append(url)
return Command(statement, **kwargs)
def twist(message, title="Notice", url=None, **kwargs):
if url is None:
raise InvalidInput("Twist command requires a url parameter.")
statement = list()
statement.append("curl -X POST -H 'Content-type: application/json' --data")
statement.append('{"content": "%s", "title": "%s"' % (message, title))
statement.append(url)
return Command(" ".join(statement), **kwargs)

@ -0,0 +1,173 @@
from ...exceptions import InvalidInput
from .base import EXCLUDED_KWARGS, Command
__all__ = (
"mysql_create",
"mysql_dump",
"mysql_exists",
"mysql_load",
"mysql_user",
)
def mysql(command, *args, host="localhost", excluded_kwargs=None, password=None, port=3306, user="root", **kwargs):
# The excluded parameters (filtered below) may vary based on implementation. We do, however, need a default.
excluded_kwargs = excluded_kwargs or EXCLUDED_KWARGS
# if 'comment' not in kwargs:
# kwargs['comment'] = "run %s mysql command" % command
# Allow additional command line switches to pass through?
# Django's management commands can have a number of options. We need to filter out internal parameters so that these
# are not used as options for the management command.
_kwargs = dict()
for key in excluded_kwargs:
if key in kwargs:
_kwargs[key] = kwargs.pop(key)
# MySQL commands always run without sudo because the --user may be provided.
_kwargs['sudo'] = False
a = list()
a.append(command)
a.append("--user %s --host=%s --port=%s" % (user, host, port))
if password:
a.append('--password="%s"' % password)
for key, value in kwargs.items():
key = key.replace("_", "-")
if type(value) is bool and value is True:
a.append("--%s" % key)
elif type(value) is str:
a.append('--%s="%s"' % (key, value))
else:
a.append('--%s=%s' % (key, value))
_args = list(args)
if len(_args) > 0:
a.append(" ".join(_args))
statement = " ".join(a)
return Command(statement, **_kwargs)
def mysql_create(database, owner=None, **kwargs):
kwargs.setdefault("comment", "create mysql database")
command = mysql("mysqladmin create", database, **kwargs)
if owner is not None:
grant = mysql_grant(owner, database=database, **kwargs)
command.statement += " && " + grant.statement
return command
def mysql_drop(database, **kwargs):
kwargs.setdefault("comment", "drop %s mysql database" % database)
return mysql("mysqladmin drop", database, **kwargs)
def mysql_dump(database, path=None, **kwargs):
kwargs.setdefault("comment", "dump mysql database")
kwargs.setdefault("complete_inserts", True)
if path is None:
path = "%s.sql" % database
return mysql("mysqldump", database, "> %s" % path, **kwargs)
def mysql_exists(database, **kwargs):
kwargs.setdefault("comment", "determine if %s mysql database exists" % database)
kwargs.setdefault("register", "%s_exists" % database)
command = mysql("mysql", **kwargs)
sql = "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '%s'" % database
command.statement += '--execute="%s"' % sql
return command
def mysql_grant(to, database=None, privileges="ALL", **kwargs):
"""Grant privileges to a user.
- to (str): The user name to which privileges are granted.
- database (str): The database name.
- host (str): The database host name or IP address.
- password (str): The password for the user with sufficient access privileges to execute the command.
- port (int): The TCP port number of the MySQL service running on the host.
- privileges (str): The privileges to be granted.
- user (str): The name of the user with sufficient access privileges to execute the command.
"""
kwargs.setdefault("comment", "grant mysql privileges to %s" % to)
host = kwargs.get("host", "localhost")
command = mysql("mysql", **kwargs)
# See https://dev.mysql.com/doc/refman/5.7/en/grant.html
_database = database or "*"
sql = "GRANT %(privileges)s ON %(database)s.* TO '%(user)s'@'%(host)s'" % {
'database': _database,
'host': host,
'privileges': privileges,
'user': to,
}
command.statement += ' --execute="%s"' % sql
return command
def mysql_load(database, path, **kwargs):
kwargs.setdefault("comment", "load data into a mysql database")
return mysql("psql", database, "< %s" % path, **kwargs)
def mysql_user(name, admin_pass=None, admin_user="root", op="create", password=None, **kwargs):
host = kwargs.get("host", "localhost")
if op == "create":
kwargs.setdefault("comment", "create %s mysql user" % name)
command = mysql("mysql", password=admin_pass, user=admin_user, **kwargs)
sql = "CREATE USER IF NOT EXISTS '%s'@'%s'" % (name, host)
if password is not None:
sql += " IDENTIFIED BY PASSWORD('%s')" % password
command.statement += ' --execute="%s"' % sql
return command
elif op == "drop":
kwargs.setdefault("comment", "remove %s mysql user" % name)
command = mysql("mysql", password=admin_pass, user=admin_user, **kwargs)
sql = "DROP USER IF EXISTS '%s'@'%s'" % (name, host)
command.statement += ' --execute="%s"' % sql
return command
elif op == "exists":
kwargs.setdefault("comment", "determine if %s mysql user exists" % name)
kwargs.setdefault("register", "mysql_use_exists")
command = mysql("mysql", password=admin_pass, user=admin_user, **kwargs)
sql = "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '%s')" % name
command.statement += ' --execute "%s"' % sql
return command
else:
raise InvalidInput("Unrecognized or unsupported MySQL user operation: %s" % op)

@ -0,0 +1,155 @@
"""
[run django checks]
django: check
[export fixtures]
django: dump lookups.Category
[import fixtures]
django: load lookups.Category
[migrate the database]
django: migrate
[collect static files]
django: static
[create super user (ad hoc command)]
django: createsuperuser root
"""
from ...exceptions import InvalidInput
from .base import EXCLUDED_KWARGS, Command
__all__ = (
"pgsql_create",
"pgsql_drop",
"pgsql_dump",
"pgsql_exists",
"pgsql_load",
"pgsql_user",
)
def pgsql(command, *args, host="localhost", excluded_kwargs=None, password=None, port=5432, user="postgres", **kwargs):
# The excluded parameters (filtered below) may vary based on implementation. We do, however, need a default.
excluded_kwargs = excluded_kwargs or EXCLUDED_KWARGS
# if 'comment' not in kwargs:
# kwargs['comment'] = "run %s postgres command" % command
# Allow additional command line switches to pass through?
# Django's management commands can have a number of options. We need to filter out internal parameters so that these
# are not used as options for the management command.
_kwargs = dict()
for key in excluded_kwargs:
if key in kwargs:
_kwargs[key] = kwargs.pop(key)
# Postgres commands always run without sudo because the -U may be provided.
_kwargs['sudo'] = False
a = list()
if password is not None:
a.append('export PGPASSWORD="%s" &&' % password)
a.append(command)
a.append("-U %s --host=%s --port=%s" % (user, host, port))
for key, value in kwargs.items():
key = key.replace("_", "-")
if type(value) is bool and value is True:
a.append("--%s" % key)
elif type(value) is str:
a.append('--%s="%s"' % (key, value))
else:
a.append('--%s=%s' % (key, value))
_args = list(args)
if len(_args) > 0:
a.append(" ".join(_args))
statement = " ".join(a)
return Command(statement, **_kwargs)
def pgsql_create(database, owner=None, template=None, **kwargs):
kwargs.setdefault("comment", "create %s postgres database" % database)
if owner is not None:
kwargs['owner'] = owner
if template is not None:
kwargs['template'] = template
return pgsql("createdb", database, **kwargs)
def pgsql_drop(database, **kwargs):
kwargs.setdefault("comment", "drop %s postgres database" % database)
return pgsql("dropdb", database, **kwargs)
def pgsql_dump(database, path=None, **kwargs):
kwargs.setdefault("comment", "dump postgres database")
kwargs.setdefault("column_inserts", True)
if path is None:
path = "%s.sql" % database
kwargs['dbname'] = database
kwargs['file'] = path
return pgsql("pg_dump", **kwargs)
def pgsql_exists(database, **kwargs):
kwargs.setdefault("comment", "determine if %s postgres database exists" % database)
kwargs.setdefault("register", "%s_exists" % database)
command = pgsql("psql", **kwargs)
command.statement += r" -lqt | cut -d \| -f 1 | grep -qw %s" % database
return command
def pgsql_load(database, path, **kwargs):
kwargs.setdefault("comment", "load data into a postgres database")
kwargs['dbname'] = database
kwargs['file'] = path
return pgsql("psql", **kwargs)
def pgsql_user(name, admin_pass=None, admin_user="postgres", op="create", password=None, **kwargs):
if op == "create":
kwargs.setdefault("comment", "create %s postgres user" % name)
command = pgsql("createuser", "-DRS %s" % name, password=admin_pass, user=admin_user, **kwargs)
if password is not None:
extra = pgsql("psql", password=admin_pass, user=admin_user, **kwargs)
command.statement += " && " + extra.statement
command.statement += " -c \"ALTER USER %s WITH ENCRYPTED PASSWORD '%s';\"" % (name, password)
return command
elif op == "drop":
kwargs.setdefault("comment", "remove %s postgres user" % name)
return pgsql("dropuser", name, password=admin_pass, user=admin_user, **kwargs)
elif op == "exists":
kwargs.setdefault("comment", "determine if %s postgres user exists" % name)
kwargs.setdefault("register", "pgsql_use_exists")
command = pgsql("psql", password=admin_pass, user=admin_user, **kwargs)
sql = "SELECT 1 FROM pgsql_roles WHERE rolname='%s'" % name
command.statement += ' -c "%s"' % sql
return command
else:
raise InvalidInput("Unrecognized or unsupported Postgres user operation: %s" % op)

@ -0,0 +1,532 @@
import os
from .base import Command
def append(path, content=None, **kwargs):
"""Append content to a file.
- path (str): The path to the file.
- content (str): The content to be appended.
"""
kwargs.setdefault("comment", "append to %s" % path)
statement = 'echo "%s" >> %s' % (content or "", path)
return Command(statement, **kwargs)
def archive(from_path, absolute=False, exclude=None, file_name="archive.tgz", strip=None, to_path=".", view=False,
**kwargs):
"""Create a file archive.
- from_path (str): The path that should be archived.
- absolute (bool): Set to ``True`` to preserve the leading slash.
- exclude (str): A pattern to be excluded from the archive.
- strip (int): Remove the specified number of leading elements from the path.
- to_path (str): Where the archive should be created. This should *not* include the file name.
- view (bool): View the output of the command as it happens.
"""
tokens = ["tar"]
switches = ["-cz"]
if absolute:
switches.append("P")
if view:
switches.append("v")
tokens.append("".join(switches))
if exclude:
tokens.append("--exclude %s" % exclude)
if strip:
tokens.append("--strip-components %s" % strip)
to_path = "%s/%s" % (to_path, file_name)
tokens.append('-f %s %s' % (to_path, from_path))
name = " ".join(tokens)
return Command(name, **kwargs)
def certbot(domain_name, email=None, webroot=None, **kwargs):
"""Get new SSL certificate from Let's Encrypt.
- domain_name (str): The domain name for which the SSL certificate is requested.
- email (str): The email address of the requester sent to the certificate authority. Required.
- webroot (str): The directory where the challenge file will be created.
"""
_email = email or os.environ.get("SCRIPTTEASE_CERTBOT_EMAIL", None)
_webroot = webroot or os.path.join("/var", "www", "domains", domain_name.replace(".", "_"), "www")
if not _email:
raise ValueError("Email is required for certbot command.")
template = "certbot certonly --agree-tos --email %(email)s -n --webroot -w %(webroot)s -d %(domain_name)s"
statement = template % {
'domain_name': domain_name,
'email': _email,
'webroot': _webroot,
}
return Command(statement, **kwargs)
def copy(from_path, to_path, overwrite=False, recursive=False, **kwargs):
"""Copy a file or directory.
- from_path (str): The file or directory to be copied.
- to_path (str): The location to which the file or directory should be copied.
- overwrite (bool): Indicates files and directories should be overwritten if they exist.
- recursive (bool): Copy sub-directories.
"""
kwargs.setdefault("comment", "copy %s to %s" % (from_path, to_path))
a = list()
a.append("cp")
if not overwrite:
a.append("-n")
if recursive:
a.append("-R")
a.append(from_path)
a.append(to_path)
return Command(" ".join(a), **kwargs)
def dir(path, group=None, mode=None, owner=None, recursive=True, **kwargs):
"""Create a directory.
- path (str): The path to be created.
- mode (int | str): The access permissions of the new directory.
- recursive (bool): Create all directories along the path.
"""
kwargs.setdefault("comment", "create directory %s" % path)
statement = ["mkdir"]
if mode is not None:
statement.append("-m %s" % mode)
if recursive:
statement.append("-p")
if group:
if recursive:
statement.append("&& chgrp -R %s" % group)
else:
statement.append("&& chgrp %s" % group)
if owner:
if recursive:
statement.append("&& chown -R %s" % owner)
else:
statement.append("&& chown %s" % owner)
statement.append(path)
return Command(" ".join(statement), **kwargs)
def extract(from_path, absolute=False, exclude=None, strip=None, to_path=None, view=False, **kwargs):
"""Extract a file archive.
- from_path (str): The path that should be archived.
- absolute (bool): Set to ``True`` to preserve the leading slash.
- exclude (str): A pattern to be excluded from the archive.
- strip (int): Remove the specified number of leading elements from the path.
- to_path (str): Where the archive should be extracted. This should *not* include the file name.
- view (bool): View the output of the command as it happens.
"""
_to_path = to_path or "./"
tokens = ["tar"]
switches = ["-xz"]
if absolute:
switches.append("P")
if view:
switches.append("v")
tokens.append("".join(switches))
if exclude:
tokens.append("--exclude %s" % exclude)
if strip:
tokens.append("--strip-components %s" % strip)
tokens.append('-f %s %s' % (from_path, _to_path))
statement = " ".join(tokens)
return Command(statement, **kwargs)
def link(source, force=False, target=None, **kwargs):
"""Create a symlink.
- source (str): The source of the link.
- force (bool): Force the creation of the link.
- target (str): The name or path of the target. Defaults to the base name of the source path.
"""
_target = target or os.path.basename(source)
kwargs.setdefault("comment", "link to %s" % source)
statement = ["ln -s"]
if force:
statement.append("-f")
statement.append(source)
statement.append(_target)
return Command(" ".join(statement), **kwargs)
def move(from_path, to_path, **kwargs):
"""Move a file or directory.
- from_path (str): The current path.
- to_path (str): The new path.
"""
kwargs.setdefault("comment", "move %s to %s" % (from_path, to_path))
statement = "mv %s %s" % (from_path, to_path)
return Command(statement, **kwargs)
def perms(path, group=None, mode=None, owner=None, recursive=False, **kwargs):
"""Set permissions on a file or directory.
- path (str): The path to be changed.
- group (str): The name of the group to be applied.
- mode (int | str): The access permissions of the file or directory.
- owner (str): The name of the user to be applied.
- recursive: Create all directories along the path.
"""
commands = list()
kwargs['comment'] = "set permissions on %s" % path
if group is not None:
statement = ["chgrp"]
if recursive:
statement.append("-R")
statement.append(group)
statement.append(path)
commands.append(Command(" ".join(statement), **kwargs))
if owner is not None:
statement = ["chown"]
if recursive:
statement.append("-R")
statement.append(owner)
statement.append(path)
commands.append(Command(" ".join(statement), **kwargs))
if mode is not None:
statement = ["chmod"]
if recursive:
statement.append("-R")
statement.append(str(mode))
statement.append(path)
commands.append(Command(" ".join(statement), **kwargs))
kwargs.setdefault("comment", "set permissions on %s" % path)
a = list()
for c in commands:
a.append(c.get_statement(include_comment=True))
return Command("\n".join(a), **kwargs)
def remove(path, force=False, recursive=False, **kwargs):
"""Remove a file or directory.
- path (str): The path to be removed.
- force (bool): Force the removal.
- recursive (bool): Remove all directories along the path.
"""
kwargs.setdefault("comment", "remove %s" % path)
statement = ["rm"]
if force:
statement.append("-f")
if recursive:
statement.append("-r")
statement.append(path)
return Command(" ".join(statement), **kwargs)
def replace(path, backup=".b", delimiter="/", find=None, replace=None, **kwargs):
"""Find and replace text in a file.
- path (str): The path to the file to be edited.
- backup (str): The backup file extension to use.
- delimiter (str): The pattern delimiter.
- find (str): The old text. Required.
- replace (str): The new text. Required.
"""
kwargs.setdefault("comment", "find and replace in %s" % path)
context = {
'backup': backup,
'delimiter': delimiter,
'path': path,
'pattern': find,
'replace': replace,
}
template = "sed -i %(backup)s 's%(delimiter)s%(pattern)s%(delimiter)s%(replace)s%(delimiter)sg' %(path)s"
statement = template % context
return Command(statement, **kwargs)
def rsync(source, target, delete=False, exclude=None, host=None, key_file=None, links=True, port=22,
recursive=True, user=None, **kwargs):
"""Synchronize a directory structure.
- source (str): The source directory.
- target (str): The target directory.
- delete (bool): Indicates target files that exist in source but not in target should be removed.
- exclude (str): The path to an exclude file.
- host (str): The host name or IP address. This causes the command to run over SSH.
- key_file (str): The privacy SSH key (path) for remote connections. User expansion is automatically applied.
- links (bool): Include symlinks in the sync.
- port (int): The SSH port to use for remote connections.
- recursive (bool): Indicates source contents should be recursively synchronized.
- user (str): The user name to use for remote connections.
"""
# - guess: When ``True``, the ``host``, ``key_file``, and ``user`` will be guessed based on the base name of
# the source path.
# :type guess: bool
# if guess:
# host = host or os.path.basename(source).replace("_", ".")
# key_file = key_file or os.path.expanduser(os.path.join("~/.ssh", os.path.basename(source)))
# user = user or os.path.basename(source)
# else:
# host = host
# key_file = key_file
# user = user
kwargs.setdefault("comment", "sync %s with %s" % (source, target))
# rsync -e "ssh -i $(SSH_KEY) -p $(SSH_PORT)" -P -rvzc --delete
# $(OUTPUTH_PATH) $(SSH_USER)@$(SSH_HOST):$(UPLOAD_PATH) --cvs-exclude;
tokens = list()
tokens.append("rsync")
tokens.append("--cvs-exclude")
tokens.append("--checksum")
tokens.append("--compress")
if links:
tokens.append("--copy-links")
if delete:
tokens.append("--delete")
if exclude is not None:
tokens.append("--exclude-from=%s" % exclude)
# --partial and --progress
tokens.append("-P")
if recursive:
tokens.append("--recursive")
tokens.append(source)
conditions = [
host is not None,
key_file is not None,
user is not None,
]
if all(conditions):
tokens.append('-e "ssh -i %s -p %s"' % (key_file, port))
tokens.append("%s@%s:%s" % (user, host, target))
else:
tokens.append(target)
statement = " ".join(tokens)
return Command(statement, **kwargs)
def scopy(from_path, to_path, host=None, key_file=None, port=22, user=None, **kwargs):
"""Copy a file or directory to a remote server.
- from_path (str): The source directory.
- to_path (str): The target directory.
- host (str): The host name or IP address. Required.
- key_file (str): The privacy SSH key (path) for remote connections. User expansion is automatically applied.
- port (int): The SSH port to use for remote connections.
- user (str): The user name to use for remote connections.
"""
kwargs.setdefault("comment", "copy %s to remote %s" % (from_path, to_path))
# TODO: What to do to force local versus remote commands?
# kwargs['local'] = True
kwargs['sudo'] = False
statement = ["scp"]
if key_file is not None:
statement.append("-i %s" % key_file)
statement.append("-P %s" % port)
statement.append(from_path)
if host is not None and user is not None:
statement.append("%s@%s:%s" % (user, host, to_path))
elif host is not None:
statement.append("%s:%s" % (host, to_path))
else:
raise ValueError("Host is a required keyword argument.")
return Command(" ".join(statement), **kwargs)
def sync(source, target, delete=False, exclude=None, links=True, recursive=True, **kwargs):
"""Synchronize a local directory structure.
- source (str): The source directory.
- target (str): The target directory.
- delete (bool): Indicates target files that exist in source but not in target should be removed.
- exclude (str): The path to an exclude file.
- host (str): The host name or IP address. This causes the command to run over SSH.
- key_file (str): The privacy SSH key (path) for remote connections. User expansion is automatically applied.
- links (bool): Include symlinks in the sync.
- port (int): The SSH port to use for remote connections.
- recursive (bool): Indicates source contents should be recursively synchronized.
- user (str): The user name to use for remote connections.
"""
# - guess: When ``True``, the ``host``, ``key_file``, and ``user`` will be guessed based on the base name of
# the source path.
# :type guess: bool
# if guess:
# host = host or os.path.basename(source).replace("_", ".")
# key_file = key_file or os.path.expanduser(os.path.join("~/.ssh", os.path.basename(source)))
# user = user or os.path.basename(source)
# else:
# host = host
# key_file = key_file
# user = user
kwargs.setdefault("comment", "sync %s with %s" % (source, target))
# rsync -e "ssh -i $(SSH_KEY) -p $(SSH_PORT)" -P -rvzc --delete
# $(OUTPUTH_PATH) $(SSH_USER)@$(SSH_HOST):$(UPLOAD_PATH) --cvs-exclude;
tokens = list()
tokens.append("rsync")
tokens.append("--cvs-exclude")
tokens.append("--checksum")
tokens.append("--compress")
if links:
tokens.append("--copy-links")
if delete:
tokens.append("--delete")
if exclude is not None:
tokens.append("--exclude-from=%s" % exclude)
# --partial and --progress
tokens.append("-P")
if recursive:
tokens.append("--recursive")
tokens.append(source)
tokens.append(target)
statement = " ".join(tokens)
return Command(statement, **kwargs)
def touch(path, **kwargs):
"""Touch a file or directory.
- path (str): The file or directory to touch.
"""
kwargs.setdefault("comment", "touch %s" % path)
return Command("touch %s" % path, **kwargs)
def wait(seconds, **kwargs):
"""Pause execution for a number of seconds.
- seconds (int): The number of seconds to wait.
"""
kwargs.setdefault("comment", "pause for %s seconds" % seconds)
return Command("sleep %s" % seconds, **kwargs)
def write(path, content=None, **kwargs):
"""Write to a file.
- path (str): The file to be written.
- content (str): The content to be written. Note: If omitted, this command is equivalent to ``touch``.
"""
_content = content or ""
kwargs.setdefault("comment", "write to %s" % path)
a = list()
if len(_content.split("\n")) > 1:
a.append("cat > %s << EOF" % path)
a.append(_content)
a.append("EOF")
else:
a.append('echo "%s" > %s' % (_content, path))
return Command(" ".join(a), **kwargs)

@ -0,0 +1,39 @@
from .base import Command
def python_pip(name, op="install", upgrade=False, venv=None, version=3, **kwargs):
"""Use pip to install or uninstall a Python package.
- name (str): The name of the package.
- op (str): The operation to perform; install, uninstall
- upgrade (bool): Upgrade an installed package.
- venv (str): The name of the virtual environment to load.
- version (int): The Python version to use, e.g. ``2`` or ``3``.
"""
manager = "pip"
if version == 3:
manager = "pip3"
if upgrade:
statement = "%s install --upgrade %s" % (manager, name)
else:
statement = "%s %s %s" % (manager, op, name)
if venv is not None:
kwargs['prefix'] = "source %s/bin/activate" % venv
kwargs.setdefault("comment", "%s %s" % (op, name))
return Command(statement, **kwargs)
def python_virtualenv(name, **kwargs):
"""Create a Python virtual environment.
- name (str): The name of the environment to create.
"""
kwargs.setdefault("comment", "create %s virtual environment" % name)
return Command("virtualenv %s" % name, **kwargs)

@ -0,0 +1,337 @@
# Imports
from commonkit import split_csv
from .base import Command, Template
from .common import COMMON_MAPPINGS
from .django import DJANGO_MAPPINGS
from .mysql import MYSQL_MAPPINGS
from .pgsql import PGSQL_MAPPINGS
from .posix import POSIX_MAPPINGS
# Exports
__all__ = (
"MAPPINGS",
"apache",
"apache_disable_module",
"apache_disable_site",
"apache_enable_module",
"apache_enable_site",
"apache_reload",
"apache_restart",
"apache_start",
"apache_stop",
"apache_test",
"command_exists",
"service_reload",
"service_restart",
"service_start",
"service_stop",
"system",
"system_install",
"system_reboot",
"system_update",
"system_upgrade",
"system_uninstall",
"template",
"user",
)
def command_exists(name):
"""Indicates whether a given command exists in this overlay.
:param name: The name of the command.
:type name: str
:rtype: bool
"""
return name in MAPPINGS
def apache(op, **kwargs):
"""Execute an Apache-related command.
- op (str): The operation to perform; reload, restart, start, stop, test.
"""
if op == "reload":
return apache_reload(**kwargs)
elif op == "restart":
return apache_restart(**kwargs)
elif op == "start":
return apache_start(**kwargs)
elif op == "stop":
return apache_stop(**kwargs)
elif op == "test":
return apache_test(**kwargs)
else:
raise NameError("Unrecognized or unsupported apache operation: %s" % op)
def apache_disable_module(name, **kwargs):
"""Disable an Apache module.
- name (str): The module name.
"""
kwargs.setdefault("comment", "disable %s apache module" % name)
return Command("a2dismod %s" % name, **kwargs)
def apache_disable_site(name, **kwargs):
"""Disable an Apache site.
- name (str): The domain name.
"""
kwargs.setdefault("comment", "disable %s apache site" % name)
return Command("a2dissite %s" % name, **kwargs)
def apache_enable_module(name, **kwargs):
"""Enable an Apache module.
- name (str): The module name.
"""
kwargs.setdefault("comment", "enable %s apache module" % name)
return Command("a2enmod %s" % name, **kwargs)
def apache_enable_site(name, **kwargs):
"""Enable an Apache site.
"""
kwargs.setdefault("comment", "enable %s apache module" % name)
return Command("a2ensite %s" % name, **kwargs)
def apache_reload(**kwargs):
kwargs.setdefault("comment", "reload apache")
kwargs.setdefault("register", "apache_reloaded")
return Command("service apache2 reload", **kwargs)
def apache_restart(**kwargs):
kwargs.setdefault("comment", "restart apache")
kwargs.setdefault("register", "apache_restarted")
return Command("service apache2 restart", **kwargs)
def apache_start(**kwargs):
kwargs.setdefault("comment", "start apache")
kwargs.setdefault("register", "apache_started")
return Command("service apache2 start", **kwargs)
def apache_stop(**kwargs):
kwargs.setdefault("comment", "stop apache")
return Command("service apache2 stop", **kwargs)
def apache_test(**kwargs):
kwargs.setdefault("comment", "check apache configuration")
kwargs.setdefault("register", "apache_checks_out")
return Command("apachectl configtest", **kwargs)
def service_reload(name, **kwargs):
"""Reload a service.
- name (str): The service name.
"""
kwargs.setdefault("comment", "reload %s service" % name)
kwargs.setdefault("register", "%s_reloaded" % name)
return Command("service %s reload" % name, **kwargs)
def service_restart(name, **kwargs):
"""Restart a service.
- name (str): The service name.
"""
kwargs.setdefault("comment", "restart %s service" % name)
kwargs.setdefault("register", "%s_restarted" % name)
return Command("service %s restart" % name, **kwargs)
def service_start(name, **kwargs):
"""Start a service.
- name (str): The service name.
"""
kwargs.setdefault("comment", "start %s service" % name)
kwargs.setdefault("register", "%s_started" % name)
return Command("service %s start" % name, **kwargs)
def service_stop(name, **kwargs):
"""Stop a service.
- name (str): The service name.
"""
kwargs.setdefault("comment", "stop %s service" % name)
kwargs.setdefault("register", "%s_stopped" % name)
return Command("service %s stop" % name, **kwargs)
def system(op, **kwargs):
"""Perform a system operation.
- op (str): The operation to perform; reboot, update, upgrade.
"""
if op == "reboot":
return system_reboot(**kwargs)
elif op == "update":
return system_update(**kwargs)
elif op == "upgrade":
return system_upgrade(**kwargs)
else:
raise NameError("Unrecognized or unsupported system operation: %s" % op)
def system_install(name, **kwargs):
"""Install a system-level package.
- name (str): The name of the package to install.
"""
kwargs.setdefault("comment", "install system package %s" % name)
return Command("apt-get install -y %s" % name, **kwargs)
def system_reboot(**kwargs):
kwargs.setdefault("comment", "reboot the system")
return Command("reboot", **kwargs)
def system_uninstall(name, **kwargs):
"""Uninstall a system-level package.
- name (str): The name of the package to uninstall.
"""
kwargs.setdefault("comment", "remove system package %s" % name)
return Command("apt-get uninstall -y %s" % name, **kwargs)
def system_update(**kwargs):
kwargs.setdefault("comment", "update system package info")
return Command("apt-get update -y", **kwargs)
def system_upgrade(**kwargs):
kwargs.setdefault("comment", "upgrade the system")
return Command("apt-get upgrade -y", **kwargs)
def template(source, target, backup=True, parser=None, **kwargs):
"""Create a file from a template.
- source (str): The path to the template file.
- target (str): The path to where the new file should be created.
- backup (bool): Indicates whether a backup should be made if the target file already exists.
- parser (str): The parser to use ``jinja`` (the default) or ``simple``.
"""
return Template(source, target, backup=backup, parser=parser, **kwargs)
def user(name, groups=None, home=None, op="add", password=None, **kwargs):
"""Create or remove a user.
- name (str): The user name.
- groups (str | list): A list of groups to which the user should belong.
- home (str): The path to the user's home directory.
- op (str); The operation to perform; ``add`` or ``remove``.
- password (str): The user's password. (NOT IMPLEMENTED)
"""
if op == "add":
kwargs.setdefault("comment", "create a user named %s" % name)
commands = list()
# The gecos switch eliminates the prompts.
a = list()
a.append('adduser %s --disabled-password --gecos ""' % name)
if home is not None:
a.append("--home %s" % home)
commands.append(Command(" ".join(a), **kwargs))
if type(groups) is str:
groups = split_csv(groups, smart=False)
if type(groups) in [list, tuple]:
for group in groups:
commands.append(Command("adduser %s %s" % (name, group), **kwargs))
a = list()
for c in commands:
a.append(c.get_statement(include_comment=True))
return Command("\n".join(a), **kwargs)
elif op == "remove":
kwargs.setdefault("comment", "remove a user named %s" % name)
return Command("deluser %s" % name, **kwargs)
else:
raise NameError("Unsupported or unrecognized operation: %s" % op)
MAPPINGS = {
'apache': apache,
'apache.disable_module': apache_disable_module,
'apache.disable_site': apache_disable_site,
'apache.enable_module': apache_enable_module,
'apache.enable_site': apache_enable_site,
'apache.reload': apache_reload,
'apache.restart': apache_restart,
'apache.start': apache_start,
'apache.stop': apache_stop,
'apache.test': apache_test,
'install': system_install,
'reboot': system_reboot,
'reload': service_reload,
'restart': service_restart,
'start': service_start,
'stop': service_stop,
'system': system,
'template': template,
'update': system_update,
'uninstall': system_uninstall,
'upgrade': system_upgrade,
'user': user,
}
MAPPINGS.update(COMMON_MAPPINGS)
MAPPINGS.update(DJANGO_MAPPINGS)
MAPPINGS.update(MYSQL_MAPPINGS)
MAPPINGS.update(PGSQL_MAPPINGS)
MAPPINGS.update(POSIX_MAPPINGS)

@ -16,7 +16,7 @@ log = logging.getLogger(__name__)
__all__ = (
"filter_snippets",
# "filter_snippets",
"load_variables",
"BaseLoader",
"Snippet",
@ -26,32 +26,40 @@ __all__ = (
# Functions
def filter_snippets(snippets, environments=None, tags=None):
"""Filter snippets based on the given criteria.
:param snippets: The snippets to be filtered.
:type snippets: list[scripttease.lib.loaders.base.Snippet]
:param environments: Environment names to be matched.
:type environments: list[str]
:param tags: Tag names to be matched.
:type tags: list[str]
"""
filtered = list()
for snippet in snippets:
if environments is not None and len(snippet.environments) > 0:
if not any_list_item(environments, snippet.environments):
continue
if tags is not None:
if not any_list_item(tags, snippet.tags):
continue
filtered.append(snippet)
return filtered
# def filter_commands(commands, key, value):
# """Filter commands based on the given criteria.
#
# :param commands: The commands to be filtered.
# :type commands: list[scripttease.lib.commands.base.Command]
#
# :param key: The attribute name to be matched.
# :type key: str
#
# :param value: The value of the attribute.
#
# """
# filtered = list()
# for command in commands:
# try:
# values = getattr(command, key)
# except AttributeError:
# continue
#
# if not any_list_item(values, key):
# continue
#
# if not any_list_item()
# if environments is not None and len(snippet.environments) > 0:
# if not any_list_item(environments, snippet.environments):
# continue
#
# if tags is not None:
# if not any_list_item(tags, snippet.tags):
# continue
#
# filtered.append(snippet)
#
# return filtered
def load_variables(path, env=None):

@ -66,7 +66,7 @@ class INILoader(BaseLoader):
# continue
# Arguments surrounded by quotes are considered to be one argument. All others are split into a
# list to be passed to the callback. It is also possible that this is a call where no arguments are
# list to be passed to the parser. It is also possible that this is a call where no arguments are
# present, so the whole thing is wrapped to protect against an index error. A TypeError is raised in
# cases where a command is provided with no positional arguments; we interpret this as True.
try:

Loading…
Cancel
Save