142 lines
5.1 KiB
Python
142 lines
5.1 KiB
Python
"""Fabric server config management fabfile.
|
|
If you need additional configuration, setup ~/.fabricrc file:
|
|
|
|
user = your_remote_server_username
|
|
|
|
To get specific command help type:
|
|
fab -d command_name
|
|
|
|
"""
|
|
# From http://fueledbylemons.com/blog/2011/04/09/server-configs-and-fabric/
|
|
|
|
|
|
import os
|
|
|
|
from fabric.api import env, task
|
|
from fabric.utils import puts
|
|
from fabric import colors
|
|
import fabric.network
|
|
import fabric.state
|
|
|
|
|
|
YAML_AVAILABLE = True
|
|
try:
|
|
import yaml
|
|
except ImportError:
|
|
YAML_AVAILABLE = False
|
|
|
|
|
|
JSON_AVAILABLE = True
|
|
try:
|
|
import simplejson as json
|
|
except ImportError:
|
|
try:
|
|
import json
|
|
except ImportError:
|
|
JSON_AVAILABLE = False
|
|
|
|
################################
|
|
# ENVIRONMENTS #
|
|
################################
|
|
|
|
def _load_config(**kwargs):
|
|
"""Find and parse server config file.
|
|
|
|
If `config` keyword argument wasn't set look for default
|
|
'server_config.yaml' or 'server_config.json' file.
|
|
|
|
"""
|
|
config, ext = os.path.splitext(kwargs.get('config',
|
|
'server_config.yaml' if os.path.exists('server_config.yaml') else 'server_config.json'))
|
|
|
|
if not os.path.exists(config + ext):
|
|
print colors.red('Error. "%s" file not found.' % (config + ext))
|
|
return {}
|
|
if YAML_AVAILABLE and ext == '.yaml':
|
|
loader = yaml
|
|
elif JSON_AVAILABLE and ext =='.json':
|
|
loader = json
|
|
else:
|
|
print colors.red('Parser package not available')
|
|
return {}
|
|
# Open file and deserialize settings.
|
|
with open(config + ext) as config_file:
|
|
return loader.load(config_file)
|
|
|
|
@task
|
|
def servers(*args, **kwargs):
|
|
"""Set destination servers or server groups by comma delimited list of names"""
|
|
# Load config
|
|
servers = _load_config(**kwargs)
|
|
# If no arguments were recieved, print a message with a list of available configs.
|
|
if not args:
|
|
print 'No server name given. Available configs:'
|
|
for key in servers:
|
|
print colors.green('\t%s' % key)
|
|
|
|
# Create `group` - a dictionary, containing copies of configs for selected servers. Server hosts
|
|
# are used as dictionary keys, which allows us to connect current command destination host with
|
|
# the correct config. This is important, because somewhere along the way fabric messes up the
|
|
# hosts order, so simple list index incrementation won't suffice.
|
|
env.group = {}
|
|
# For each given server name
|
|
for name in args:
|
|
# Recursive function call to retrieve all server records. If `name` is a group(e.g. `all`)
|
|
# - get it's members, iterate through them and create `group`
|
|
# record. Else, get fields from `name` server record.
|
|
# If requested server is not in the settings dictionary output error message and list all
|
|
# available servers.
|
|
_build_group(name, servers)
|
|
|
|
|
|
# Copy server hosts from `env.group` keys - this gives us a complete list of unique hosts to
|
|
# operate on. No host is added twice, so we can safely add overlaping groups. Each added host is
|
|
# guaranteed to have a config record in `env.group`.
|
|
env.hosts = env.group.keys()
|
|
|
|
def _build_group(name, servers):
|
|
"""Recursively walk through servers dictionary and search for all server records."""
|
|
# We're going to reference server a lot, so we'd better store it.
|
|
server = servers.get(name, None)
|
|
# If `name` exists in servers dictionary we
|
|
if server:
|
|
# check whether it's a group by looking for `members`
|
|
if isinstance(server, list):
|
|
if fabric.state.output['debug']:
|
|
puts("%s is a group, getting members" % name)
|
|
for item in server:
|
|
# and call this function for each of them.
|
|
_build_group(item, servers)
|
|
# When, finally, we dig through to the standalone server records, we retrieve
|
|
# configs and store them in `env.group`
|
|
else:
|
|
if fabric.state.output['debug']:
|
|
puts("%s is a server, filling up env.group" % name)
|
|
env.group[server['host']] = server
|
|
else:
|
|
print colors.red('Error. "%s" config not found. Run `fab servers` to list all available configs' % name)
|
|
|
|
def reduce_env(task):
|
|
"""
|
|
Copies server config settings from `env.group` dictionary to env variable.
|
|
|
|
This way, tasks have easier access to server-specific variables:
|
|
`env.owner` instead of `env.group[env.host]['owner']`
|
|
|
|
"""
|
|
def task_with_setup(*args, **kwargs):
|
|
# If `s:server` was run before the current command - then we should copy values to
|
|
# `env`. Otherwise, hosts were passed through command line with `fab -H host1,host2
|
|
# command` and we skip.
|
|
if env.get("group", None):
|
|
for key,val in env.group[env.host].items():
|
|
setattr(env, key, val)
|
|
if fabric.state.output['debug']:
|
|
puts("[env] %s : %s" % (key, val))
|
|
|
|
task(*args, **kwargs)
|
|
# Don't keep host connections open, disconnect from each host after each task.
|
|
# Function will be available in fabric 1.0 release.
|
|
# fabric.network.disconnect_all()
|
|
return task_with_setup
|