Friday, December 12, 2014

Using Git With Multiple SSH Keys and Accounts

Generating SSH keys for GitHub

Here are github account and work account.

  • GitHub:
    SSH Key: github_id_rsa
    Account: oopsmonk

  • Work:
    SSH Key: work_id_rsa
    Account: SamChen

Add SSH config File

Modify ~/.ssh/config

# Default github
Host github.com
    HostName github.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/github_id_rsa

# Work git server
Host work.gitserver.com
    HostName work.gitserver.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/work_id_rsa

Git Repository Configuation

  • GitHub Project:

    $ git clone https://github.com/abc/projectA.git  
    $ cd projectA
    #github account
    $ git config user.name "oopsmonk"
    $ git config user.email "oopsmonk@example.com.tw"
  • Working Project:

    $ git clone https://work.com.tw/repo/projectW.git  
    $ cd projectW
    #working account
    $ git config user.name "SamChen"
    $ git config user.email "SamChen@example.com.tw"

Now you can deal with git repositories using different accounts.

(Additional) Using ssh-agent to access GitHub without password

Check key list

# No ssh-agent running 
$ ssh-add -l
Could not open a connection to your authentication agent.
$  

Enable ssh-agent (Ubuntu12.04):

#enable ssh-agent
$ eval `ssh-agent -s`

#add keys to agent
$ cd ~/.ssh
$ ssh-add work_id_rsa github_id_rsa
Identity added: work_id_rsa (work_id_rsa)
Identity added: github_id_rsa (github_id_rsa)

#list added keys
$ ssh-add -L

#delete keys in agent  
$ ssh-add -D

Now you can commit modifications to git repository without password.

Reference:
Understanding ssh-agent and ssh-add
How can I run ssh-add automatically, without password prompt?

Wednesday, December 10, 2014

pyenv Quick Start (Utunbu 14.04)

installation

Requirements

$ sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \
libreadline-dev libsqlite3-dev wget curl llvm

install pyenv

$ cd ~
$ git clone git://github.com/yyuu/pyenv.git .pyenv
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(pyenv init -)"' >> ~/.bashrc

install pyenv-virtualenv

$ git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv 
$ echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc
$ exec $SHELL

Common Usage (Command Reference)

List all available versions

$ pyenv version -l  

Install python2.7.8 to pyenv

$ pyenv install 2.7.8  

List installed versions

$ pyenv versions
* system (set by /home/oopsmonk/.pyenv/version)
  2.7.8
  3.4.2

Creating a virtualenv

$ pyenv virtualenv 2.7.8 mypy-2.7.8

List virtualenvs

$ pyenv virtualenvs
  mypy-2.7.8 (created from /home/oopsmonk/.pyenv/versions/2.7.8)
  mypy-3.4.2 (created from /home/oopsmonk/.pyenv/versions/3.4.2)

# current versions 
$ pyenv versions
  * system (set by /home/oopsmonk/.pyenv/version)
  2.7.8
  3.4.2
  mypy-2.7.8
  mypy-3.4.2

Use python via virtualenv

# show current version
oopsmonk@VBox:~/markdown-note$ python --version
Python 2.7.6

# Change to other version
oopsmonk@VBox:~/markdown-note$ pyenv activate mypy-2.7.8
(mypy-2.7.8)oopsmonk@VBox:~/markdown-note$ python --version
Python 2.7.8

# Deactivate
(mypy-2.7.8)oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv deactivate
oopsmonk@oopsmonk-VBox:~/markdown-note$  python --version
Python 2.7.8
# Why current version is 2.7.8? it's supposed to 2.7.6.

oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv activate mypy-3.4.2
(mypy-3.4.2) oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv deactivate
oopsmonk@oopsmonk-VBox:~/markdown-note$ python --version
Python 3.4.2

oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv versions
  system
  2.7.8
  3.4.2
  mypy-2.7.8
* mypy-3.4.2 (set by PYENV_VERSION environment variable)

# delete a virtualenv   
$ pyenv uninstall mypy-2.7.8

# Go back to original system version  
$ alias pyenv_deactivate='pyenv deactivate && unset PYENV_VERSION'
oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv activate mypy-3.4.2
(mypy-3.4.2) oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv_deactivate
oopsmonk@oopsmonk-VBox:~/markdown-note$ python --version
Python 2.7.6

Use python via pyenv

# Global python version
$ pyenv global 

# python version in current folder
oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv local mypy-3.4.2
oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv version
mypy-3.4.2 (set by /home/oopsmonk/markdown-note/.python-version)
oopsmonk@oopsmonk-VBox:~/markdown-note$ cd ..
oopsmonk@oopsmonk-VBox:~$ pyenv version
system (set by /home/oopsmonk/.pyenv/version)
oopsmonk@oopsmonk-VBox:~$ cd -
/home/oopsmonk/markdown-note
oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv version
mypy-3.4.2 (set by /home/oopsmonk/markdown-note/.python-version)
oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv local --unset
oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv version
system (set by /home/oopsmonk/.pyenv/version)

# python version in current shell 
oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv shell mypy-3.4.2
oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv version
mypy-3.4.2 (set by PYENV_VERSION environment variable)
oopsmonk@oopsmonk-VBox:~/markdown-note$ cd -
/home/oopsmonk
oopsmonk@oopsmonk-VBox:~$ pyenv version
mypy-3.4.2 (set by PYENV_VERSION environment variable)
oopsmonk@oopsmonk-VBox:~$ pyenv shell --unset
oopsmonk@oopsmonk-VBox:~$ pyenv version
system (set by /home/oopsmonk/.pyenv/version)
oopsmonk@oopsmonk-VBox:~$ cd -
/home/oopsmonk/markdown-note
oopsmonk@oopsmonk-VBox:~/markdown-note$ pyenv version
system (set by /home/oopsmonk/.pyenv/version)

Monday, July 21, 2014

Raspberry Pi Monitor

Use RRDTool monitor Raspberry Pi, include CPU temperture, Memory usage, Disk I/O, Network I/O...

Install

install packages

sudo apt-get install libcairo2-dev libpango1.0-dev libglib2.0-dev libxml2-dev librrd-dev python2.7-dev rrdtool python-rrdtool
wget https://pypi.python.org/packages/source/p/psutil/psutil-2.1.1.tar.gz
tar xf psutil-2.1.1.tar.gz
cd psutil-2.1.1
sudo python setup.py install

Download or clone rpi-monitor on github
https://github.com/oopsmonk/rpi-monitor

Setup Crontab

By defualt, the cron.log is disabled in Raspbian. To enable it:

sudo vi /etc/rsyslog.conf

find the line and uncomment it.

# cron.*                          /var/log/cron.log

Restart rsyslog via:

sudo /etc/init.d/rsyslog restart

Modify crontab

crontab -e

Add schedule as below

#data collection every 5 minutes
*/5 * * * * /path/to/rpi-monitor/rpi_monitor.py
#generate daily graph report at 00:01
1 0 * * * /path/to/rpi-monitor/graphReport.py -1d
#generate weekly graph report at 00:03 on Monday
3 0 * * 1 /path/to/rpi-monitor/graphReport.py -1w  

Report example

Raspberry Pi Hardware

CPU Temperture

The temperture drop to 44 because I add a fan on CPU.

CPU Used Percentage

PID Count

Memory Usage

Mount point Usage

Mount Point Percentage

HDD I/O

eth0 I/O

eth1 I/O

Wednesday, August 7, 2013

Web dev example : JSON & jQuery Mobile & Bottle.

Bottle is a fast, simple and lightweight WSGI micro web-framework for Python.
jQuery Mobile base on jQuery for mobile device.
jQuery vs. jQuery Mobile vs. jQuery UI

Install bottle:

$ sudo apt-get install python-setuptools
$ easy_install bottle

Demo server deployment

file structure:

BottlejQuery
├── bottleJQuery.py
└── index.html

run command:

$ ./bottleJQuery.py

connect to server:

http://localhost:8080/bottle  

Building simple web server use bottle

bottleJQuery.py

#!/usr/bin/env python
from bottle import route, static_file, debug, run, get, redirect
from bottle import post, request
import os, inspect, json

#enable bottle debug
debug(True)

# WebApp route path
routePath = '/bottle'
# get directory of WebApp (bottleJQuery.py's dir)
rootPath = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))

@route(routePath)
def rootHome():
    return redirect(routePath+'/index.html')

@route(routePath + '/<filename:re:.*\.html>')
def html_file(filename):
    return static_file(filename, root=rootPath)

@get(routePath + '/jsontest')
def testJsonGET():
    print "GET Header : \n %s" % dict(request.headers) #for debug header
    return {"id":2,"name":"Jone"}

@post(routePath + '/jsontest')
def testJsonPost():
    print "POST Header : \n %s" % dict(request.headers) #for debug header
    data = request.json
    print "data : %s" % data 
    if data == None:
        return json.dumps({'Status':"Failed!"})
    else:
        return json.dumps({'Status':"Success!"})

run(host='localhost', port=8080, reloader=True)

Test GET request:

$ curl -i -X GET http://localhost:8080/bottle/jsontest

HTTP/1.0 200 OK
Date: Wed, 07 Aug 2013 16:03:53 GMT
Server: WSGIServer/0.1 Python/2.7.3
Content-Length: 25
Content-Type: application/json

{"id": 2, "name": "Jone"}

bottle debug message:

GET Header :
 {'Host': 'localhost:8080', 'Content-Type': 'text/plain', 'Content-Length': '', 'Accept': '*/*', 'User-Agent': 'curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3'}
localhost - - [08/Aug/2013 00:03:53] "GET /bottle/jsontest HTTP/1.1" 200 25

Test POST request:

$ curl -X POST -H "Content-Type: application/json" -d '{"name":"OopsMonk","pwd":"abc"}' http://localhost:8080/bottle/jsontest
{"Status": "Success!"}

bottle debug message:

POST Header :
 {'Host': 'localhost:8080', 'Content-Type': 'application/json', 'Content-Length': '31', 'Accept': '*/*', 'User-Agent': 'curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3'}
data : {u'pwd': u'abc', u'name': u'OopsMonk'}
localhost - - [08/Aug/2013 00:04:56] "POST /bottle/jsontest HTTP/1.1" 200 22

Building simple web page

Create 4 buttons in the index.html file:

  • jQuery.getJSON : use jQuery.getJSON API.
    bottle debug message:

    GET Header :
     {'Content-Length': '', 'Accept-Language': 'zh-tw,zh;q=0.8,en-us;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'Host': '127.0.0.1:8080', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0', 'Connection': 'close', 'X-Requested-With': 'XMLHttpRequest', 'Referer': 'http://192.168.1.38/bottle/index.html', 'Content-Type': 'text/plain'}  
    localhost - - [08/Aug/2013 00:42:08] "GET /bottle/jsontest HTTP/1.0" 200 25
  • jQuery.post : use jQuery.post API.
    Unfortunately it's not work, because the content-Type is fixed 'application/x-www-form-urlencoded; charset=UTF-8', that's a problem when using bottle.request.json API to retrieve JSON from request.
    For this perpose we have to overwirte jQuery.post or use jQuery.ajax.
    bottle debug message:

    POST Header :
     {'Content-Length': '22', 'Accept-Language': 'zh-tw,zh;q=0.8,en-us;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'Host': '127.0.0.1:8080', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0', 'Connection': 'close', 'X-Requested-With': 'XMLHttpRequest', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Referer': 'http://192.168.1.38/bottle/index.html', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
    data : None
    localhost - - [08/Aug/2013 00:55:17] "POST /bottle/jsontest HTTP/1.0" 200 21
  • jQuery.ajax GET, jQuery.ajax POST : use jQuery.ajax API.
    GET debug message:

    GET Header :
     {'Content-Length': '', 'Accept-Language': 'zh-tw,zh;q=0.8,en-us;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'Host': '127.0.0.1:8080', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0', 'Connection': 'close', 'X-Requested-With': 'XMLHttpRequest', 'Referer': 'http://192.168.1.38/bottle/index.html', 'Content-Type': 'text/plain'}
    localhost - - [08/Aug/2013 01:00:17] "GET /bottle/jsontest HTTP/1.0" 200 25

    POST debug message:

    POST Header :
     {'Content-Length': '22', 'Accept-Language': 'zh-tw,zh;q=0.8,en-us;q=0.5,en;q=0.3', 'Accept-Encoding': 'gzip, deflate', 'Host': '127.0.0.1:8080', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0', 'Connection': 'close', 'X-Requested-With': 'XMLHttpRequest', 'Pragma': 'no-cache', 'Cache-Control': 'no-cache', 'Referer': 'http://192.168.1.38/bottle/index.html', 'Content-Type': 'application/json; charset=utf-8'}
    data : {u'id': 3, u'name': u'Ping'}
    localhost - - [08/Aug/2013 01:01:40] "POST /bottle/jsontest HTTP/1.0" 200 22

index.html

<!DOCTYPE html>
<html>
<head>
<title>Bottle & jQuery Mobile</title>

<link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js"></script>

</head>

<body>
<h3>This is a example using jQuery Mobile and pyhton bottle fromwork. </h3>
<div data-role="controlgroup" data-type="horizontal">
    <a href="#" id="btnGetJSON" data-role="button">jQuery.getJSON</a>
    <a href="#" id="btnPOSTJSON" data-role="button">jQuery.post</a>
    <a href="#" id="btnAJAXGet" data-role="button">jQuery.ajax GET</a>
    <a href="#" id="btnAJAXPOST" data-role="button">jQuery.ajax POST</a>
</div>

<script>
    //button action 
    $("#btnGetJSON").click(function(){
        $.getJSON("jsontest", function(data){
            $.each(data, function(index, value){
                alert("index: " + index + " , value: "+ value);
            });
        });
    });

    $("#btnPOSTJSON").click(function(){
        alert("Not easy work on bottle !!!");
        /*
        It's not work, send JSON via jQuery.post().
        because the content-Type is not 'application/json',
        The content type is fixed :
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
        that's a problem on bottle.request.json.
        */

        $.post(
        "jsontest", 
        JSON.stringify({"id":3, "name":"Ping"}), 
        function(ret_data, st){
            $.each(ret_data, function(index, value){
                alert("index: " + index + " , value: "+ value);
            });
            alert("Server return status : " + st); 
        },
        'json'
        );
    });

    $("#btnAJAXGet").click(function(){
        $.ajax
        ({
            url: 'jsontest',
            success: function(data){
                $.each(data, function(index, value){
                    alert("index: " + index + " , value: "+ value);
                });
            },
            /*
            if dataType not set, the Accept in request header is:
            'Accept': '* / *'
            dataType = json :
            'Accept': 'application/json, text/javascript, * /*; q=0.01'
            */
            dataType: 'json'
        });
    });

    $("#btnAJAXPOST").click(function(){
        var post_data = {"id":3, "name":"Ping"};
        $.ajax
        ({
            type: 'POST',
            url: 'jsontest',
            data:JSON.stringify(post_data),
            contentType: "application/json; charset=utf-8",
            dataType: 'json',
            success: function(data){
                $.each(data, function(index, value){
                    alert("index: " + index + " , value: "+ value);
                });
            }
        });
    });
</script>
</body>
</html>

Wednesday, July 31, 2013

Sending HTML Mail Using SMTP With Authorization

Here is a text/plain MIME type parts in official exmaple code.
I remove it from my sample code, because it's not show up in mail at Office Outlook 2010.

#!/usr/bin/env python
"""
File name: sendMail.py
Python send HTML mail using SMTP with authorization

Usage :
./sendMail.py to@gmail.com Subtitle [ FilePath | txt ]
"""

import smtplib
import sys,traceback
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from time import gmtime, strftime

#log file location
log_path = "./mail-log";
#log timestamp format
logger_tstamp = "%Y-%m-%d %H:%M:%S"

#SMPT server
smtp_server = "smtp.gmail.com"
#gmail 465 or 578
smtp_port = 587

#mail from 
user = "from@gmail.com"
pwd = "password"

# log method
class Logger:
    file = 1
    err = 2

class ContentType:
    file = 1
    txt = 2

#select log method 
debug_log = Logger.err
#select content type
content_type = ContentType.txt

def debug(msg):
    if debug_log == Logger.file:
        with open(log_path, 'a') as log_file:
            log_file.write(strftime(logger_tstamp, gmtime()) + msg)
        log_file.close()

    elif debug_log == Logger.err:
        sys.stderr.write(strftime(logger_tstamp, gmtime()) + msg)

    else: 
        print(strftime(logger_tstamp, gmtime()) + msg)

#check argument number
arg_count = len(sys.argv)
if arg_count !=4:
    debug("[Error]: invalid argument\n")
    sys.exit(1)
try:
    mail_to = sys.argv[1]
    subject = sys.argv[2]
    if content_type == ContentType.file:
        content_path = sys.argv[3]
        with open(content_path, 'r') as f_content:
            content = f_content.read()
        f_content.close()
    else:
        content = sys.argv[3]

    serv = smtplib.SMTP(smtp_server, smtp_port)
#    serv.ehlo()
    serv.starttls()
#    serv.ehlo()
    serv.login(user, pwd);
    msg = MIMEMultipart('alternative')
    msg['Subject'] = subject
    msg['From'] = user
    msg['To'] = mail_to
    part1 = MIMEText(content, 'html')
    msg.attach(part1)
    serv.sendmail(user, mail_to, msg.as_string())
    serv.quit()
    debug("[Debug] subject : " + subject + "\n")
except:
    debug("[Error]: send mail error\n")
    debug("[Error]:\n" + traceback.format_exc())
    sys.exit(2);

Reference:
email: Examples
smtplib
How to send email in Python via SMTPLIB

Friday, July 5, 2013

Redirect and Save iptables on Ubuntu 12.04

Redirect port 8080 to 80

sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080  

Check iptables setting

sudo iptables -t nat -L  

Save configure to iptables.rules

sudo iptables-save > /etc/iptables.rules  

Save Solution #1

Configre /etc/network/interfaces

iface eth0 inet dhcp  
    pre-up iptables-restore < /etc/iptables.rules  

Save Solution #2

Configure /etc/network/if-pre-up.d/iptablesload

#!/bin/sh
iptables-restore < /etc/iptables.rules
exit 0

Configure /etc/network/if-post-down.d/iptablessave

#!/bin/sh
iptables-save -c > /etc/iptables.rules
if [ -f /etc/iptables.downrules ]; then
    iptables-restore < /etc/iptables.downrules
fi
exit 0

Change permissions

sudo chmod +x /etc/network/if-post-down.d/iptablessave  
sudo chmod +x /etc/network/if-pre-up.d/iptablesload  

Reference:
Tomcat: redirecting traffic from port 8080 to 80 using iptables
IptablesHowTo