def test_flag_is_not_set_when_watched_file_is_not_modified(watched_file): from burlap.files import watch with watch('watched') as f: pass assert not f.changed
def test_flag_is_set_when_watched_file_is_modified(watched_file): from burlap.files import watch from burlap.require import file as require_file with watch('watched') as f: require_file('watched', contents='bbb') assert f.changed
def test_callback_is_not_called_when_watched_file_is_not_modified(watched_file): from burlap.files import watch from burlap.require import file as require_file try: with watch('watched', callback=partial(require_file, 'modified2')): pass assert not is_file('modified2') finally: run('rm -f modified2')
def test_callback_is_called_when_watched_file_is_modified(watched_file): from burlap.files import watch from burlap.require import file as require_file try: with watch('watched', callback=partial(require_file, 'modified1')): require_file('watched', contents='bbb') assert is_file('modified1') finally: run('rm -f modified1')
def test_callback_is_not_called_when_watched_file_is_not_modified( watched_file): from burlap.files import watch from burlap.require import file as require_file try: with watch('watched', callback=partial(require_file, 'modified2')): pass assert not is_file('modified2') finally: run('rm -f modified2')
def firewall(zones=None, interfaces=None, policy=None, rules=None, routestopped=None, masq=None): """ Ensure that a firewall is configured. Example:: from burlap.shorewall import * from burlap import require # We need a firewall with some custom rules require.shorewall.firewall( rules=[ Ping(), SSH(), HTTP(), HTTPS(), SMTP(), rule(port=1234, source=hosts(['example.com'])), ] ) """ family = distrib_family() if family != 'debian': raise UnsupportedFamily(supported=['debian']) require_deb_package('shorewall') with watch(CONFIG_FILES) as config: _zone_config(zones) _interfaces_config(interfaces) _policy_config(policy) _rules_config(rules) _routestopped_config(routestopped) _masq_config(masq) if config.changed: puts("Shorewall configuration changed") if is_started(): restart('shorewall') with settings(hide('running'), shell_env()): sed('/etc/default/shorewall', 'startup=0', 'startup=1', use_sudo=True)
def _locales_generic(names, config_file, command): supported = supported_locales() _check_for_unsupported_locales(names, supported) # Regenerate locales if config file changes with watch(config_file, use_sudo=True) as config: # Add valid locale names to the config file charset_from_name = dict(supported) for name in names: charset = charset_from_name[name] locale = "%s %s" % (name, charset) uncomment(config_file, escape(locale), use_sudo=True, shell=True) append(config_file, locale, use_sudo=True, partial=True, shell=True) if config.changed: run_as_root(command)
def sysctl(key, value, persist=True): """ Require a kernel parameter to have a specific value. """ if get_sysctl(key) != value: set_sysctl(key, value) if persist: from burlap.require import file as require_file filename = '/etc/sysctl.d/60-%s.conf' % key with watch(filename, use_sudo=True) as config: require_file(filename, contents='%(key)s = %(value)s\n' % locals(), use_sudo=True) if config.changed: if distrib_family() == 'debian': with settings(warn_only=True): run_as_root('service procps start')
def _update_ssh_setting(sshd_config, name, value): """ Update a yes/no setting in the SSH config file """ with watch(sshd_config) as config_file: with shell_env(): # First try to change existing setting sed(sshd_config, r'^(\s*#\s*)?%s\s+(yes|no)' % name, '%s %s' % (name, value), use_sudo=True) # Then append setting if it's still missing _append(sshd_config, '%s %s' % (name, value), use_sudo=True) if config_file.changed and is_running('ssh'): restart('ssh')
def source(name, uri, distribution, *components): """ Require a package source. :: from burlap import require # Official MongoDB packages require.deb.source('mongodb', 'http://downloads-distro.mongodb.org/repo/ubuntu-upstart', 'dist', '10gen') """ from burlap.require import file as require_file path = '/etc/apt/sources.list.d/%(name)s.list' % locals() components = ' '.join(components) source_line = 'deb %(uri)s %(distribution)s %(components)s\n' % locals() with watch(path) as config: require_file(path=path, contents=source_line, use_sudo=True) if config.changed: puts('Added APT repository: %s' % source_line) update_index()
def process(name, **kwargs): """ Require a supervisor process to be running. Keyword arguments will be used to build the program configuration file. Some useful arguments are: - ``command``: complete command including arguments (**required**) - ``directory``: absolute path to the working directory - ``user``: run the process as this user - ``stdout_logfile``: absolute path to the log file You should refer to the `supervisor documentation`_ for the complete list of allowed arguments. .. note:: the default values for the following arguments differs from the ``supervisor`` defaults: - ``autorestart``: defaults to ``true`` - ``redirect_stderr``: defaults to ``true`` Example:: from burlap import require require.supervisor.process('myapp', command='/path/to/venv/bin/myapp --config production.ini --someflag', directory='/path/to/working/dir', user='******', stdout_logfile='/path/to/logs/myapp.log', ) .. _supervisor documentation: http://supervisord.org/configuration.html#program-x-section-values """ from burlap.require import file as require_file from burlap.require.deb import package as require_deb_package from burlap.require.rpm import package as require_rpm_package from burlap.require.arch import package as require_arch_package from burlap.require.service import started as require_started family = distrib_family() if family == 'debian': require_deb_package('supervisor') require_started('supervisor') elif family == 'redhat': require_rpm_package('supervisord') require_started('supervisord') elif family == 'arch': require_arch_package('supervisor') require_started('supervisord') else: raise UnsupportedFamily(supported=['debian', 'redhat', 'arch']) # Set default parameters params = {} params.update(kwargs) params.setdefault('autorestart', 'true') params.setdefault('redirect_stderr', 'true') # Build config file from parameters lines = [] lines.append('[program:%(name)s]' % locals()) for key, value in sorted(params.items()): lines.append("%s=%s" % (key, value)) # Upload config file if family == 'debian': filename = '/etc/supervisor/conf.d/%(name)s.conf' % locals() elif family == 'redhat': filename = '/etc/supervisord.d/%(name)s.ini' % locals() elif family == 'arch': filename = '/etc/supervisor.d/%(name)s.ini' % locals() with watch(filename, callback=update_config, use_sudo=True): require_file(filename, contents='\n'.join(lines), use_sudo=True) # Start the process if needed if process_status(name) == 'STOPPED': start_process(name)
def instance(name, version=VERSION, bind='127.0.0.1', port=6379, **kwargs): """ Require a Redis instance to be running. The required Redis version will be automatically installed using :py:func:`burlap.require.redis.installed_from_source` if needed. You can specify the IP address and port on which to listen to using the *bind* and *port* parameters. .. warning:: Redis is designed to be accessed by trusted clients inside trusted environments. It is usually not a good idea to expose the Redis instance directly to the internet. Therefore, with the default settings, the Redis instance will only listen to local clients. If you want to make your Redis instance accessible to other servers over an untrusted network, you should probably add some firewall rules to restrict access. For example: :: from burlap import require from burlap.shorewall import Ping, SSH, hosts, rule # The computers that will need to talk to the Redis server REDIS_CLIENTS = [ 'web1.example.com', 'web2.example.com', ] # The Redis server port REDIS_PORT = 6379 # Setup a basic firewall require.shorewall.firewall( rules=[ Ping(), SSH(), rule(port=REDIS_PORT, source=hosts(REDIS_CLIENTS)), ] ) # Make the Redis instance listen on all interfaces require.redis.instance('mydb', bind='0.0.0.0', port=REDIS_PORT) .. seealso:: `Redis Security <http://redis.io/topics/security>`_ You can also use any valid Redis configuration directives as extra keyword arguments. For directives that can be repeated on multiple lines (such as ``save``), you can supply a list of values. The instance will be managed using supervisord, as a process named ``redis_{name}``, running as the ``redis`` user. :: from burlap import require from burlap.supervisor import process_status require.redis.instance('mydb') print(process_status('redis_mydb')) .. seealso:: :ref:`supervisor_module` and :ref:`require_supervisor_module` The default settings enable persistence using periodic RDB snapshots saved in the `/var/db/redis` directory. You may want to use AOF persistence instead: :: require.redis.instance('mydb', appendonly='yes', save=[]) In certain situations, you may want to disable persistence completely: :: require.redis.instance('cache', port=6380, save=[]) .. seealso:: `Redis Persistence <http://redis.io/topics/persistence>`_ """ from burlap.require import directory as require_directory from burlap.require import file as require_file from burlap.require.supervisor import process as require_process from burlap.require.system import sysctl as require_sysctl installed_from_source(version) require_directory('/etc/redis', use_sudo=True, owner='redis') require_directory('/var/db/redis', use_sudo=True, owner='redis') require_directory('/var/log/redis', use_sudo=True, owner='redis') # Required for background saving with settings(warn_only=True): require_sysctl('vm.overcommit_memory', '1') # Set default parameters params = {} params.update(kwargs) params.setdefault('bind', bind) params.setdefault('port', port) params.setdefault('logfile', '/var/log/redis/redis-%(name)s.log' % locals()) params.setdefault('loglevel', 'verbose') params.setdefault('dir', '/var/db/redis') params.setdefault('dbfilename', 'redis-%(name)s-dump.rdb' % locals()) params.setdefault('save', ['900 1', '300 10', '60 10000']) # Build config file from parameters # (keys such as 'save' may result in multiple config lines) lines = [] for key, value in sorted(params.items()): if isinstance(value, list): for elem in value: lines.append("%s %s" % (key, elem)) else: lines.append("%s %s" % (key, value)) redis_server = '/opt/redis-%(version)s/redis-server' % locals() config_filename = '/etc/redis/%(name)s.conf' % locals() # Upload config file with watch(config_filename, use_sudo=True) as config: require_file(config_filename, contents='\n'.join(lines), use_sudo=True, owner='redis') # Use supervisord to manage process process_name = 'redis_%s' % name require_process( process_name, user='******', directory='/var/run', command="%(redis_server)s %(config_filename)s" % locals(), ) # Restart if needed if config.changed: burlap.supervisor.restart_process(process_name)