Skip to content

Instantly share code, notes, and snippets.

@averrin
Created October 18, 2012 08:26
Show Gist options
  • Select an option

  • Save averrin/3910452 to your computer and use it in GitHub Desktop.

Select an option

Save averrin/3910452 to your computer and use it in GitHub Desktop.
Nervarin.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#MODULES
try:
from functools import partial
from fabric.api import run, env, open_shell, put, sudo, get, prompt, puts
from fabric.colors import red
from fabric.decorators import task
from fabric.tasks import execute
import os
import shutil
import json
except ImportError:
print 'Plz install deps:'
print '>\tsudo pip install fabric bottle boto'
exit(1)
# CONFIG
PORT = 8080
serve_key = 'aqwersdf'
ssh_startup = 'tmux new-session -t default || tmux new-session -s default'
packages = ['tmux', 'zsh', 'curl', 'w3m', 'autojump', 'vim']
pm_args = {'apt-get': '-ym --force-yes', 'yum': '-y'}
central_server = '[email protected]:22'
new_server = {
"description": "",
"tags": [],
"ssh_port": 22,
"ip": "",
"host": "",
"groups": ["ssh"],
"projects": [],
"ftp_port": 21,
"other_hosts": [],
"ssh_user": "averrin",
"ssh_password": "",
"ssh_cert": "",
"os": "linux"
}
TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<title>Nervarin</title>
<!-- Bootstrap -->
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/css/bootstrap-combined.min.css" rel="stylesheet">
<!-- <link rel="stylesheet" href="http://averr.in/static/gen/bp_packed.css?1311598113">
<link rel="stylesheet" href="http://averr.in/static/gen/packed.css?1304342019"> -->
</head>
<body>
<div class="row-fluid" style="padding-top: 6px;">
{% for name,s in servers %}
<div class="well span4" style="height: 300px; margin-left: 6px; overflow-y: auto;">
<h4>{{s.alias}} <small>{% if s.host %}{{s.host}}{% else %}{{s.ip}}{% endif %}</small></h4>
<strong>IP:</strong> {{s.ip}} <br />
<strong>OS:</strong> {{s.os}} <br />
{% if s.os=="linux" %}<strong>SSH:</strong> ssh {{s.ssh_user}}{% if s.ssh_password %}:{{s.ssh_password}}{% endif %}@{% if s.host %}{{s.host}}{% else %}{{s.ip}}{% endif %} -p {{s.ssh_port}} <br />{% endif %}
{% if s.os=="linux" %}<strong>FTP port:</strong> {{s.ftp_port}} <br />{% endif %}
{% if s.other_hosts %}<strong>other_hosts:</strong> {{s.other_hosts|join(', ')}} <br />{% endif %}
<strong>Tags:</strong> {% for tag in s.tags %}<span class="label label-success">{{tag}}</span> {% endfor %} <br />
{% if s.attrs %}
<strong>Attributes:</strong>
<ul>
{% for k,v in s.attrs.iteritems() %}
<li><strong>{{k}}:</strong> {{v}}</li>
{% endfor %}
</ul>
{% endif %}
{% if s.groups %}
<strong>Groups:</strong>
{% for g in s.groups %}
<span class="label label-info">{{g}}</span>
{% endfor %}
</ul>
{% endif %}
</div>
{% endfor %}
</div>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.1.1/js/bootstrap.min.js"></script>
</body>
</html>
"""
tmux_conf = """
set-option -g default-shell "{{shell}}"
unbind C-b
unbind l
set -g prefix C-a
bind-key C-a last-window
set-option -g mouse-select-pane on
unbind %
bind | split-window -h
bind - split-window -v
bind % killp
bind a displayp \; lsp
bind h neww htop
bind r source-file ~/.tmux.conf
set -g default-terminal "screen-256color"
set -g history-limit 1000
set-window-option -g mode-keys vi # vi key
set-option -g status-keys vi
set-window-option -g utf8 on
set-window-option -g mode-mouse off
set-option -g base-index 1
set-option -g status-utf8 on
set-option -g status-justify right
set-option -g status-bg black
set-option -g status-fg white
set-option -g status-interval 5
set-option -g status-left-length 30
set-option -g status-left '#[fg=red,bold]» #[fg=blue,bold]#T#[default]'
set-option -g status-right '#[fg=white,bold]»» #[fg=blue,bold]###S #[fg=red]%R %d.%m#(acpi | cut -d ',' -f 2)#[default]'
set-option -g visual-activity on
set-window-option -g monitor-activity on
set-window-option -g window-status-current-fg white
set-window-option -g window-status-current-bg default
set-window-option -g window-status-current-attr bold
set-window-option -g clock-mode-colour cyan
set-window-option -g clock-mode-style 24
set-window-option -g window-status-fg white
set-window-option -g window-status-attr dim
set -g default-terminal screen-256color
"""
zshrc = """
ZSH=$HOME/.oh-my-zsh
ZSH_THEME="sorin"
plugins=(git)
source $ZSH/oh-my-zsh.sh
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
#source /usr/share/autojump/autojump.sh
PS1="(%{$fg[cyan]%}{{server}}%{$reset_color%}) $PS1"
"""
# ENDCONFIG
certs = {'aws_ssh_averrin': """-----BEGIN RSA PRIVATE KEY-----
-----END RSA PRIVATE KEY-----""",
'aws_key': """-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----""",
'aws_cert': """-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----"""
}
# SERVERS LOGIC
@task
def backup_json():
put('servers.json', '.')
@task
def update_json():
get('servers.json', '.')
class ServerList(dict):
def __init__(self):
self.load()
self.certs = certs
self.by_tags = partial(self.by_attrs, 'tags')
self.by_projects = partial(self.by_attrs, 'projects')
self.by_hosts = partial(self.by_attrs, 'other_hosts')
def by_attrs(self, key, *values):
res = []
for s in self.values():
if len(set(values).intersection(s[key])) == len(values):
res.append(s)
return res
def by_attr(self, key, value):
res = []
for s in self.values():
if s[key] == value:
res.append(s)
return res
@classmethod
def getCert(cls, key):
return cls.certs[key]
def dump(self):
with file('servers.json', 'w') as f:
f.write(json.dumps(self, indent=4))
def load(self):
if os.path.isfile('servers.json'):
with file('servers.json', 'r') as f:
self.update(json.loads(f.read()))
else:
execute(update_json, hosts=[central_server])
self.load()
# ETC
def dump_to_file(filename, text, mod=777):
with file('.temp/' + filename, 'w') as f:
f.write(text)
os.system('chmod %s %s' % (mod, '.temp/' + filename))
return '.temp/' + filename
@task
def clean():
"""
Clean temp files
"""
os.system('chmod 777 -R .temp')
if os.path.isdir('.temp'):
shutil.rmtree('.temp')
def current():
return SERVERS.by_attr('ip', env['host_string'].split('@')[1].split(':')[0])[0]
env.key_filename = []
clean()
os.mkdir('.temp')
for cert in certs:
env.key_filename.append(dump_to_file(cert, certs[cert], 400))
SERVERS = ServerList()
for s in SERVERS:
SERVERS[s]['alias'] = s
env.roledefs['linux'] = []
for s in SERVERS.by_attr('os', 'linux'):
env.passwords['%(ssh_user)s@%(ip)s:%(ssh_port)s' % s] = s['ssh_password']
s['host_string'] = '%(ssh_user)s@%(ip)s:%(ssh_port)s' % s
env.passwords['%(ssh_user)s@%(host)s:%(ssh_port)s' % s] = s['ssh_password']
for h in s['other_hosts']:
env.passwords['%s@%s:%s' % (s['ssh_user'], h, s['ssh_port'])] = s['ssh_password']
env.roledefs[s['alias']] = ['%(ssh_user)s@%(ip)s:%(ssh_port)s' % s]
for s in SERVERS.values():
for g in s['groups']:
if not g in env.roledefs:
env.roledefs[g] = []
if 'ssh_user' in s:
env.roledefs[g].append('%(ssh_user)s@%(ip)s:%(ssh_port)s' % s)
else:
env.roledefs[g].append(s['ip'])
# SERVE LOGIC
try:
from bottle import route, request, HTTPError
from bottle import run as server_run
from jinja2 import Template
@route('/')
def dump():
if not serve_key in request.GET:
raise HTTPError(404, "These are not the droids you're looking for")
return Template(TEMPLATE).render(servers=SERVERS.iteritems(), certs=SERVERS.certs)
@task
def serve():
"""
Run web-server for html version of servers list
"""
server_run(host='0.0.0.0', port=PORT, reloader=True)
except:
print red('Serve not supported. Need Bottle and Jinja2')
# FABRIC LOGIC
@task
def shell(native=False, tmux=True):
"""
Open common ssh shell
"""
if native or eval(str(native)):
open_shell(ssh_startup if eval(str(tmux)) else '')
else:
key = current()['ssh_cert']
password = SERVERS.by_attr('ip', env['host_string'].split('@')[1].split(':')[0])[0]['ssh_password']
if key:
key = '-i .temp/' + key
ssh = "sshpass -p '%s' ssh %s -p %s %s -t '%s'" % (password,
env['host_string'].split(':')[0],
env['host_string'].split(':')[1],
key,
ssh_startup if eval(str(tmux)) else '')
os.system(ssh)
print ssh
clean()
@task
def send_file(local, remote):
"""
Send file by scp
"""
put(local, remote)
# clean()
@task
def get_file(remote, local):
"""
Send file by scp
"""
get(remote, local)
# clean()
@task
def init(pm='apt-get'):
"""
Install must-have tools like tmux, zsh and others
"""
env.warn_only = True
s = current()
if not 'pm' in s:
s['pm'] = pm
for p in packages:
sudo('%s install %s %s' % (s['pm'], p, pm_args[s['pm']]))
run('curl -L https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh | sh')
env.warn_only = False
path = run('which zsh')
put(dump_to_file('.tmux.conf', tmux_conf.replace('{{shell}}', path)), '.')
put(dump_to_file('.zshrc', zshrc.replace('{{server}}', current()['alias'])), '.')
# clean()
@task
def get_info():
from pprint import pprint
pprint(current())
run('lsb_release -a', shell=False)
run('uname -a', shell=False)
# clean()
@task
def full_backup():
backup_json()
put(env['fabfile'], '.')
@task
def test():
print env
if __name__ == "__main__":
os.system('ipython --quick -c "import nervarin as n" -i')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment