Created
October 18, 2012 08:26
-
-
Save averrin/3910452 to your computer and use it in GitHub Desktop.
Nervarin.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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