#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2011 Ryohei Ueda
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import xml.sax.saxutils
import logging
from .template import *
import random
from socket import gethostbyaddr
import BaseHTTPServer
import cgi
import sqlite3
import socket
import binascii
import os
import ping                     # easy_install ping
from subprocess import check_call
from string import Template

import parallel_util

LOGGER_FILE_FORMAT = '[%(asctime)s] %(name)s [%(levelname)s] %(message)s'
LOGGER_CONSOLE_FORMAT = '>> %(message)s'

def ping_host(hostname):
    ping_ret = ping.quiet_ping(hostname, timeout=0.1)
    return ping_ret[0] == 0     # verify return core is 0

def create_logger(log_file_name):
    logger = logging.getLogger("pxe")
    fh = logging.FileHandler(log_file_name)
    fh.setLevel(logging.INFO)
    ch = logging.StreamHandler()
    ch.setLevel(logging.INFO)
    fh.setFormatter(logging.Formatter(LOGGER_FILE_FORMAT))
    ch.setFormatter(logging.Formatter(LOGGER_CONSOLE_FORMAT))
    logger.addHandler(fh)
    logger.addHandler(ch)
    logger.setLevel(logging.INFO)
    return logger

def send_wol_magick_packet(macs, ipaddr, port):
    "http://www.emptypage.jp/gadgets/wol.html"
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    for mac in macs:
        for sep in ':-':
            if sep in mac:
                mac = ''.join([x.rjust(2, '0') for x in mac.split(sep)])
                break
        mac = mac.rjust(12, '0')
        p = '\xff' * 6 + binascii.unhexlify(mac) * 16
        s.sendto(p, (ipaddr, port))
    s.close()
    
def open_db(db):
    logger = logging.getLogger("pxe")
    logger.info("opening %s as DB" % (db))
    if os.path.exists(db):
        con = sqlite3.connect(db)
    else:
        con = sqlite3.connect(db)
        con.execute(DB_CREATE_TABLE_SQL)
    return con

def delete_host(con, host):
    template = Template(DEL_HOST_SQL)
    sql = template.substitute({"hostname": host})
    con.execute(sql)
    con.commit()

def add_host(con, host, ip, mac, root):
    template = Template(ADD_HOST_SQL)
    sql = template.substitute({"hostname": host,
                               "ip": ip,
                               "macaddress": mac,
                               "root": root})
    con.execute(sql)
    con.commit()

def all_hosts(con):
    sql_result = con.execute(ALL_HOSTS_SQL)
    result = {}
    for row in sql_result:
        result[row[0]] = {"ip": row[1], "macaddress": row[2], "root": row[3]}
    return result

def find_by_ip(con, ip):
    template = Template(FIND_IP_SQL)
    sql_str = template.substitute({"ip": ip})
    sql_result = con.execute(sql_str)
    for row in sql_result:
        return {"ip": row[1], "macaddress": row[2], "root": row[3]}
    return False

def find_by_hostname(con, hostname):
    template = Template(FIND_HOST_SQL)
    sql_str = template.substitute({"hostname": hostname})
    sql_result = con.execute(sql_str)
    for row in sql_result:
        return {"ip": row[1], "macaddress": row[2], "root": row[3]}
    raise "cannot find %s" % (hostname)

def delete_machine(hostname, db):
    logger = logging.getLogger("pxe")
    con = open_db(db)
    logger.info("deleteing a machine:: %s" % hostname)
    delete_host(con, hostname)
    con.close()
    
def add_machine(hostname, mac, ip, root, db):
    logger = logging.getLogger("pxe")
    con = open_db(db)
    logger.info("adding a machine:: hostname - %s, mac - %s, ip - %s, root - %s" % (hostname, mac, ip, root))
    add_host(con, hostname, ip, mac, root)
    con.close()
    
def generate_dhcp(db,
                  prefix_dhcp_file,
                  subnet,
                  netmask,
                  dhcp_range_start,
                  dhcp_range_stop,
                  broadcast,
                  dns_ip,
                  domain_name,
                  gateway,
                  pxe_server,
                  pxe_filename,
                  overwrite_dhcp,
                  dhcp_conf_file,
                  restart_dhcp):
    logger = logging.getLogger("pxe")
    logger.info("generating dhcpd.conf")
    con = open_db(db)
    machines = all_hosts(con)
    generated_string = []
    # generate host section
    for (hostname, ip_mac) in machines.items():
        ip = ip_mac["ip"]
        mac = ip_mac["macaddress"]
        template = Template(DHCP_HOST_TMPL)
        host_str = template.substitute({"hostname": hostname,
                                        "ip": ip,
                                        "mac": mac})
        generated_string.append(host_str)
    if prefix_dhcp_file:
        logger.info("adding %s as prefix" % (prefix_dhcp_file))
        f = open(prefix_dhcp_file)
        prefix_str = "".join(f.readlines())
    else:
        prefix_str = ""
    # generate dhcp subnet section
    dhcp_subnet_tmpl = Template(DHCP_SUBNET_TMPL)
    replace_dict = {"subnet": subnet,
                    "netmask": netmask,
                    "dhcp_range_start": dhcp_range_start,
                    "dhcp_range_stop": dhcp_range_stop,
                    "broadcast": broadcast,
                    "dns_ip": dns_ip,
                    "domain_name": domain_name,
                    "gateway": gateway,
                    "pxe_server": pxe_server,
                    "pxe_filename": pxe_filename,
                    "hosts": "\n".join(generated_string)}
    dhcp_subnet_str = dhcp_subnet_tmpl.substitute(replace_dict)
    if overwrite_dhcp:
        logger.info("overwriting %s" % (dhcp_conf_file))
        path = dhcp_conf_file
        f = open(path, "w")
        f.write(prefix_str + dhcp_subnet_str)
        f.close()
        if restart_dhcp:
            logger.info("restarting dhcp")
            check_call(["sudo", "/etc/init.d/dhcp3-server", "restart"])
    else:
        print(prefix_str + dhcp_subnet_str)

def print_machine_list(db):
    logger = logging.getLogger("pxe")
    con = open_db(db)
    machines = all_hosts(con)
    for hostname, ip_mac in machines.items():
        ip = ip_mac["ip"]
        mac = ip_mac["macaddress"]
        root = ip_mac["root"]
        print("""%s:
  ip: %s
  mac: %s
  root: %s
""" % (hostname, ip, mac, root))

def wake_on_lan(hostname, port, broadcast, db):
    logger = logging.getLogger("pxe")
    con = open_db(db)
    logger.info("wake on lan %s" % (hostname))
    mac = find_by_hostname(con, hostname)["macaddress"]
    send_wol_magick_packet([mac], broadcast, port)

def chroot_command(chroot_dir, *args):
    command = ["sudo", "chroot", chroot_dir]
    command.extend(*args)
    return check_call(command)
    
def generate_pxe_template_filesystem(template_dir):
    logger = logging.getLogger("pxe")
    logger.info("generating template filesyste")
    try:
        check_call(["sudo", "debootstrap", "lucid", template_dir])
    except:
        # remove template_dir
        logger.info("removing template dir")
        check_call(["sudo", "rm", "-rf", template_dir])
        raise


def copy_template_filesystem(template_dir, target_dir, apt_sources):
    logger = logging.getLogger("pxe")
    logger.info("copying filesystem")
    check_call(["sudo", "cp", "-ax", template_dir, target_dir])
    logger.info("copying etc/apt/sources.list")
    check_call(["sudo", "cp", apt_sources,
                os.path.join(target_dir, "etc", "apt", "sources.list")])
    return

class ChrootEnvironment():
    def __init__(self, target_dir):
        self.target_dir = target_dir
    def __enter__(self):
        target_dir = self.target_dir
        check_call(["sudo", "mount", "-o", "bind", "/dev/",
                    os.path.join(target_dir, "dev")])
        chroot_command(target_dir,
                       "mount -t proc none /proc".split())
        chroot_command(target_dir,
                       "mount -t sysfs none /sys".split())
        chroot_command(target_dir,
                       "mount -t devpts none /dev/pts".split())
    def __exit__(self, type, value, traceback):
        target_dir = self.target_dir
        chroot_command(target_dir,
                       "umount -lf /proc".split())
        chroot_command(target_dir,
                       "umount -lf /sys".split())
        chroot_command(target_dir,
                       "umount -lf /dev/pts".split())
        check_call(["sudo", "umount", "-lf", os.path.join(target_dir, "dev")])
 

def install_apt_packages(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger("pxe")
        logger.info("installing base apt packages")
        chroot_command(target_dir, ["apt-get", "update"])
        chroot_command(target_dir, ["apt-get", "install", "wget"])
        chroot_command(target_dir, ["sh", "-c",
                                    "wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | apt-key add -"])
        chroot_command(target_dir, ["sh", "-c",
                                    "wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | apt-key add -"])
        chroot_command(target_dir, ["sh", "-c",
                                    "wget https://www.ubuntulinux.jp/sources.list.d/lucid.list -O /etc/apt/sources.list.d/ubuntu-ja.list"])
        chroot_command(target_dir, ["apt-get", "update"])
        chroot_command(target_dir,
                       ["apt-get", "install", "--force-yes", "-y"] + APT_PACKAGES.split())
        logger.info("installing ros and openrave apt packages")
        chroot_command(target_dir, ["sh", "-c",
                                    "echo deb http://packages.ros.org/ros/ubuntu lucid main > /etc/apt/sources.list.d/ros-latest.list"])
        chroot_command(target_dir, ["sh", "-c",
                                    "wget http://packages.ros.org/ros.key -O - | apt-key add -"])
        chroot_command(target_dir, ["add-apt-repository", "ppa:openrave/release"])
        chroot_command(target_dir, ["apt-get", "update"])
        #chroot_command(target_dir, ["apt-get", "install", "--force-yes", "-y", "ros-diamondback-ros-base", "openrave"])

def setup_user(target_dir, user, passwd):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger("pxe")
        logger.info("adding pxe user")
        chroot_command(target_dir, ["sh", "-c",
                                    "id %s || useradd %s -g sudo -s /bin/bash" % (user,
                                                                                  user)])
        chroot_command(target_dir, ["sh", "-c",
                                    "echo %s:%s | chpasswd" % (user, passwd)])
        chroot_command(target_dir, ["rm", "-f", "/etc/hostname"])

def update_initram(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger("pxe")
        logger.info("updating initram.img")
        chroot_command(target_dir, ["sh", "-c",
                                    "echo '%s' > /etc/initramfs-tools/initramfs.conf" % (INITRAMFS_CONF)])
        chroot_command(target_dir, ["sh", "-c",
                                    "echo '%s' > /etc/fstab" % (FSTAB)])
        chroot_command(target_dir, ["sh", "-c",
                                    "echo '%s' > /etc/initramfs-tools/modules" % (INITRAM_MODULES)])
        chroot_command(target_dir, ["sh", "-c",
                                    "mkinitramfs -o /boot/initrd.img-`uname -r` `ls /lib/modules`"])

def setup_pxe_dphys(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger("pxe")
        logger.info("setup pxe_dphys")
        chroot_command(target_dir,
                       ["sh", "-c",
                        """cat > /usr/local/sbin/pxe-dphys-swapfile <<EOF
%s
EOF""" % (PXE_DPHYS_SWAPFILE)])
        chroot_command(target_dir,
                       ["sh", "-c",
                        """cat > /etc/init.d/pxe-dphys-swapfile <<EOF
%s
EOF""" % (PXE_DPHYS_SWAPFILE_INIT_D)])
        chroot_command(target_dir,
                       ["sh", "-c",
                        """cat > /etc/dphys-swapfile <<EOF
%s
EOF""" % (PXE_DPHYS_CONFIG)])
        chroot_command(target_dir,
                       ["chmod", "+x", "/usr/local/sbin/pxe-dphys-swapfile"])
        chroot_command(target_dir,
                       ["chmod", "+x", "/etc/init.d/pxe-dphys-swapfile"])
        chroot_command(target_dir,
                       ["update-rc.d", "pxe-dphys-swapfile", "defaults"])

        
def generate_pxe_filesystem(template_dir, target_dir, apt_sources,
                            user, passwd):
    logger = logging.getLogger("pxe")
    if not os.path.exists(template_dir):
        generate_pxe_template_filesystem(template_dir)
    if not os.path.exists(target_dir):
        copy_template_filesystem(template_dir, target_dir, apt_sources)
    check_call(["sudo", "cp", apt_sources,
                os.path.join(target_dir, "etc", "apt", "sources.list")])
    install_apt_packages(target_dir)
    setup_user(target_dir, user, passwd)
    setup_pxe_dphys(target_dir)
    update_initram(target_dir)

def generate_top_html(db, log):
    con = open_db(db)
    machines = all_hosts(con)
    host_strs = []
    hosts = sorted(machines.keys())
    for hostname in hosts:
        ip_mac = machines[hostname]
        ip = ip_mac["ip"]
        mac = ip_mac["macaddress"]
        root = ip_mac["root"]
        alivep = ping_host(ip)
        if alivep:
            template = Template(HTML_ALIVE_HOST_TMPL)
        else:
            template = Template(HTML_DEAD_HOST_TMPL)
        host_strs.append(template.substitute({"hostname": hostname,
                                              "ip": ip,
                                              "macaddress": mac,
                                              "root": root}))
    con.close()
    # generate log
    f = open(log)
    log_lines = f.readlines()[-1000:] # max 1000 lines
    log_strs = []
    for line in log_lines:
        log_strs.append("<p>" + xml.sax.saxutils.escape(line) + "</p>")
    log_strs.reverse()
    f.close()
    html_template = Template(HTML_TMPL)
    
    return html_template.substitute({"hosts": "\n".join(host_strs),
                                     "log": "\n".join(log_strs)})

def update_dhcp_from_web():
    if global_options.overwrite_dhcp:
        logger = logging.getLogger("pxe")
        generate_dhcp(global_options.db,
                      global_options.prefix_dhcp_file,
                      global_options.subnet,
                      global_options.netmask,
                      global_options.dhcp_range_start,
                      global_options.dhcp_range_stop,
                      global_options.broadcast,
                      global_options.dns_ip,
                      global_options.domain_name,
                      global_options.gateway,
                      global_options.pxe_server,
                      global_options.pxe_filename,
                      global_options.overwrite_dhcp,
                      global_options.dhcp_conf_file)
    if global_options.generate_pxe_config_files:
        generate_pxe_config_files(global_options.db,
                                  global_options.generate_pxe_config_files,
                                  global_options.tftp_dir)
    # restart dhcp server
    check_call(["sudo", "/etc/init.d/dhcp3-server", "restart"])

def generate_error_html(e, host, port):
    return ERROR_HTML_TMPL % ("http://" + host + ":" + str(port), e)
        
class WebHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(s):
        s.send_response(200)
        s.send_header("Content-type", "text/html")
        s.end_headers()
        try:
            if s.path.startswith("/delete"): # --delete
                delete_host = cgi.parse_qs(s.path.split("?")[1]).keys()[0]
                delete_machine(delete_host, db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ("http://" +
                                            global_options.web_hostname
                                            + ":" +
                                            str(global_options.web_port))
            elif s.path.startswith("/add"): # --add
                add_host_desc = cgi.parse_qs(s.path.split("?")[1])
                add_machine(add_host_desc["hostname"][0],
                            add_host_desc["macaddress"][0],
                            add_host_desc["ip"][0],
                            add_host_desc["root"][0],
                            db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ("http://" +
                                            global_options.web_hostname
                                            + ":" +
                                            str(global_options.web_port))
            elif s.path.startswith("/auto_add"): # --auto-add
                add_host_desc = cgi.parse_qs(s.path.split("?")[1])
                root_dir = add_host_desc["root"][0]
                try:
                    global_options.auto_add = root_dir
                    auto_add_vm(global_options)
                finally:
                    global_options.auto_add = None
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ("http://" +
                                            global_options.web_hostname
                                            + ":" +
                                            str(global_options.web_port))
            elif s.path.startswith("/boot"):
                logger = logging.getLogger("pxe")
                html = SUCCESS_HTML_TMPL % ("http://" +
                                            global_options.web_hostname
                                            + ":" +
                                            str(global_options.web_port))
                params = cgi.parse_qs(s.path.split("?")[1])
                vmname = params["vmname"][0]
                physc = params["physical"][0]
                logger.info("invoke %s @ %s" % (vmname, physc))
                try:
                    global_options.generate_virtualbox_image = vmname
                    global_options.refer_physical_machine = physc
                    generate_virtualbox_image(global_options.db,
                                              global_options.generate_virtualbox_image,
                                              global_options.virtualbox_path,
                                              global_options.refer_physical_machine,
                                              global_options.virtualbox_cpunum,
                                              global_options.virtualbox_memsize,
                                              global_options.virtualbox_vramsize,
                                              global_options.virtualbox_macaddress,
                                              global_options.remote_user,
                                              False)
                finally:
                    global_options.generate_virtualbox_image = None
                    global_options.refer_physical_machine = None
                boot_vm(global_options.virtualbox_path, vmname, physc)
                html = SUCCESS_HTML_TMPL % ("http://" +
                                            global_options.web_hostname
                                            + ":" +
                                            str(global_options.web_port))
            else:
                html = generate_top_html(db_name, global_options.log)
            s.wfile.write(html)
        except Exception as e:
            logger = logging.getLogger("pxe")
            logger.info("error has occurred: %s" % (e))
            html = generate_error_html(e,
                                       global_options.web_hostname,
                                       global_options.web_port)
            s.wfile.write(html)
    
def run_web(options):
    db = options.db
    port = options.web_port
    overwritep = options.overwrite_dhcp
    # setup global variables
    global db_name              # dirty hack!
    global global_options
    db_name = db
    global_options = options
    BaseHTTPServer.HTTPServer((options.web_hostname, port),
                              WebHandler).serve_forever()

def generate_pxe_config_files(db, generate_pxe_config_files, tftp_dir):
    if generate_pxe_config_files:
        logger = logging.getLogger("pxe")
        logger.info("generate the pxe configuration files")
        con = open_db(db)
        machines = all_hosts(con)
        for hostname in machines.keys():
            template = Template(PXE_CONFIG_TMPL)
            mac = machines[hostname]["macaddress"]
            file_name = os.path.join(tftp_dir, "pxelinux.cfg",
                                     "01-" + mac.replace(":", "-"))
            file_str = template.substitute({"hostname": hostname,
                                            "root": machines[hostname]["root"],
                                            "tftp_dir": tftp_dir})

            f = open(file_name, "w")
            f.write(file_str)
            f.close()
            check_call(["sudo", "chown", "pxe.tftp", file_name])
            check_call(["sudo", "chmod", "g+rw", file_name])
            check_call(["sudo", "chmod", "u+rw", file_name])
            
def generate_uuid():
    pipe = os.popen("uuidgen")
    uuid = pipe.read().strip()
    pipe.close()
    return uuid

def generate_virtualbox_image(db,
                              generate_virtualbox_image,
                              virtualbox_path,
                              refer_physical_machine,
                              virtualbox_cpunum,
                              virtualbox_memsize,
                              virtualbox_vramsize,
                              virtualbox_macaddress,
                              remote_user,
                              register_vm = True):
                              
    vmname = generate_virtualbox_image
    logger = logging.getLogger("pxe")
    if not os.path.exists(virtualbox_path):
        os.makedirs(virtualbox_path)
    if refer_physical_machine:
        host = refer_physical_machine
        logger.info("get cpuinfo of %s" % (host))
        infos = parallel_util.cpuinfos([host],
                                       arch_filter = False,
                                       verbose = True,
                                       ros_filter = False,
                                       username = remote_user)
        logger.info("cpuinfo of %s => %s" % (host, infos[0]))
        cpunum = infos[0][host][0]
        memsize = int(infos[0][host][1] * 0.8 / 1000)
    else:
        cpunum = virtualbox_cpunum
        memsize = virtualbox_memsize
    vramsize = virtualbox_vramsize
    if virtualbox_macaddress:
        macaddress = virtualbox_macaddress
    else:
        # lookup macaddress from db
        con = open_db(db)
        hostname = find_by_hostname(con, vmname)
        macaddress = hostname["macaddress"]
        con.close()
    cd_uuid = "9f6f1044-98a6-406c-be64-eec39baef4cb"
    machine_uuid = generate_uuid()
    vm_path = os.path.join(virtualbox_path, vmname + ".vbox")
    f = open(vm_path, "w")
    template = Template(VIRTUALBOX_XML_TEMPLATE)
    content = template.substitute({"machine_uuid": machine_uuid,
                                   "cd_uuid": cd_uuid,
                                   "hostname": vmname,
                                   "cpunum": cpunum,
                                   "memsize": memsize,
                                   "vramsize": vramsize,
                                   "macaddress": macaddress.replace(":", "")})
    f.write(content)
    f.close()
    #print("please register %s on your virtualbox" % (vm_path))
    if register_vm:
        check_call(["VBoxManage", "registervm", vmname + ".vbox"])
    
def print_virtualbox_macaddress():
    print(generate_virtualbox_macaddress())
    
def generate_virtualbox_macaddress():
    # TODO: check duplication of macaddress
    return "08:00:27:%02x:%02x:%02x" % (int(random.random()*0xff), int(random.random()*0xff), int(random.random()*0xff))

def nslookup(ip):
    output = gethostbyaddr(ip)
    return output[0]

def print_lookup_free_host(options):
    string = lookup_free_host(options)
    print(string)

def lookup_free_host(db, dhcp_range_start, dhcp_range_stop):
    con = open_db(db)
    start = dhcp_range_start
    stop = dhcp_range_stop
    # it might be a bug
    ip_1 = start.split(".")[0]
    ip_2 = start.split(".")[1]
    ip_3 = start.split(".")[2]
    start_ip_suffix = int(start.split(".")[3])
    stop_ip_suffix = int(stop.split(".")[3])
    for ip in range(start_ip_suffix, stop_ip_suffix):
        full_ip = ".".join([ip_1, ip_2, ip_3, str(ip)])
        not_available = find_by_ip(con, full_ip)
        if not not_available:
            # TODO: check ping!
            return "%s %s" % (nslookup(full_ip), full_ip)
    print("none")

def auto_add_vm(options):
    logger = logging.getLogger("pxe")
    root = options.auto_add
    free_host_ip = lookup_free_host(options.db,
                                    options.dhcp_range_start,
                                    options.dhcp_range_stop)
    free_host = free_host_ip.split()[0]
    free_ip = free_host_ip.split()[1]
    mac_address = generate_virtualbox_macaddress()
    add_machine(free_host, mac_address, free_ip, root, options.db)
    options.overwrite_dhcp = True # force to be True
    generate_dhcp(options.db,
                  options.prefix_dhcp_file,
                  options.subnet,
                  options.netmask,
                  options.dhcp_range_start,
                  options.dhcp_range_stop,
                  options.broadcast,
                  options.dns_ip,
                  options.domain_name,
                  options.gateway,
                  options.pxe_server,
                  options.pxe_filename,
                  options.overwrite_dhcp,
                  options.dhcp_conf_file)
    options.generate_pxe_config_files = True # force to be True
    generate_pxe_config_files(options.db,
                              options.generate_pxe_config_files,
                              options.tftp_dir)
    print(free_host)

def boot_vm(vmdir, vmname, physical_machine):
    logger = logging.getLogger("pxe")
    cmd0 = ["ssh", "-t", "pxe@%s" % (physical_machine),
            "sh -c 'VBoxManage unregistervm %s || exit 0'" % (vmname)]
    cmd1 = ["ssh", "pxe@%s" % physical_machine, "mkdir -p .VirtualBox"]
    cmd2 = ["scp", os.path.join(vmdir, vmname + ".vbox"), 
            "pxe@%s:.VirtualBox" % (physical_machine)]
    cmd3 = ["ssh", "-t", "pxe@%s" % (physical_machine),
            "VBoxManage registervm %s" % (vmname + ".vbox")]
    cmd4 = ["ssh", "pxe@%s" % physical_machine,
            "screen -d -m VBoxHeadless -s %s" % vmname]
    logger.info("exec => %s" % (cmd0))
    check_call(cmd0)
    logger.info("exec => %s" % (cmd1))
    check_call(cmd1)
    logger.info("exec => %s" % (cmd2))
    check_call(cmd2)
    logger.info("exec => %s" % (cmd3))
    check_call(cmd3)
    logger.info("exec => %s" % (cmd4))
    check_call(cmd4)

27ed45db0c2eb555ae591cfb72001a440fb85356
<mask token>


def ping_host(hostname):
    ping_ret = ping.quiet_ping(hostname, timeout=0.1)
    return ping_ret[0] == 0


<mask token>


def send_wol_magick_packet(macs, ipaddr, port):
    """http://www.emptypage.jp/gadgets/wol.html"""
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    for mac in macs:
        for sep in ':-':
            if sep in mac:
                mac = ''.join([x.rjust(2, '0') for x in mac.split(sep)])
                break
        mac = mac.rjust(12, '0')
        p = 'ÿ' * 6 + binascii.unhexlify(mac) * 16
        s.sendto(p, (ipaddr, port))
    s.close()


def open_db(db):
    logger = logging.getLogger('pxe')
    logger.info('opening %s as DB' % db)
    if os.path.exists(db):
        con = sqlite3.connect(db)
    else:
        con = sqlite3.connect(db)
        con.execute(DB_CREATE_TABLE_SQL)
    return con


def delete_host(con, host):
    template = Template(DEL_HOST_SQL)
    sql = template.substitute({'hostname': host})
    con.execute(sql)
    con.commit()


def add_host(con, host, ip, mac, root):
    template = Template(ADD_HOST_SQL)
    sql = template.substitute({'hostname': host, 'ip': ip, 'macaddress':
        mac, 'root': root})
    con.execute(sql)
    con.commit()


<mask token>


def find_by_hostname(con, hostname):
    template = Template(FIND_HOST_SQL)
    sql_str = template.substitute({'hostname': hostname})
    sql_result = con.execute(sql_str)
    for row in sql_result:
        return {'ip': row[1], 'macaddress': row[2], 'root': row[3]}
    raise ('cannot find %s' % hostname)


def delete_machine(hostname, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info('deleteing a machine:: %s' % hostname)
    delete_host(con, hostname)
    con.close()


def add_machine(hostname, mac, ip, root, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info(
        'adding a machine:: hostname - %s, mac - %s, ip - %s, root - %s' %
        (hostname, mac, ip, root))
    add_host(con, hostname, ip, mac, root)
    con.close()


def generate_dhcp(db, prefix_dhcp_file, subnet, netmask, dhcp_range_start,
    dhcp_range_stop, broadcast, dns_ip, domain_name, gateway, pxe_server,
    pxe_filename, overwrite_dhcp, dhcp_conf_file, restart_dhcp):
    logger = logging.getLogger('pxe')
    logger.info('generating dhcpd.conf')
    con = open_db(db)
    machines = all_hosts(con)
    generated_string = []
    for hostname, ip_mac in machines.items():
        ip = ip_mac['ip']
        mac = ip_mac['macaddress']
        template = Template(DHCP_HOST_TMPL)
        host_str = template.substitute({'hostname': hostname, 'ip': ip,
            'mac': mac})
        generated_string.append(host_str)
    if prefix_dhcp_file:
        logger.info('adding %s as prefix' % prefix_dhcp_file)
        f = open(prefix_dhcp_file)
        prefix_str = ''.join(f.readlines())
    else:
        prefix_str = ''
    dhcp_subnet_tmpl = Template(DHCP_SUBNET_TMPL)
    replace_dict = {'subnet': subnet, 'netmask': netmask,
        'dhcp_range_start': dhcp_range_start, 'dhcp_range_stop':
        dhcp_range_stop, 'broadcast': broadcast, 'dns_ip': dns_ip,
        'domain_name': domain_name, 'gateway': gateway, 'pxe_server':
        pxe_server, 'pxe_filename': pxe_filename, 'hosts': '\n'.join(
        generated_string)}
    dhcp_subnet_str = dhcp_subnet_tmpl.substitute(replace_dict)
    if overwrite_dhcp:
        logger.info('overwriting %s' % dhcp_conf_file)
        path = dhcp_conf_file
        f = open(path, 'w')
        f.write(prefix_str + dhcp_subnet_str)
        f.close()
        if restart_dhcp:
            logger.info('restarting dhcp')
            check_call(['sudo', '/etc/init.d/dhcp3-server', 'restart'])
    else:
        print(prefix_str + dhcp_subnet_str)


<mask token>


def wake_on_lan(hostname, port, broadcast, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info('wake on lan %s' % hostname)
    mac = find_by_hostname(con, hostname)['macaddress']
    send_wol_magick_packet([mac], broadcast, port)


def chroot_command(chroot_dir, *args):
    command = ['sudo', 'chroot', chroot_dir]
    command.extend(*args)
    return check_call(command)


<mask token>


def copy_template_filesystem(template_dir, target_dir, apt_sources):
    logger = logging.getLogger('pxe')
    logger.info('copying filesystem')
    check_call(['sudo', 'cp', '-ax', template_dir, target_dir])
    logger.info('copying etc/apt/sources.list')
    check_call(['sudo', 'cp', apt_sources, os.path.join(target_dir, 'etc',
        'apt', 'sources.list')])
    return


class ChrootEnvironment:

    def __init__(self, target_dir):
        self.target_dir = target_dir

    def __enter__(self):
        target_dir = self.target_dir
        check_call(['sudo', 'mount', '-o', 'bind', '/dev/', os.path.join(
            target_dir, 'dev')])
        chroot_command(target_dir, 'mount -t proc none /proc'.split())
        chroot_command(target_dir, 'mount -t sysfs none /sys'.split())
        chroot_command(target_dir, 'mount -t devpts none /dev/pts'.split())

    def __exit__(self, type, value, traceback):
        target_dir = self.target_dir
        chroot_command(target_dir, 'umount -lf /proc'.split())
        chroot_command(target_dir, 'umount -lf /sys'.split())
        chroot_command(target_dir, 'umount -lf /dev/pts'.split())
        check_call(['sudo', 'umount', '-lf', os.path.join(target_dir, 'dev')])


def install_apt_packages(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('installing base apt packages')
        chroot_command(target_dir, ['apt-get', 'update'])
        chroot_command(target_dir, ['apt-get', 'install', 'wget'])
        chroot_command(target_dir, ['sh', '-c',
            'wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | apt-key add -'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | apt-key add -'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget https://www.ubuntulinux.jp/sources.list.d/lucid.list -O /etc/apt/sources.list.d/ubuntu-ja.list'
            ])
        chroot_command(target_dir, ['apt-get', 'update'])
        chroot_command(target_dir, ['apt-get', 'install', '--force-yes',
            '-y'] + APT_PACKAGES.split())
        logger.info('installing ros and openrave apt packages')
        chroot_command(target_dir, ['sh', '-c',
            'echo deb http://packages.ros.org/ros/ubuntu lucid main > /etc/apt/sources.list.d/ros-latest.list'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget http://packages.ros.org/ros.key -O - | apt-key add -'])
        chroot_command(target_dir, ['add-apt-repository',
            'ppa:openrave/release'])
        chroot_command(target_dir, ['apt-get', 'update'])


def setup_user(target_dir, user, passwd):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('adding pxe user')
        chroot_command(target_dir, ['sh', '-c', 
            'id %s || useradd %s -g sudo -s /bin/bash' % (user, user)])
        chroot_command(target_dir, ['sh', '-c', 'echo %s:%s | chpasswd' % (
            user, passwd)])
        chroot_command(target_dir, ['rm', '-f', '/etc/hostname'])


<mask token>


def generate_top_html(db, log):
    con = open_db(db)
    machines = all_hosts(con)
    host_strs = []
    hosts = sorted(machines.keys())
    for hostname in hosts:
        ip_mac = machines[hostname]
        ip = ip_mac['ip']
        mac = ip_mac['macaddress']
        root = ip_mac['root']
        alivep = ping_host(ip)
        if alivep:
            template = Template(HTML_ALIVE_HOST_TMPL)
        else:
            template = Template(HTML_DEAD_HOST_TMPL)
        host_strs.append(template.substitute({'hostname': hostname, 'ip':
            ip, 'macaddress': mac, 'root': root}))
    con.close()
    f = open(log)
    log_lines = f.readlines()[-1000:]
    log_strs = []
    for line in log_lines:
        log_strs.append('<p>' + xml.sax.saxutils.escape(line) + '</p>')
    log_strs.reverse()
    f.close()
    html_template = Template(HTML_TMPL)
    return html_template.substitute({'hosts': '\n'.join(host_strs), 'log':
        '\n'.join(log_strs)})


def update_dhcp_from_web():
    if global_options.overwrite_dhcp:
        logger = logging.getLogger('pxe')
        generate_dhcp(global_options.db, global_options.prefix_dhcp_file,
            global_options.subnet, global_options.netmask, global_options.
            dhcp_range_start, global_options.dhcp_range_stop,
            global_options.broadcast, global_options.dns_ip, global_options
            .domain_name, global_options.gateway, global_options.pxe_server,
            global_options.pxe_filename, global_options.overwrite_dhcp,
            global_options.dhcp_conf_file)
    if global_options.generate_pxe_config_files:
        generate_pxe_config_files(global_options.db, global_options.
            generate_pxe_config_files, global_options.tftp_dir)
    check_call(['sudo', '/etc/init.d/dhcp3-server', 'restart'])


def generate_error_html(e, host, port):
    return ERROR_HTML_TMPL % ('http://' + host + ':' + str(port), e)


class WebHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(s):
        s.send_response(200)
        s.send_header('Content-type', 'text/html')
        s.end_headers()
        try:
            if s.path.startswith('/delete'):
                delete_host = cgi.parse_qs(s.path.split('?')[1]).keys()[0]
                delete_machine(delete_host, db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/add'):
                add_host_desc = cgi.parse_qs(s.path.split('?')[1])
                add_machine(add_host_desc['hostname'][0], add_host_desc[
                    'macaddress'][0], add_host_desc['ip'][0], add_host_desc
                    ['root'][0], db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/auto_add'):
                add_host_desc = cgi.parse_qs(s.path.split('?')[1])
                root_dir = add_host_desc['root'][0]
                try:
                    global_options.auto_add = root_dir
                    auto_add_vm(global_options)
                finally:
                    global_options.auto_add = None
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/boot'):
                logger = logging.getLogger('pxe')
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
                params = cgi.parse_qs(s.path.split('?')[1])
                vmname = params['vmname'][0]
                physc = params['physical'][0]
                logger.info('invoke %s @ %s' % (vmname, physc))
                try:
                    global_options.generate_virtualbox_image = vmname
                    global_options.refer_physical_machine = physc
                    generate_virtualbox_image(global_options.db,
                        global_options.generate_virtualbox_image,
                        global_options.virtualbox_path, global_options.
                        refer_physical_machine, global_options.
                        virtualbox_cpunum, global_options.
                        virtualbox_memsize, global_options.
                        virtualbox_vramsize, global_options.
                        virtualbox_macaddress, global_options.remote_user, 
                        False)
                finally:
                    global_options.generate_virtualbox_image = None
                    global_options.refer_physical_machine = None
                boot_vm(global_options.virtualbox_path, vmname, physc)
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            else:
                html = generate_top_html(db_name, global_options.log)
            s.wfile.write(html)
        except Exception as e:
            logger = logging.getLogger('pxe')
            logger.info('error has occurred: %s' % e)
            html = generate_error_html(e, global_options.web_hostname,
                global_options.web_port)
            s.wfile.write(html)


def run_web(options):
    db = options.db
    port = options.web_port
    overwritep = options.overwrite_dhcp
    global db_name
    global global_options
    db_name = db
    global_options = options
    BaseHTTPServer.HTTPServer((options.web_hostname, port), WebHandler
        ).serve_forever()


def generate_pxe_config_files(db, generate_pxe_config_files, tftp_dir):
    if generate_pxe_config_files:
        logger = logging.getLogger('pxe')
        logger.info('generate the pxe configuration files')
        con = open_db(db)
        machines = all_hosts(con)
        for hostname in machines.keys():
            template = Template(PXE_CONFIG_TMPL)
            mac = machines[hostname]['macaddress']
            file_name = os.path.join(tftp_dir, 'pxelinux.cfg', '01-' + mac.
                replace(':', '-'))
            file_str = template.substitute({'hostname': hostname, 'root':
                machines[hostname]['root'], 'tftp_dir': tftp_dir})
            f = open(file_name, 'w')
            f.write(file_str)
            f.close()
            check_call(['sudo', 'chown', 'pxe.tftp', file_name])
            check_call(['sudo', 'chmod', 'g+rw', file_name])
            check_call(['sudo', 'chmod', 'u+rw', file_name])


def generate_uuid():
    pipe = os.popen('uuidgen')
    uuid = pipe.read().strip()
    pipe.close()
    return uuid


def generate_virtualbox_image(db, generate_virtualbox_image,
    virtualbox_path, refer_physical_machine, virtualbox_cpunum,
    virtualbox_memsize, virtualbox_vramsize, virtualbox_macaddress,
    remote_user, register_vm=True):
    vmname = generate_virtualbox_image
    logger = logging.getLogger('pxe')
    if not os.path.exists(virtualbox_path):
        os.makedirs(virtualbox_path)
    if refer_physical_machine:
        host = refer_physical_machine
        logger.info('get cpuinfo of %s' % host)
        infos = parallel_util.cpuinfos([host], arch_filter=False, verbose=
            True, ros_filter=False, username=remote_user)
        logger.info('cpuinfo of %s => %s' % (host, infos[0]))
        cpunum = infos[0][host][0]
        memsize = int(infos[0][host][1] * 0.8 / 1000)
    else:
        cpunum = virtualbox_cpunum
        memsize = virtualbox_memsize
    vramsize = virtualbox_vramsize
    if virtualbox_macaddress:
        macaddress = virtualbox_macaddress
    else:
        con = open_db(db)
        hostname = find_by_hostname(con, vmname)
        macaddress = hostname['macaddress']
        con.close()
    cd_uuid = '9f6f1044-98a6-406c-be64-eec39baef4cb'
    machine_uuid = generate_uuid()
    vm_path = os.path.join(virtualbox_path, vmname + '.vbox')
    f = open(vm_path, 'w')
    template = Template(VIRTUALBOX_XML_TEMPLATE)
    content = template.substitute({'machine_uuid': machine_uuid, 'cd_uuid':
        cd_uuid, 'hostname': vmname, 'cpunum': cpunum, 'memsize': memsize,
        'vramsize': vramsize, 'macaddress': macaddress.replace(':', '')})
    f.write(content)
    f.close()
    if register_vm:
        check_call(['VBoxManage', 'registervm', vmname + '.vbox'])


def print_virtualbox_macaddress():
    print(generate_virtualbox_macaddress())


def generate_virtualbox_macaddress():
    return '08:00:27:%02x:%02x:%02x' % (int(random.random() * 255), int(
        random.random() * 255), int(random.random() * 255))


def nslookup(ip):
    output = gethostbyaddr(ip)
    return output[0]


def print_lookup_free_host(options):
    string = lookup_free_host(options)
    print(string)


<mask token>


def auto_add_vm(options):
    logger = logging.getLogger('pxe')
    root = options.auto_add
    free_host_ip = lookup_free_host(options.db, options.dhcp_range_start,
        options.dhcp_range_stop)
    free_host = free_host_ip.split()[0]
    free_ip = free_host_ip.split()[1]
    mac_address = generate_virtualbox_macaddress()
    add_machine(free_host, mac_address, free_ip, root, options.db)
    options.overwrite_dhcp = True
    generate_dhcp(options.db, options.prefix_dhcp_file, options.subnet,
        options.netmask, options.dhcp_range_start, options.dhcp_range_stop,
        options.broadcast, options.dns_ip, options.domain_name, options.
        gateway, options.pxe_server, options.pxe_filename, options.
        overwrite_dhcp, options.dhcp_conf_file)
    options.generate_pxe_config_files = True
    generate_pxe_config_files(options.db, options.generate_pxe_config_files,
        options.tftp_dir)
    print(free_host)


def boot_vm(vmdir, vmname, physical_machine):
    logger = logging.getLogger('pxe')
    cmd0 = ['ssh', '-t', 'pxe@%s' % physical_machine, 
        "sh -c 'VBoxManage unregistervm %s || exit 0'" % vmname]
    cmd1 = ['ssh', 'pxe@%s' % physical_machine, 'mkdir -p .VirtualBox']
    cmd2 = ['scp', os.path.join(vmdir, vmname + '.vbox'), 
        'pxe@%s:.VirtualBox' % physical_machine]
    cmd3 = ['ssh', '-t', 'pxe@%s' % physical_machine, 
        'VBoxManage registervm %s' % (vmname + '.vbox')]
    cmd4 = ['ssh', 'pxe@%s' % physical_machine, 
        'screen -d -m VBoxHeadless -s %s' % vmname]
    logger.info('exec => %s' % cmd0)
    check_call(cmd0)
    logger.info('exec => %s' % cmd1)
    check_call(cmd1)
    logger.info('exec => %s' % cmd2)
    check_call(cmd2)
    logger.info('exec => %s' % cmd3)
    check_call(cmd3)
    logger.info('exec => %s' % cmd4)
    check_call(cmd4)

<mask token>


def ping_host(hostname):
    ping_ret = ping.quiet_ping(hostname, timeout=0.1)
    return ping_ret[0] == 0


def create_logger(log_file_name):
    logger = logging.getLogger('pxe')
    fh = logging.FileHandler(log_file_name)
    fh.setLevel(logging.INFO)
    ch = logging.StreamHandler()
    ch.setLevel(logging.INFO)
    fh.setFormatter(logging.Formatter(LOGGER_FILE_FORMAT))
    ch.setFormatter(logging.Formatter(LOGGER_CONSOLE_FORMAT))
    logger.addHandler(fh)
    logger.addHandler(ch)
    logger.setLevel(logging.INFO)
    return logger


def send_wol_magick_packet(macs, ipaddr, port):
    """http://www.emptypage.jp/gadgets/wol.html"""
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    for mac in macs:
        for sep in ':-':
            if sep in mac:
                mac = ''.join([x.rjust(2, '0') for x in mac.split(sep)])
                break
        mac = mac.rjust(12, '0')
        p = 'ÿ' * 6 + binascii.unhexlify(mac) * 16
        s.sendto(p, (ipaddr, port))
    s.close()


def open_db(db):
    logger = logging.getLogger('pxe')
    logger.info('opening %s as DB' % db)
    if os.path.exists(db):
        con = sqlite3.connect(db)
    else:
        con = sqlite3.connect(db)
        con.execute(DB_CREATE_TABLE_SQL)
    return con


def delete_host(con, host):
    template = Template(DEL_HOST_SQL)
    sql = template.substitute({'hostname': host})
    con.execute(sql)
    con.commit()


def add_host(con, host, ip, mac, root):
    template = Template(ADD_HOST_SQL)
    sql = template.substitute({'hostname': host, 'ip': ip, 'macaddress':
        mac, 'root': root})
    con.execute(sql)
    con.commit()


<mask token>


def find_by_hostname(con, hostname):
    template = Template(FIND_HOST_SQL)
    sql_str = template.substitute({'hostname': hostname})
    sql_result = con.execute(sql_str)
    for row in sql_result:
        return {'ip': row[1], 'macaddress': row[2], 'root': row[3]}
    raise ('cannot find %s' % hostname)


def delete_machine(hostname, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info('deleteing a machine:: %s' % hostname)
    delete_host(con, hostname)
    con.close()


def add_machine(hostname, mac, ip, root, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info(
        'adding a machine:: hostname - %s, mac - %s, ip - %s, root - %s' %
        (hostname, mac, ip, root))
    add_host(con, hostname, ip, mac, root)
    con.close()


def generate_dhcp(db, prefix_dhcp_file, subnet, netmask, dhcp_range_start,
    dhcp_range_stop, broadcast, dns_ip, domain_name, gateway, pxe_server,
    pxe_filename, overwrite_dhcp, dhcp_conf_file, restart_dhcp):
    logger = logging.getLogger('pxe')
    logger.info('generating dhcpd.conf')
    con = open_db(db)
    machines = all_hosts(con)
    generated_string = []
    for hostname, ip_mac in machines.items():
        ip = ip_mac['ip']
        mac = ip_mac['macaddress']
        template = Template(DHCP_HOST_TMPL)
        host_str = template.substitute({'hostname': hostname, 'ip': ip,
            'mac': mac})
        generated_string.append(host_str)
    if prefix_dhcp_file:
        logger.info('adding %s as prefix' % prefix_dhcp_file)
        f = open(prefix_dhcp_file)
        prefix_str = ''.join(f.readlines())
    else:
        prefix_str = ''
    dhcp_subnet_tmpl = Template(DHCP_SUBNET_TMPL)
    replace_dict = {'subnet': subnet, 'netmask': netmask,
        'dhcp_range_start': dhcp_range_start, 'dhcp_range_stop':
        dhcp_range_stop, 'broadcast': broadcast, 'dns_ip': dns_ip,
        'domain_name': domain_name, 'gateway': gateway, 'pxe_server':
        pxe_server, 'pxe_filename': pxe_filename, 'hosts': '\n'.join(
        generated_string)}
    dhcp_subnet_str = dhcp_subnet_tmpl.substitute(replace_dict)
    if overwrite_dhcp:
        logger.info('overwriting %s' % dhcp_conf_file)
        path = dhcp_conf_file
        f = open(path, 'w')
        f.write(prefix_str + dhcp_subnet_str)
        f.close()
        if restart_dhcp:
            logger.info('restarting dhcp')
            check_call(['sudo', '/etc/init.d/dhcp3-server', 'restart'])
    else:
        print(prefix_str + dhcp_subnet_str)


<mask token>


def wake_on_lan(hostname, port, broadcast, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info('wake on lan %s' % hostname)
    mac = find_by_hostname(con, hostname)['macaddress']
    send_wol_magick_packet([mac], broadcast, port)


def chroot_command(chroot_dir, *args):
    command = ['sudo', 'chroot', chroot_dir]
    command.extend(*args)
    return check_call(command)


<mask token>


def copy_template_filesystem(template_dir, target_dir, apt_sources):
    logger = logging.getLogger('pxe')
    logger.info('copying filesystem')
    check_call(['sudo', 'cp', '-ax', template_dir, target_dir])
    logger.info('copying etc/apt/sources.list')
    check_call(['sudo', 'cp', apt_sources, os.path.join(target_dir, 'etc',
        'apt', 'sources.list')])
    return


class ChrootEnvironment:

    def __init__(self, target_dir):
        self.target_dir = target_dir

    def __enter__(self):
        target_dir = self.target_dir
        check_call(['sudo', 'mount', '-o', 'bind', '/dev/', os.path.join(
            target_dir, 'dev')])
        chroot_command(target_dir, 'mount -t proc none /proc'.split())
        chroot_command(target_dir, 'mount -t sysfs none /sys'.split())
        chroot_command(target_dir, 'mount -t devpts none /dev/pts'.split())

    def __exit__(self, type, value, traceback):
        target_dir = self.target_dir
        chroot_command(target_dir, 'umount -lf /proc'.split())
        chroot_command(target_dir, 'umount -lf /sys'.split())
        chroot_command(target_dir, 'umount -lf /dev/pts'.split())
        check_call(['sudo', 'umount', '-lf', os.path.join(target_dir, 'dev')])


def install_apt_packages(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('installing base apt packages')
        chroot_command(target_dir, ['apt-get', 'update'])
        chroot_command(target_dir, ['apt-get', 'install', 'wget'])
        chroot_command(target_dir, ['sh', '-c',
            'wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | apt-key add -'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | apt-key add -'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget https://www.ubuntulinux.jp/sources.list.d/lucid.list -O /etc/apt/sources.list.d/ubuntu-ja.list'
            ])
        chroot_command(target_dir, ['apt-get', 'update'])
        chroot_command(target_dir, ['apt-get', 'install', '--force-yes',
            '-y'] + APT_PACKAGES.split())
        logger.info('installing ros and openrave apt packages')
        chroot_command(target_dir, ['sh', '-c',
            'echo deb http://packages.ros.org/ros/ubuntu lucid main > /etc/apt/sources.list.d/ros-latest.list'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget http://packages.ros.org/ros.key -O - | apt-key add -'])
        chroot_command(target_dir, ['add-apt-repository',
            'ppa:openrave/release'])
        chroot_command(target_dir, ['apt-get', 'update'])


def setup_user(target_dir, user, passwd):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('adding pxe user')
        chroot_command(target_dir, ['sh', '-c', 
            'id %s || useradd %s -g sudo -s /bin/bash' % (user, user)])
        chroot_command(target_dir, ['sh', '-c', 'echo %s:%s | chpasswd' % (
            user, passwd)])
        chroot_command(target_dir, ['rm', '-f', '/etc/hostname'])


def update_initram(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('updating initram.img')
        chroot_command(target_dir, ['sh', '-c', 
            "echo '%s' > /etc/initramfs-tools/initramfs.conf" % INITRAMFS_CONF]
            )
        chroot_command(target_dir, ['sh', '-c', "echo '%s' > /etc/fstab" %
            FSTAB])
        chroot_command(target_dir, ['sh', '-c', 
            "echo '%s' > /etc/initramfs-tools/modules" % INITRAM_MODULES])
        chroot_command(target_dir, ['sh', '-c',
            'mkinitramfs -o /boot/initrd.img-`uname -r` `ls /lib/modules`'])


<mask token>


def generate_top_html(db, log):
    con = open_db(db)
    machines = all_hosts(con)
    host_strs = []
    hosts = sorted(machines.keys())
    for hostname in hosts:
        ip_mac = machines[hostname]
        ip = ip_mac['ip']
        mac = ip_mac['macaddress']
        root = ip_mac['root']
        alivep = ping_host(ip)
        if alivep:
            template = Template(HTML_ALIVE_HOST_TMPL)
        else:
            template = Template(HTML_DEAD_HOST_TMPL)
        host_strs.append(template.substitute({'hostname': hostname, 'ip':
            ip, 'macaddress': mac, 'root': root}))
    con.close()
    f = open(log)
    log_lines = f.readlines()[-1000:]
    log_strs = []
    for line in log_lines:
        log_strs.append('<p>' + xml.sax.saxutils.escape(line) + '</p>')
    log_strs.reverse()
    f.close()
    html_template = Template(HTML_TMPL)
    return html_template.substitute({'hosts': '\n'.join(host_strs), 'log':
        '\n'.join(log_strs)})


def update_dhcp_from_web():
    if global_options.overwrite_dhcp:
        logger = logging.getLogger('pxe')
        generate_dhcp(global_options.db, global_options.prefix_dhcp_file,
            global_options.subnet, global_options.netmask, global_options.
            dhcp_range_start, global_options.dhcp_range_stop,
            global_options.broadcast, global_options.dns_ip, global_options
            .domain_name, global_options.gateway, global_options.pxe_server,
            global_options.pxe_filename, global_options.overwrite_dhcp,
            global_options.dhcp_conf_file)
    if global_options.generate_pxe_config_files:
        generate_pxe_config_files(global_options.db, global_options.
            generate_pxe_config_files, global_options.tftp_dir)
    check_call(['sudo', '/etc/init.d/dhcp3-server', 'restart'])


def generate_error_html(e, host, port):
    return ERROR_HTML_TMPL % ('http://' + host + ':' + str(port), e)


class WebHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(s):
        s.send_response(200)
        s.send_header('Content-type', 'text/html')
        s.end_headers()
        try:
            if s.path.startswith('/delete'):
                delete_host = cgi.parse_qs(s.path.split('?')[1]).keys()[0]
                delete_machine(delete_host, db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/add'):
                add_host_desc = cgi.parse_qs(s.path.split('?')[1])
                add_machine(add_host_desc['hostname'][0], add_host_desc[
                    'macaddress'][0], add_host_desc['ip'][0], add_host_desc
                    ['root'][0], db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/auto_add'):
                add_host_desc = cgi.parse_qs(s.path.split('?')[1])
                root_dir = add_host_desc['root'][0]
                try:
                    global_options.auto_add = root_dir
                    auto_add_vm(global_options)
                finally:
                    global_options.auto_add = None
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/boot'):
                logger = logging.getLogger('pxe')
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
                params = cgi.parse_qs(s.path.split('?')[1])
                vmname = params['vmname'][0]
                physc = params['physical'][0]
                logger.info('invoke %s @ %s' % (vmname, physc))
                try:
                    global_options.generate_virtualbox_image = vmname
                    global_options.refer_physical_machine = physc
                    generate_virtualbox_image(global_options.db,
                        global_options.generate_virtualbox_image,
                        global_options.virtualbox_path, global_options.
                        refer_physical_machine, global_options.
                        virtualbox_cpunum, global_options.
                        virtualbox_memsize, global_options.
                        virtualbox_vramsize, global_options.
                        virtualbox_macaddress, global_options.remote_user, 
                        False)
                finally:
                    global_options.generate_virtualbox_image = None
                    global_options.refer_physical_machine = None
                boot_vm(global_options.virtualbox_path, vmname, physc)
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            else:
                html = generate_top_html(db_name, global_options.log)
            s.wfile.write(html)
        except Exception as e:
            logger = logging.getLogger('pxe')
            logger.info('error has occurred: %s' % e)
            html = generate_error_html(e, global_options.web_hostname,
                global_options.web_port)
            s.wfile.write(html)


def run_web(options):
    db = options.db
    port = options.web_port
    overwritep = options.overwrite_dhcp
    global db_name
    global global_options
    db_name = db
    global_options = options
    BaseHTTPServer.HTTPServer((options.web_hostname, port), WebHandler
        ).serve_forever()


def generate_pxe_config_files(db, generate_pxe_config_files, tftp_dir):
    if generate_pxe_config_files:
        logger = logging.getLogger('pxe')
        logger.info('generate the pxe configuration files')
        con = open_db(db)
        machines = all_hosts(con)
        for hostname in machines.keys():
            template = Template(PXE_CONFIG_TMPL)
            mac = machines[hostname]['macaddress']
            file_name = os.path.join(tftp_dir, 'pxelinux.cfg', '01-' + mac.
                replace(':', '-'))
            file_str = template.substitute({'hostname': hostname, 'root':
                machines[hostname]['root'], 'tftp_dir': tftp_dir})
            f = open(file_name, 'w')
            f.write(file_str)
            f.close()
            check_call(['sudo', 'chown', 'pxe.tftp', file_name])
            check_call(['sudo', 'chmod', 'g+rw', file_name])
            check_call(['sudo', 'chmod', 'u+rw', file_name])


def generate_uuid():
    pipe = os.popen('uuidgen')
    uuid = pipe.read().strip()
    pipe.close()
    return uuid


def generate_virtualbox_image(db, generate_virtualbox_image,
    virtualbox_path, refer_physical_machine, virtualbox_cpunum,
    virtualbox_memsize, virtualbox_vramsize, virtualbox_macaddress,
    remote_user, register_vm=True):
    vmname = generate_virtualbox_image
    logger = logging.getLogger('pxe')
    if not os.path.exists(virtualbox_path):
        os.makedirs(virtualbox_path)
    if refer_physical_machine:
        host = refer_physical_machine
        logger.info('get cpuinfo of %s' % host)
        infos = parallel_util.cpuinfos([host], arch_filter=False, verbose=
            True, ros_filter=False, username=remote_user)
        logger.info('cpuinfo of %s => %s' % (host, infos[0]))
        cpunum = infos[0][host][0]
        memsize = int(infos[0][host][1] * 0.8 / 1000)
    else:
        cpunum = virtualbox_cpunum
        memsize = virtualbox_memsize
    vramsize = virtualbox_vramsize
    if virtualbox_macaddress:
        macaddress = virtualbox_macaddress
    else:
        con = open_db(db)
        hostname = find_by_hostname(con, vmname)
        macaddress = hostname['macaddress']
        con.close()
    cd_uuid = '9f6f1044-98a6-406c-be64-eec39baef4cb'
    machine_uuid = generate_uuid()
    vm_path = os.path.join(virtualbox_path, vmname + '.vbox')
    f = open(vm_path, 'w')
    template = Template(VIRTUALBOX_XML_TEMPLATE)
    content = template.substitute({'machine_uuid': machine_uuid, 'cd_uuid':
        cd_uuid, 'hostname': vmname, 'cpunum': cpunum, 'memsize': memsize,
        'vramsize': vramsize, 'macaddress': macaddress.replace(':', '')})
    f.write(content)
    f.close()
    if register_vm:
        check_call(['VBoxManage', 'registervm', vmname + '.vbox'])


def print_virtualbox_macaddress():
    print(generate_virtualbox_macaddress())


def generate_virtualbox_macaddress():
    return '08:00:27:%02x:%02x:%02x' % (int(random.random() * 255), int(
        random.random() * 255), int(random.random() * 255))


def nslookup(ip):
    output = gethostbyaddr(ip)
    return output[0]


def print_lookup_free_host(options):
    string = lookup_free_host(options)
    print(string)


<mask token>


def auto_add_vm(options):
    logger = logging.getLogger('pxe')
    root = options.auto_add
    free_host_ip = lookup_free_host(options.db, options.dhcp_range_start,
        options.dhcp_range_stop)
    free_host = free_host_ip.split()[0]
    free_ip = free_host_ip.split()[1]
    mac_address = generate_virtualbox_macaddress()
    add_machine(free_host, mac_address, free_ip, root, options.db)
    options.overwrite_dhcp = True
    generate_dhcp(options.db, options.prefix_dhcp_file, options.subnet,
        options.netmask, options.dhcp_range_start, options.dhcp_range_stop,
        options.broadcast, options.dns_ip, options.domain_name, options.
        gateway, options.pxe_server, options.pxe_filename, options.
        overwrite_dhcp, options.dhcp_conf_file)
    options.generate_pxe_config_files = True
    generate_pxe_config_files(options.db, options.generate_pxe_config_files,
        options.tftp_dir)
    print(free_host)


def boot_vm(vmdir, vmname, physical_machine):
    logger = logging.getLogger('pxe')
    cmd0 = ['ssh', '-t', 'pxe@%s' % physical_machine, 
        "sh -c 'VBoxManage unregistervm %s || exit 0'" % vmname]
    cmd1 = ['ssh', 'pxe@%s' % physical_machine, 'mkdir -p .VirtualBox']
    cmd2 = ['scp', os.path.join(vmdir, vmname + '.vbox'), 
        'pxe@%s:.VirtualBox' % physical_machine]
    cmd3 = ['ssh', '-t', 'pxe@%s' % physical_machine, 
        'VBoxManage registervm %s' % (vmname + '.vbox')]
    cmd4 = ['ssh', 'pxe@%s' % physical_machine, 
        'screen -d -m VBoxHeadless -s %s' % vmname]
    logger.info('exec => %s' % cmd0)
    check_call(cmd0)
    logger.info('exec => %s' % cmd1)
    check_call(cmd1)
    logger.info('exec => %s' % cmd2)
    check_call(cmd2)
    logger.info('exec => %s' % cmd3)
    check_call(cmd3)
    logger.info('exec => %s' % cmd4)
    check_call(cmd4)

<mask token>


def ping_host(hostname):
    ping_ret = ping.quiet_ping(hostname, timeout=0.1)
    return ping_ret[0] == 0


def create_logger(log_file_name):
    logger = logging.getLogger('pxe')
    fh = logging.FileHandler(log_file_name)
    fh.setLevel(logging.INFO)
    ch = logging.StreamHandler()
    ch.setLevel(logging.INFO)
    fh.setFormatter(logging.Formatter(LOGGER_FILE_FORMAT))
    ch.setFormatter(logging.Formatter(LOGGER_CONSOLE_FORMAT))
    logger.addHandler(fh)
    logger.addHandler(ch)
    logger.setLevel(logging.INFO)
    return logger


def send_wol_magick_packet(macs, ipaddr, port):
    """http://www.emptypage.jp/gadgets/wol.html"""
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    for mac in macs:
        for sep in ':-':
            if sep in mac:
                mac = ''.join([x.rjust(2, '0') for x in mac.split(sep)])
                break
        mac = mac.rjust(12, '0')
        p = 'ÿ' * 6 + binascii.unhexlify(mac) * 16
        s.sendto(p, (ipaddr, port))
    s.close()


def open_db(db):
    logger = logging.getLogger('pxe')
    logger.info('opening %s as DB' % db)
    if os.path.exists(db):
        con = sqlite3.connect(db)
    else:
        con = sqlite3.connect(db)
        con.execute(DB_CREATE_TABLE_SQL)
    return con


def delete_host(con, host):
    template = Template(DEL_HOST_SQL)
    sql = template.substitute({'hostname': host})
    con.execute(sql)
    con.commit()


def add_host(con, host, ip, mac, root):
    template = Template(ADD_HOST_SQL)
    sql = template.substitute({'hostname': host, 'ip': ip, 'macaddress':
        mac, 'root': root})
    con.execute(sql)
    con.commit()


<mask token>


def find_by_hostname(con, hostname):
    template = Template(FIND_HOST_SQL)
    sql_str = template.substitute({'hostname': hostname})
    sql_result = con.execute(sql_str)
    for row in sql_result:
        return {'ip': row[1], 'macaddress': row[2], 'root': row[3]}
    raise ('cannot find %s' % hostname)


def delete_machine(hostname, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info('deleteing a machine:: %s' % hostname)
    delete_host(con, hostname)
    con.close()


def add_machine(hostname, mac, ip, root, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info(
        'adding a machine:: hostname - %s, mac - %s, ip - %s, root - %s' %
        (hostname, mac, ip, root))
    add_host(con, hostname, ip, mac, root)
    con.close()


def generate_dhcp(db, prefix_dhcp_file, subnet, netmask, dhcp_range_start,
    dhcp_range_stop, broadcast, dns_ip, domain_name, gateway, pxe_server,
    pxe_filename, overwrite_dhcp, dhcp_conf_file, restart_dhcp):
    logger = logging.getLogger('pxe')
    logger.info('generating dhcpd.conf')
    con = open_db(db)
    machines = all_hosts(con)
    generated_string = []
    for hostname, ip_mac in machines.items():
        ip = ip_mac['ip']
        mac = ip_mac['macaddress']
        template = Template(DHCP_HOST_TMPL)
        host_str = template.substitute({'hostname': hostname, 'ip': ip,
            'mac': mac})
        generated_string.append(host_str)
    if prefix_dhcp_file:
        logger.info('adding %s as prefix' % prefix_dhcp_file)
        f = open(prefix_dhcp_file)
        prefix_str = ''.join(f.readlines())
    else:
        prefix_str = ''
    dhcp_subnet_tmpl = Template(DHCP_SUBNET_TMPL)
    replace_dict = {'subnet': subnet, 'netmask': netmask,
        'dhcp_range_start': dhcp_range_start, 'dhcp_range_stop':
        dhcp_range_stop, 'broadcast': broadcast, 'dns_ip': dns_ip,
        'domain_name': domain_name, 'gateway': gateway, 'pxe_server':
        pxe_server, 'pxe_filename': pxe_filename, 'hosts': '\n'.join(
        generated_string)}
    dhcp_subnet_str = dhcp_subnet_tmpl.substitute(replace_dict)
    if overwrite_dhcp:
        logger.info('overwriting %s' % dhcp_conf_file)
        path = dhcp_conf_file
        f = open(path, 'w')
        f.write(prefix_str + dhcp_subnet_str)
        f.close()
        if restart_dhcp:
            logger.info('restarting dhcp')
            check_call(['sudo', '/etc/init.d/dhcp3-server', 'restart'])
    else:
        print(prefix_str + dhcp_subnet_str)


def print_machine_list(db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    machines = all_hosts(con)
    for hostname, ip_mac in machines.items():
        ip = ip_mac['ip']
        mac = ip_mac['macaddress']
        root = ip_mac['root']
        print('%s:\n  ip: %s\n  mac: %s\n  root: %s\n' % (hostname, ip, mac,
            root))


def wake_on_lan(hostname, port, broadcast, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info('wake on lan %s' % hostname)
    mac = find_by_hostname(con, hostname)['macaddress']
    send_wol_magick_packet([mac], broadcast, port)


def chroot_command(chroot_dir, *args):
    command = ['sudo', 'chroot', chroot_dir]
    command.extend(*args)
    return check_call(command)


<mask token>


def copy_template_filesystem(template_dir, target_dir, apt_sources):
    logger = logging.getLogger('pxe')
    logger.info('copying filesystem')
    check_call(['sudo', 'cp', '-ax', template_dir, target_dir])
    logger.info('copying etc/apt/sources.list')
    check_call(['sudo', 'cp', apt_sources, os.path.join(target_dir, 'etc',
        'apt', 'sources.list')])
    return


class ChrootEnvironment:

    def __init__(self, target_dir):
        self.target_dir = target_dir

    def __enter__(self):
        target_dir = self.target_dir
        check_call(['sudo', 'mount', '-o', 'bind', '/dev/', os.path.join(
            target_dir, 'dev')])
        chroot_command(target_dir, 'mount -t proc none /proc'.split())
        chroot_command(target_dir, 'mount -t sysfs none /sys'.split())
        chroot_command(target_dir, 'mount -t devpts none /dev/pts'.split())

    def __exit__(self, type, value, traceback):
        target_dir = self.target_dir
        chroot_command(target_dir, 'umount -lf /proc'.split())
        chroot_command(target_dir, 'umount -lf /sys'.split())
        chroot_command(target_dir, 'umount -lf /dev/pts'.split())
        check_call(['sudo', 'umount', '-lf', os.path.join(target_dir, 'dev')])


def install_apt_packages(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('installing base apt packages')
        chroot_command(target_dir, ['apt-get', 'update'])
        chroot_command(target_dir, ['apt-get', 'install', 'wget'])
        chroot_command(target_dir, ['sh', '-c',
            'wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | apt-key add -'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | apt-key add -'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget https://www.ubuntulinux.jp/sources.list.d/lucid.list -O /etc/apt/sources.list.d/ubuntu-ja.list'
            ])
        chroot_command(target_dir, ['apt-get', 'update'])
        chroot_command(target_dir, ['apt-get', 'install', '--force-yes',
            '-y'] + APT_PACKAGES.split())
        logger.info('installing ros and openrave apt packages')
        chroot_command(target_dir, ['sh', '-c',
            'echo deb http://packages.ros.org/ros/ubuntu lucid main > /etc/apt/sources.list.d/ros-latest.list'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget http://packages.ros.org/ros.key -O - | apt-key add -'])
        chroot_command(target_dir, ['add-apt-repository',
            'ppa:openrave/release'])
        chroot_command(target_dir, ['apt-get', 'update'])


def setup_user(target_dir, user, passwd):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('adding pxe user')
        chroot_command(target_dir, ['sh', '-c', 
            'id %s || useradd %s -g sudo -s /bin/bash' % (user, user)])
        chroot_command(target_dir, ['sh', '-c', 'echo %s:%s | chpasswd' % (
            user, passwd)])
        chroot_command(target_dir, ['rm', '-f', '/etc/hostname'])


def update_initram(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('updating initram.img')
        chroot_command(target_dir, ['sh', '-c', 
            "echo '%s' > /etc/initramfs-tools/initramfs.conf" % INITRAMFS_CONF]
            )
        chroot_command(target_dir, ['sh', '-c', "echo '%s' > /etc/fstab" %
            FSTAB])
        chroot_command(target_dir, ['sh', '-c', 
            "echo '%s' > /etc/initramfs-tools/modules" % INITRAM_MODULES])
        chroot_command(target_dir, ['sh', '-c',
            'mkinitramfs -o /boot/initrd.img-`uname -r` `ls /lib/modules`'])


<mask token>


def generate_top_html(db, log):
    con = open_db(db)
    machines = all_hosts(con)
    host_strs = []
    hosts = sorted(machines.keys())
    for hostname in hosts:
        ip_mac = machines[hostname]
        ip = ip_mac['ip']
        mac = ip_mac['macaddress']
        root = ip_mac['root']
        alivep = ping_host(ip)
        if alivep:
            template = Template(HTML_ALIVE_HOST_TMPL)
        else:
            template = Template(HTML_DEAD_HOST_TMPL)
        host_strs.append(template.substitute({'hostname': hostname, 'ip':
            ip, 'macaddress': mac, 'root': root}))
    con.close()
    f = open(log)
    log_lines = f.readlines()[-1000:]
    log_strs = []
    for line in log_lines:
        log_strs.append('<p>' + xml.sax.saxutils.escape(line) + '</p>')
    log_strs.reverse()
    f.close()
    html_template = Template(HTML_TMPL)
    return html_template.substitute({'hosts': '\n'.join(host_strs), 'log':
        '\n'.join(log_strs)})


def update_dhcp_from_web():
    if global_options.overwrite_dhcp:
        logger = logging.getLogger('pxe')
        generate_dhcp(global_options.db, global_options.prefix_dhcp_file,
            global_options.subnet, global_options.netmask, global_options.
            dhcp_range_start, global_options.dhcp_range_stop,
            global_options.broadcast, global_options.dns_ip, global_options
            .domain_name, global_options.gateway, global_options.pxe_server,
            global_options.pxe_filename, global_options.overwrite_dhcp,
            global_options.dhcp_conf_file)
    if global_options.generate_pxe_config_files:
        generate_pxe_config_files(global_options.db, global_options.
            generate_pxe_config_files, global_options.tftp_dir)
    check_call(['sudo', '/etc/init.d/dhcp3-server', 'restart'])


def generate_error_html(e, host, port):
    return ERROR_HTML_TMPL % ('http://' + host + ':' + str(port), e)


class WebHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(s):
        s.send_response(200)
        s.send_header('Content-type', 'text/html')
        s.end_headers()
        try:
            if s.path.startswith('/delete'):
                delete_host = cgi.parse_qs(s.path.split('?')[1]).keys()[0]
                delete_machine(delete_host, db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/add'):
                add_host_desc = cgi.parse_qs(s.path.split('?')[1])
                add_machine(add_host_desc['hostname'][0], add_host_desc[
                    'macaddress'][0], add_host_desc['ip'][0], add_host_desc
                    ['root'][0], db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/auto_add'):
                add_host_desc = cgi.parse_qs(s.path.split('?')[1])
                root_dir = add_host_desc['root'][0]
                try:
                    global_options.auto_add = root_dir
                    auto_add_vm(global_options)
                finally:
                    global_options.auto_add = None
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/boot'):
                logger = logging.getLogger('pxe')
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
                params = cgi.parse_qs(s.path.split('?')[1])
                vmname = params['vmname'][0]
                physc = params['physical'][0]
                logger.info('invoke %s @ %s' % (vmname, physc))
                try:
                    global_options.generate_virtualbox_image = vmname
                    global_options.refer_physical_machine = physc
                    generate_virtualbox_image(global_options.db,
                        global_options.generate_virtualbox_image,
                        global_options.virtualbox_path, global_options.
                        refer_physical_machine, global_options.
                        virtualbox_cpunum, global_options.
                        virtualbox_memsize, global_options.
                        virtualbox_vramsize, global_options.
                        virtualbox_macaddress, global_options.remote_user, 
                        False)
                finally:
                    global_options.generate_virtualbox_image = None
                    global_options.refer_physical_machine = None
                boot_vm(global_options.virtualbox_path, vmname, physc)
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            else:
                html = generate_top_html(db_name, global_options.log)
            s.wfile.write(html)
        except Exception as e:
            logger = logging.getLogger('pxe')
            logger.info('error has occurred: %s' % e)
            html = generate_error_html(e, global_options.web_hostname,
                global_options.web_port)
            s.wfile.write(html)


def run_web(options):
    db = options.db
    port = options.web_port
    overwritep = options.overwrite_dhcp
    global db_name
    global global_options
    db_name = db
    global_options = options
    BaseHTTPServer.HTTPServer((options.web_hostname, port), WebHandler
        ).serve_forever()


def generate_pxe_config_files(db, generate_pxe_config_files, tftp_dir):
    if generate_pxe_config_files:
        logger = logging.getLogger('pxe')
        logger.info('generate the pxe configuration files')
        con = open_db(db)
        machines = all_hosts(con)
        for hostname in machines.keys():
            template = Template(PXE_CONFIG_TMPL)
            mac = machines[hostname]['macaddress']
            file_name = os.path.join(tftp_dir, 'pxelinux.cfg', '01-' + mac.
                replace(':', '-'))
            file_str = template.substitute({'hostname': hostname, 'root':
                machines[hostname]['root'], 'tftp_dir': tftp_dir})
            f = open(file_name, 'w')
            f.write(file_str)
            f.close()
            check_call(['sudo', 'chown', 'pxe.tftp', file_name])
            check_call(['sudo', 'chmod', 'g+rw', file_name])
            check_call(['sudo', 'chmod', 'u+rw', file_name])


def generate_uuid():
    pipe = os.popen('uuidgen')
    uuid = pipe.read().strip()
    pipe.close()
    return uuid


def generate_virtualbox_image(db, generate_virtualbox_image,
    virtualbox_path, refer_physical_machine, virtualbox_cpunum,
    virtualbox_memsize, virtualbox_vramsize, virtualbox_macaddress,
    remote_user, register_vm=True):
    vmname = generate_virtualbox_image
    logger = logging.getLogger('pxe')
    if not os.path.exists(virtualbox_path):
        os.makedirs(virtualbox_path)
    if refer_physical_machine:
        host = refer_physical_machine
        logger.info('get cpuinfo of %s' % host)
        infos = parallel_util.cpuinfos([host], arch_filter=False, verbose=
            True, ros_filter=False, username=remote_user)
        logger.info('cpuinfo of %s => %s' % (host, infos[0]))
        cpunum = infos[0][host][0]
        memsize = int(infos[0][host][1] * 0.8 / 1000)
    else:
        cpunum = virtualbox_cpunum
        memsize = virtualbox_memsize
    vramsize = virtualbox_vramsize
    if virtualbox_macaddress:
        macaddress = virtualbox_macaddress
    else:
        con = open_db(db)
        hostname = find_by_hostname(con, vmname)
        macaddress = hostname['macaddress']
        con.close()
    cd_uuid = '9f6f1044-98a6-406c-be64-eec39baef4cb'
    machine_uuid = generate_uuid()
    vm_path = os.path.join(virtualbox_path, vmname + '.vbox')
    f = open(vm_path, 'w')
    template = Template(VIRTUALBOX_XML_TEMPLATE)
    content = template.substitute({'machine_uuid': machine_uuid, 'cd_uuid':
        cd_uuid, 'hostname': vmname, 'cpunum': cpunum, 'memsize': memsize,
        'vramsize': vramsize, 'macaddress': macaddress.replace(':', '')})
    f.write(content)
    f.close()
    if register_vm:
        check_call(['VBoxManage', 'registervm', vmname + '.vbox'])


def print_virtualbox_macaddress():
    print(generate_virtualbox_macaddress())


def generate_virtualbox_macaddress():
    return '08:00:27:%02x:%02x:%02x' % (int(random.random() * 255), int(
        random.random() * 255), int(random.random() * 255))


def nslookup(ip):
    output = gethostbyaddr(ip)
    return output[0]


def print_lookup_free_host(options):
    string = lookup_free_host(options)
    print(string)


<mask token>


def auto_add_vm(options):
    logger = logging.getLogger('pxe')
    root = options.auto_add
    free_host_ip = lookup_free_host(options.db, options.dhcp_range_start,
        options.dhcp_range_stop)
    free_host = free_host_ip.split()[0]
    free_ip = free_host_ip.split()[1]
    mac_address = generate_virtualbox_macaddress()
    add_machine(free_host, mac_address, free_ip, root, options.db)
    options.overwrite_dhcp = True
    generate_dhcp(options.db, options.prefix_dhcp_file, options.subnet,
        options.netmask, options.dhcp_range_start, options.dhcp_range_stop,
        options.broadcast, options.dns_ip, options.domain_name, options.
        gateway, options.pxe_server, options.pxe_filename, options.
        overwrite_dhcp, options.dhcp_conf_file)
    options.generate_pxe_config_files = True
    generate_pxe_config_files(options.db, options.generate_pxe_config_files,
        options.tftp_dir)
    print(free_host)


def boot_vm(vmdir, vmname, physical_machine):
    logger = logging.getLogger('pxe')
    cmd0 = ['ssh', '-t', 'pxe@%s' % physical_machine, 
        "sh -c 'VBoxManage unregistervm %s || exit 0'" % vmname]
    cmd1 = ['ssh', 'pxe@%s' % physical_machine, 'mkdir -p .VirtualBox']
    cmd2 = ['scp', os.path.join(vmdir, vmname + '.vbox'), 
        'pxe@%s:.VirtualBox' % physical_machine]
    cmd3 = ['ssh', '-t', 'pxe@%s' % physical_machine, 
        'VBoxManage registervm %s' % (vmname + '.vbox')]
    cmd4 = ['ssh', 'pxe@%s' % physical_machine, 
        'screen -d -m VBoxHeadless -s %s' % vmname]
    logger.info('exec => %s' % cmd0)
    check_call(cmd0)
    logger.info('exec => %s' % cmd1)
    check_call(cmd1)
    logger.info('exec => %s' % cmd2)
    check_call(cmd2)
    logger.info('exec => %s' % cmd3)
    check_call(cmd3)
    logger.info('exec => %s' % cmd4)
    check_call(cmd4)

<mask token>


def ping_host(hostname):
    ping_ret = ping.quiet_ping(hostname, timeout=0.1)
    return ping_ret[0] == 0


def create_logger(log_file_name):
    logger = logging.getLogger('pxe')
    fh = logging.FileHandler(log_file_name)
    fh.setLevel(logging.INFO)
    ch = logging.StreamHandler()
    ch.setLevel(logging.INFO)
    fh.setFormatter(logging.Formatter(LOGGER_FILE_FORMAT))
    ch.setFormatter(logging.Formatter(LOGGER_CONSOLE_FORMAT))
    logger.addHandler(fh)
    logger.addHandler(ch)
    logger.setLevel(logging.INFO)
    return logger


def send_wol_magick_packet(macs, ipaddr, port):
    """http://www.emptypage.jp/gadgets/wol.html"""
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    for mac in macs:
        for sep in ':-':
            if sep in mac:
                mac = ''.join([x.rjust(2, '0') for x in mac.split(sep)])
                break
        mac = mac.rjust(12, '0')
        p = 'ÿ' * 6 + binascii.unhexlify(mac) * 16
        s.sendto(p, (ipaddr, port))
    s.close()


def open_db(db):
    logger = logging.getLogger('pxe')
    logger.info('opening %s as DB' % db)
    if os.path.exists(db):
        con = sqlite3.connect(db)
    else:
        con = sqlite3.connect(db)
        con.execute(DB_CREATE_TABLE_SQL)
    return con


def delete_host(con, host):
    template = Template(DEL_HOST_SQL)
    sql = template.substitute({'hostname': host})
    con.execute(sql)
    con.commit()


def add_host(con, host, ip, mac, root):
    template = Template(ADD_HOST_SQL)
    sql = template.substitute({'hostname': host, 'ip': ip, 'macaddress':
        mac, 'root': root})
    con.execute(sql)
    con.commit()


<mask token>


def find_by_ip(con, ip):
    template = Template(FIND_IP_SQL)
    sql_str = template.substitute({'ip': ip})
    sql_result = con.execute(sql_str)
    for row in sql_result:
        return {'ip': row[1], 'macaddress': row[2], 'root': row[3]}
    return False


def find_by_hostname(con, hostname):
    template = Template(FIND_HOST_SQL)
    sql_str = template.substitute({'hostname': hostname})
    sql_result = con.execute(sql_str)
    for row in sql_result:
        return {'ip': row[1], 'macaddress': row[2], 'root': row[3]}
    raise ('cannot find %s' % hostname)


def delete_machine(hostname, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info('deleteing a machine:: %s' % hostname)
    delete_host(con, hostname)
    con.close()


def add_machine(hostname, mac, ip, root, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info(
        'adding a machine:: hostname - %s, mac - %s, ip - %s, root - %s' %
        (hostname, mac, ip, root))
    add_host(con, hostname, ip, mac, root)
    con.close()


def generate_dhcp(db, prefix_dhcp_file, subnet, netmask, dhcp_range_start,
    dhcp_range_stop, broadcast, dns_ip, domain_name, gateway, pxe_server,
    pxe_filename, overwrite_dhcp, dhcp_conf_file, restart_dhcp):
    logger = logging.getLogger('pxe')
    logger.info('generating dhcpd.conf')
    con = open_db(db)
    machines = all_hosts(con)
    generated_string = []
    for hostname, ip_mac in machines.items():
        ip = ip_mac['ip']
        mac = ip_mac['macaddress']
        template = Template(DHCP_HOST_TMPL)
        host_str = template.substitute({'hostname': hostname, 'ip': ip,
            'mac': mac})
        generated_string.append(host_str)
    if prefix_dhcp_file:
        logger.info('adding %s as prefix' % prefix_dhcp_file)
        f = open(prefix_dhcp_file)
        prefix_str = ''.join(f.readlines())
    else:
        prefix_str = ''
    dhcp_subnet_tmpl = Template(DHCP_SUBNET_TMPL)
    replace_dict = {'subnet': subnet, 'netmask': netmask,
        'dhcp_range_start': dhcp_range_start, 'dhcp_range_stop':
        dhcp_range_stop, 'broadcast': broadcast, 'dns_ip': dns_ip,
        'domain_name': domain_name, 'gateway': gateway, 'pxe_server':
        pxe_server, 'pxe_filename': pxe_filename, 'hosts': '\n'.join(
        generated_string)}
    dhcp_subnet_str = dhcp_subnet_tmpl.substitute(replace_dict)
    if overwrite_dhcp:
        logger.info('overwriting %s' % dhcp_conf_file)
        path = dhcp_conf_file
        f = open(path, 'w')
        f.write(prefix_str + dhcp_subnet_str)
        f.close()
        if restart_dhcp:
            logger.info('restarting dhcp')
            check_call(['sudo', '/etc/init.d/dhcp3-server', 'restart'])
    else:
        print(prefix_str + dhcp_subnet_str)


def print_machine_list(db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    machines = all_hosts(con)
    for hostname, ip_mac in machines.items():
        ip = ip_mac['ip']
        mac = ip_mac['macaddress']
        root = ip_mac['root']
        print('%s:\n  ip: %s\n  mac: %s\n  root: %s\n' % (hostname, ip, mac,
            root))


def wake_on_lan(hostname, port, broadcast, db):
    logger = logging.getLogger('pxe')
    con = open_db(db)
    logger.info('wake on lan %s' % hostname)
    mac = find_by_hostname(con, hostname)['macaddress']
    send_wol_magick_packet([mac], broadcast, port)


def chroot_command(chroot_dir, *args):
    command = ['sudo', 'chroot', chroot_dir]
    command.extend(*args)
    return check_call(command)


<mask token>


def copy_template_filesystem(template_dir, target_dir, apt_sources):
    logger = logging.getLogger('pxe')
    logger.info('copying filesystem')
    check_call(['sudo', 'cp', '-ax', template_dir, target_dir])
    logger.info('copying etc/apt/sources.list')
    check_call(['sudo', 'cp', apt_sources, os.path.join(target_dir, 'etc',
        'apt', 'sources.list')])
    return


class ChrootEnvironment:

    def __init__(self, target_dir):
        self.target_dir = target_dir

    def __enter__(self):
        target_dir = self.target_dir
        check_call(['sudo', 'mount', '-o', 'bind', '/dev/', os.path.join(
            target_dir, 'dev')])
        chroot_command(target_dir, 'mount -t proc none /proc'.split())
        chroot_command(target_dir, 'mount -t sysfs none /sys'.split())
        chroot_command(target_dir, 'mount -t devpts none /dev/pts'.split())

    def __exit__(self, type, value, traceback):
        target_dir = self.target_dir
        chroot_command(target_dir, 'umount -lf /proc'.split())
        chroot_command(target_dir, 'umount -lf /sys'.split())
        chroot_command(target_dir, 'umount -lf /dev/pts'.split())
        check_call(['sudo', 'umount', '-lf', os.path.join(target_dir, 'dev')])


def install_apt_packages(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('installing base apt packages')
        chroot_command(target_dir, ['apt-get', 'update'])
        chroot_command(target_dir, ['apt-get', 'install', 'wget'])
        chroot_command(target_dir, ['sh', '-c',
            'wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | apt-key add -'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | apt-key add -'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget https://www.ubuntulinux.jp/sources.list.d/lucid.list -O /etc/apt/sources.list.d/ubuntu-ja.list'
            ])
        chroot_command(target_dir, ['apt-get', 'update'])
        chroot_command(target_dir, ['apt-get', 'install', '--force-yes',
            '-y'] + APT_PACKAGES.split())
        logger.info('installing ros and openrave apt packages')
        chroot_command(target_dir, ['sh', '-c',
            'echo deb http://packages.ros.org/ros/ubuntu lucid main > /etc/apt/sources.list.d/ros-latest.list'
            ])
        chroot_command(target_dir, ['sh', '-c',
            'wget http://packages.ros.org/ros.key -O - | apt-key add -'])
        chroot_command(target_dir, ['add-apt-repository',
            'ppa:openrave/release'])
        chroot_command(target_dir, ['apt-get', 'update'])


def setup_user(target_dir, user, passwd):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('adding pxe user')
        chroot_command(target_dir, ['sh', '-c', 
            'id %s || useradd %s -g sudo -s /bin/bash' % (user, user)])
        chroot_command(target_dir, ['sh', '-c', 'echo %s:%s | chpasswd' % (
            user, passwd)])
        chroot_command(target_dir, ['rm', '-f', '/etc/hostname'])


def update_initram(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('updating initram.img')
        chroot_command(target_dir, ['sh', '-c', 
            "echo '%s' > /etc/initramfs-tools/initramfs.conf" % INITRAMFS_CONF]
            )
        chroot_command(target_dir, ['sh', '-c', "echo '%s' > /etc/fstab" %
            FSTAB])
        chroot_command(target_dir, ['sh', '-c', 
            "echo '%s' > /etc/initramfs-tools/modules" % INITRAM_MODULES])
        chroot_command(target_dir, ['sh', '-c',
            'mkinitramfs -o /boot/initrd.img-`uname -r` `ls /lib/modules`'])


def setup_pxe_dphys(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger('pxe')
        logger.info('setup pxe_dphys')
        chroot_command(target_dir, ['sh', '-c', 
            """cat > /usr/local/sbin/pxe-dphys-swapfile <<EOF
%s
EOF""" %
            PXE_DPHYS_SWAPFILE])
        chroot_command(target_dir, ['sh', '-c', 
            """cat > /etc/init.d/pxe-dphys-swapfile <<EOF
%s
EOF""" %
            PXE_DPHYS_SWAPFILE_INIT_D])
        chroot_command(target_dir, ['sh', '-c', 
            'cat > /etc/dphys-swapfile <<EOF\n%s\nEOF' % PXE_DPHYS_CONFIG])
        chroot_command(target_dir, ['chmod', '+x',
            '/usr/local/sbin/pxe-dphys-swapfile'])
        chroot_command(target_dir, ['chmod', '+x',
            '/etc/init.d/pxe-dphys-swapfile'])
        chroot_command(target_dir, ['update-rc.d', 'pxe-dphys-swapfile',
            'defaults'])


def generate_pxe_filesystem(template_dir, target_dir, apt_sources, user, passwd
    ):
    logger = logging.getLogger('pxe')
    if not os.path.exists(template_dir):
        generate_pxe_template_filesystem(template_dir)
    if not os.path.exists(target_dir):
        copy_template_filesystem(template_dir, target_dir, apt_sources)
    check_call(['sudo', 'cp', apt_sources, os.path.join(target_dir, 'etc',
        'apt', 'sources.list')])
    install_apt_packages(target_dir)
    setup_user(target_dir, user, passwd)
    setup_pxe_dphys(target_dir)
    update_initram(target_dir)


def generate_top_html(db, log):
    con = open_db(db)
    machines = all_hosts(con)
    host_strs = []
    hosts = sorted(machines.keys())
    for hostname in hosts:
        ip_mac = machines[hostname]
        ip = ip_mac['ip']
        mac = ip_mac['macaddress']
        root = ip_mac['root']
        alivep = ping_host(ip)
        if alivep:
            template = Template(HTML_ALIVE_HOST_TMPL)
        else:
            template = Template(HTML_DEAD_HOST_TMPL)
        host_strs.append(template.substitute({'hostname': hostname, 'ip':
            ip, 'macaddress': mac, 'root': root}))
    con.close()
    f = open(log)
    log_lines = f.readlines()[-1000:]
    log_strs = []
    for line in log_lines:
        log_strs.append('<p>' + xml.sax.saxutils.escape(line) + '</p>')
    log_strs.reverse()
    f.close()
    html_template = Template(HTML_TMPL)
    return html_template.substitute({'hosts': '\n'.join(host_strs), 'log':
        '\n'.join(log_strs)})


def update_dhcp_from_web():
    if global_options.overwrite_dhcp:
        logger = logging.getLogger('pxe')
        generate_dhcp(global_options.db, global_options.prefix_dhcp_file,
            global_options.subnet, global_options.netmask, global_options.
            dhcp_range_start, global_options.dhcp_range_stop,
            global_options.broadcast, global_options.dns_ip, global_options
            .domain_name, global_options.gateway, global_options.pxe_server,
            global_options.pxe_filename, global_options.overwrite_dhcp,
            global_options.dhcp_conf_file)
    if global_options.generate_pxe_config_files:
        generate_pxe_config_files(global_options.db, global_options.
            generate_pxe_config_files, global_options.tftp_dir)
    check_call(['sudo', '/etc/init.d/dhcp3-server', 'restart'])


def generate_error_html(e, host, port):
    return ERROR_HTML_TMPL % ('http://' + host + ':' + str(port), e)


class WebHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(s):
        s.send_response(200)
        s.send_header('Content-type', 'text/html')
        s.end_headers()
        try:
            if s.path.startswith('/delete'):
                delete_host = cgi.parse_qs(s.path.split('?')[1]).keys()[0]
                delete_machine(delete_host, db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/add'):
                add_host_desc = cgi.parse_qs(s.path.split('?')[1])
                add_machine(add_host_desc['hostname'][0], add_host_desc[
                    'macaddress'][0], add_host_desc['ip'][0], add_host_desc
                    ['root'][0], db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/auto_add'):
                add_host_desc = cgi.parse_qs(s.path.split('?')[1])
                root_dir = add_host_desc['root'][0]
                try:
                    global_options.auto_add = root_dir
                    auto_add_vm(global_options)
                finally:
                    global_options.auto_add = None
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            elif s.path.startswith('/boot'):
                logger = logging.getLogger('pxe')
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
                params = cgi.parse_qs(s.path.split('?')[1])
                vmname = params['vmname'][0]
                physc = params['physical'][0]
                logger.info('invoke %s @ %s' % (vmname, physc))
                try:
                    global_options.generate_virtualbox_image = vmname
                    global_options.refer_physical_machine = physc
                    generate_virtualbox_image(global_options.db,
                        global_options.generate_virtualbox_image,
                        global_options.virtualbox_path, global_options.
                        refer_physical_machine, global_options.
                        virtualbox_cpunum, global_options.
                        virtualbox_memsize, global_options.
                        virtualbox_vramsize, global_options.
                        virtualbox_macaddress, global_options.remote_user, 
                        False)
                finally:
                    global_options.generate_virtualbox_image = None
                    global_options.refer_physical_machine = None
                boot_vm(global_options.virtualbox_path, vmname, physc)
                html = SUCCESS_HTML_TMPL % ('http://' + global_options.
                    web_hostname + ':' + str(global_options.web_port))
            else:
                html = generate_top_html(db_name, global_options.log)
            s.wfile.write(html)
        except Exception as e:
            logger = logging.getLogger('pxe')
            logger.info('error has occurred: %s' % e)
            html = generate_error_html(e, global_options.web_hostname,
                global_options.web_port)
            s.wfile.write(html)


def run_web(options):
    db = options.db
    port = options.web_port
    overwritep = options.overwrite_dhcp
    global db_name
    global global_options
    db_name = db
    global_options = options
    BaseHTTPServer.HTTPServer((options.web_hostname, port), WebHandler
        ).serve_forever()


def generate_pxe_config_files(db, generate_pxe_config_files, tftp_dir):
    if generate_pxe_config_files:
        logger = logging.getLogger('pxe')
        logger.info('generate the pxe configuration files')
        con = open_db(db)
        machines = all_hosts(con)
        for hostname in machines.keys():
            template = Template(PXE_CONFIG_TMPL)
            mac = machines[hostname]['macaddress']
            file_name = os.path.join(tftp_dir, 'pxelinux.cfg', '01-' + mac.
                replace(':', '-'))
            file_str = template.substitute({'hostname': hostname, 'root':
                machines[hostname]['root'], 'tftp_dir': tftp_dir})
            f = open(file_name, 'w')
            f.write(file_str)
            f.close()
            check_call(['sudo', 'chown', 'pxe.tftp', file_name])
            check_call(['sudo', 'chmod', 'g+rw', file_name])
            check_call(['sudo', 'chmod', 'u+rw', file_name])


def generate_uuid():
    pipe = os.popen('uuidgen')
    uuid = pipe.read().strip()
    pipe.close()
    return uuid


def generate_virtualbox_image(db, generate_virtualbox_image,
    virtualbox_path, refer_physical_machine, virtualbox_cpunum,
    virtualbox_memsize, virtualbox_vramsize, virtualbox_macaddress,
    remote_user, register_vm=True):
    vmname = generate_virtualbox_image
    logger = logging.getLogger('pxe')
    if not os.path.exists(virtualbox_path):
        os.makedirs(virtualbox_path)
    if refer_physical_machine:
        host = refer_physical_machine
        logger.info('get cpuinfo of %s' % host)
        infos = parallel_util.cpuinfos([host], arch_filter=False, verbose=
            True, ros_filter=False, username=remote_user)
        logger.info('cpuinfo of %s => %s' % (host, infos[0]))
        cpunum = infos[0][host][0]
        memsize = int(infos[0][host][1] * 0.8 / 1000)
    else:
        cpunum = virtualbox_cpunum
        memsize = virtualbox_memsize
    vramsize = virtualbox_vramsize
    if virtualbox_macaddress:
        macaddress = virtualbox_macaddress
    else:
        con = open_db(db)
        hostname = find_by_hostname(con, vmname)
        macaddress = hostname['macaddress']
        con.close()
    cd_uuid = '9f6f1044-98a6-406c-be64-eec39baef4cb'
    machine_uuid = generate_uuid()
    vm_path = os.path.join(virtualbox_path, vmname + '.vbox')
    f = open(vm_path, 'w')
    template = Template(VIRTUALBOX_XML_TEMPLATE)
    content = template.substitute({'machine_uuid': machine_uuid, 'cd_uuid':
        cd_uuid, 'hostname': vmname, 'cpunum': cpunum, 'memsize': memsize,
        'vramsize': vramsize, 'macaddress': macaddress.replace(':', '')})
    f.write(content)
    f.close()
    if register_vm:
        check_call(['VBoxManage', 'registervm', vmname + '.vbox'])


def print_virtualbox_macaddress():
    print(generate_virtualbox_macaddress())


def generate_virtualbox_macaddress():
    return '08:00:27:%02x:%02x:%02x' % (int(random.random() * 255), int(
        random.random() * 255), int(random.random() * 255))


def nslookup(ip):
    output = gethostbyaddr(ip)
    return output[0]


def print_lookup_free_host(options):
    string = lookup_free_host(options)
    print(string)


<mask token>


def auto_add_vm(options):
    logger = logging.getLogger('pxe')
    root = options.auto_add
    free_host_ip = lookup_free_host(options.db, options.dhcp_range_start,
        options.dhcp_range_stop)
    free_host = free_host_ip.split()[0]
    free_ip = free_host_ip.split()[1]
    mac_address = generate_virtualbox_macaddress()
    add_machine(free_host, mac_address, free_ip, root, options.db)
    options.overwrite_dhcp = True
    generate_dhcp(options.db, options.prefix_dhcp_file, options.subnet,
        options.netmask, options.dhcp_range_start, options.dhcp_range_stop,
        options.broadcast, options.dns_ip, options.domain_name, options.
        gateway, options.pxe_server, options.pxe_filename, options.
        overwrite_dhcp, options.dhcp_conf_file)
    options.generate_pxe_config_files = True
    generate_pxe_config_files(options.db, options.generate_pxe_config_files,
        options.tftp_dir)
    print(free_host)


def boot_vm(vmdir, vmname, physical_machine):
    logger = logging.getLogger('pxe')
    cmd0 = ['ssh', '-t', 'pxe@%s' % physical_machine, 
        "sh -c 'VBoxManage unregistervm %s || exit 0'" % vmname]
    cmd1 = ['ssh', 'pxe@%s' % physical_machine, 'mkdir -p .VirtualBox']
    cmd2 = ['scp', os.path.join(vmdir, vmname + '.vbox'), 
        'pxe@%s:.VirtualBox' % physical_machine]
    cmd3 = ['ssh', '-t', 'pxe@%s' % physical_machine, 
        'VBoxManage registervm %s' % (vmname + '.vbox')]
    cmd4 = ['ssh', 'pxe@%s' % physical_machine, 
        'screen -d -m VBoxHeadless -s %s' % vmname]
    logger.info('exec => %s' % cmd0)
    check_call(cmd0)
    logger.info('exec => %s' % cmd1)
    check_call(cmd1)
    logger.info('exec => %s' % cmd2)
    check_call(cmd2)
    logger.info('exec => %s' % cmd3)
    check_call(cmd3)
    logger.info('exec => %s' % cmd4)
    check_call(cmd4)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2011 Ryohei Ueda
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#     http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import xml.sax.saxutils
import logging
from .template import *
import random
from socket import gethostbyaddr
import BaseHTTPServer
import cgi
import sqlite3
import socket
import binascii
import os
import ping                     # easy_install ping
from subprocess import check_call
from string import Template

import parallel_util

LOGGER_FILE_FORMAT = '[%(asctime)s] %(name)s [%(levelname)s] %(message)s'
LOGGER_CONSOLE_FORMAT = '>> %(message)s'

def ping_host(hostname):
    ping_ret = ping.quiet_ping(hostname, timeout=0.1)
    return ping_ret[0] == 0     # verify return core is 0

def create_logger(log_file_name):
    logger = logging.getLogger("pxe")
    fh = logging.FileHandler(log_file_name)
    fh.setLevel(logging.INFO)
    ch = logging.StreamHandler()
    ch.setLevel(logging.INFO)
    fh.setFormatter(logging.Formatter(LOGGER_FILE_FORMAT))
    ch.setFormatter(logging.Formatter(LOGGER_CONSOLE_FORMAT))
    logger.addHandler(fh)
    logger.addHandler(ch)
    logger.setLevel(logging.INFO)
    return logger

def send_wol_magick_packet(macs, ipaddr, port):
    "http://www.emptypage.jp/gadgets/wol.html"
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    for mac in macs:
        for sep in ':-':
            if sep in mac:
                mac = ''.join([x.rjust(2, '0') for x in mac.split(sep)])
                break
        mac = mac.rjust(12, '0')
        p = '\xff' * 6 + binascii.unhexlify(mac) * 16
        s.sendto(p, (ipaddr, port))
    s.close()
    
def open_db(db):
    logger = logging.getLogger("pxe")
    logger.info("opening %s as DB" % (db))
    if os.path.exists(db):
        con = sqlite3.connect(db)
    else:
        con = sqlite3.connect(db)
        con.execute(DB_CREATE_TABLE_SQL)
    return con

def delete_host(con, host):
    template = Template(DEL_HOST_SQL)
    sql = template.substitute({"hostname": host})
    con.execute(sql)
    con.commit()

def add_host(con, host, ip, mac, root):
    template = Template(ADD_HOST_SQL)
    sql = template.substitute({"hostname": host,
                               "ip": ip,
                               "macaddress": mac,
                               "root": root})
    con.execute(sql)
    con.commit()

def all_hosts(con):
    sql_result = con.execute(ALL_HOSTS_SQL)
    result = {}
    for row in sql_result:
        result[row[0]] = {"ip": row[1], "macaddress": row[2], "root": row[3]}
    return result

def find_by_ip(con, ip):
    template = Template(FIND_IP_SQL)
    sql_str = template.substitute({"ip": ip})
    sql_result = con.execute(sql_str)
    for row in sql_result:
        return {"ip": row[1], "macaddress": row[2], "root": row[3]}
    return False

def find_by_hostname(con, hostname):
    template = Template(FIND_HOST_SQL)
    sql_str = template.substitute({"hostname": hostname})
    sql_result = con.execute(sql_str)
    for row in sql_result:
        return {"ip": row[1], "macaddress": row[2], "root": row[3]}
    raise "cannot find %s" % (hostname)

def delete_machine(hostname, db):
    logger = logging.getLogger("pxe")
    con = open_db(db)
    logger.info("deleteing a machine:: %s" % hostname)
    delete_host(con, hostname)
    con.close()
    
def add_machine(hostname, mac, ip, root, db):
    logger = logging.getLogger("pxe")
    con = open_db(db)
    logger.info("adding a machine:: hostname - %s, mac - %s, ip - %s, root - %s" % (hostname, mac, ip, root))
    add_host(con, hostname, ip, mac, root)
    con.close()
    
def generate_dhcp(db,
                  prefix_dhcp_file,
                  subnet,
                  netmask,
                  dhcp_range_start,
                  dhcp_range_stop,
                  broadcast,
                  dns_ip,
                  domain_name,
                  gateway,
                  pxe_server,
                  pxe_filename,
                  overwrite_dhcp,
                  dhcp_conf_file,
                  restart_dhcp):
    logger = logging.getLogger("pxe")
    logger.info("generating dhcpd.conf")
    con = open_db(db)
    machines = all_hosts(con)
    generated_string = []
    # generate host section
    for (hostname, ip_mac) in machines.items():
        ip = ip_mac["ip"]
        mac = ip_mac["macaddress"]
        template = Template(DHCP_HOST_TMPL)
        host_str = template.substitute({"hostname": hostname,
                                        "ip": ip,
                                        "mac": mac})
        generated_string.append(host_str)
    if prefix_dhcp_file:
        logger.info("adding %s as prefix" % (prefix_dhcp_file))
        f = open(prefix_dhcp_file)
        prefix_str = "".join(f.readlines())
    else:
        prefix_str = ""
    # generate dhcp subnet section
    dhcp_subnet_tmpl = Template(DHCP_SUBNET_TMPL)
    replace_dict = {"subnet": subnet,
                    "netmask": netmask,
                    "dhcp_range_start": dhcp_range_start,
                    "dhcp_range_stop": dhcp_range_stop,
                    "broadcast": broadcast,
                    "dns_ip": dns_ip,
                    "domain_name": domain_name,
                    "gateway": gateway,
                    "pxe_server": pxe_server,
                    "pxe_filename": pxe_filename,
                    "hosts": "\n".join(generated_string)}
    dhcp_subnet_str = dhcp_subnet_tmpl.substitute(replace_dict)
    if overwrite_dhcp:
        logger.info("overwriting %s" % (dhcp_conf_file))
        path = dhcp_conf_file
        f = open(path, "w")
        f.write(prefix_str + dhcp_subnet_str)
        f.close()
        if restart_dhcp:
            logger.info("restarting dhcp")
            check_call(["sudo", "/etc/init.d/dhcp3-server", "restart"])
    else:
        print(prefix_str + dhcp_subnet_str)

def print_machine_list(db):
    logger = logging.getLogger("pxe")
    con = open_db(db)
    machines = all_hosts(con)
    for hostname, ip_mac in machines.items():
        ip = ip_mac["ip"]
        mac = ip_mac["macaddress"]
        root = ip_mac["root"]
        print("""%s:
  ip: %s
  mac: %s
  root: %s
""" % (hostname, ip, mac, root))

def wake_on_lan(hostname, port, broadcast, db):
    logger = logging.getLogger("pxe")
    con = open_db(db)
    logger.info("wake on lan %s" % (hostname))
    mac = find_by_hostname(con, hostname)["macaddress"]
    send_wol_magick_packet([mac], broadcast, port)

def chroot_command(chroot_dir, *args):
    command = ["sudo", "chroot", chroot_dir]
    command.extend(*args)
    return check_call(command)
    
def generate_pxe_template_filesystem(template_dir):
    logger = logging.getLogger("pxe")
    logger.info("generating template filesyste")
    try:
        check_call(["sudo", "debootstrap", "lucid", template_dir])
    except:
        # remove template_dir
        logger.info("removing template dir")
        check_call(["sudo", "rm", "-rf", template_dir])
        raise


def copy_template_filesystem(template_dir, target_dir, apt_sources):
    logger = logging.getLogger("pxe")
    logger.info("copying filesystem")
    check_call(["sudo", "cp", "-ax", template_dir, target_dir])
    logger.info("copying etc/apt/sources.list")
    check_call(["sudo", "cp", apt_sources,
                os.path.join(target_dir, "etc", "apt", "sources.list")])
    return

class ChrootEnvironment():
    def __init__(self, target_dir):
        self.target_dir = target_dir
    def __enter__(self):
        target_dir = self.target_dir
        check_call(["sudo", "mount", "-o", "bind", "/dev/",
                    os.path.join(target_dir, "dev")])
        chroot_command(target_dir,
                       "mount -t proc none /proc".split())
        chroot_command(target_dir,
                       "mount -t sysfs none /sys".split())
        chroot_command(target_dir,
                       "mount -t devpts none /dev/pts".split())
    def __exit__(self, type, value, traceback):
        target_dir = self.target_dir
        chroot_command(target_dir,
                       "umount -lf /proc".split())
        chroot_command(target_dir,
                       "umount -lf /sys".split())
        chroot_command(target_dir,
                       "umount -lf /dev/pts".split())
        check_call(["sudo", "umount", "-lf", os.path.join(target_dir, "dev")])
 

def install_apt_packages(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger("pxe")
        logger.info("installing base apt packages")
        chroot_command(target_dir, ["apt-get", "update"])
        chroot_command(target_dir, ["apt-get", "install", "wget"])
        chroot_command(target_dir, ["sh", "-c",
                                    "wget -q https://www.ubuntulinux.jp/ubuntu-ja-archive-keyring.gpg -O- | apt-key add -"])
        chroot_command(target_dir, ["sh", "-c",
                                    "wget -q https://www.ubuntulinux.jp/ubuntu-jp-ppa-keyring.gpg -O- | apt-key add -"])
        chroot_command(target_dir, ["sh", "-c",
                                    "wget https://www.ubuntulinux.jp/sources.list.d/lucid.list -O /etc/apt/sources.list.d/ubuntu-ja.list"])
        chroot_command(target_dir, ["apt-get", "update"])
        chroot_command(target_dir,
                       ["apt-get", "install", "--force-yes", "-y"] + APT_PACKAGES.split())
        logger.info("installing ros and openrave apt packages")
        chroot_command(target_dir, ["sh", "-c",
                                    "echo deb http://packages.ros.org/ros/ubuntu lucid main > /etc/apt/sources.list.d/ros-latest.list"])
        chroot_command(target_dir, ["sh", "-c",
                                    "wget http://packages.ros.org/ros.key -O - | apt-key add -"])
        chroot_command(target_dir, ["add-apt-repository", "ppa:openrave/release"])
        chroot_command(target_dir, ["apt-get", "update"])
        #chroot_command(target_dir, ["apt-get", "install", "--force-yes", "-y", "ros-diamondback-ros-base", "openrave"])

def setup_user(target_dir, user, passwd):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger("pxe")
        logger.info("adding pxe user")
        chroot_command(target_dir, ["sh", "-c",
                                    "id %s || useradd %s -g sudo -s /bin/bash" % (user,
                                                                                  user)])
        chroot_command(target_dir, ["sh", "-c",
                                    "echo %s:%s | chpasswd" % (user, passwd)])
        chroot_command(target_dir, ["rm", "-f", "/etc/hostname"])

def update_initram(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger("pxe")
        logger.info("updating initram.img")
        chroot_command(target_dir, ["sh", "-c",
                                    "echo '%s' > /etc/initramfs-tools/initramfs.conf" % (INITRAMFS_CONF)])
        chroot_command(target_dir, ["sh", "-c",
                                    "echo '%s' > /etc/fstab" % (FSTAB)])
        chroot_command(target_dir, ["sh", "-c",
                                    "echo '%s' > /etc/initramfs-tools/modules" % (INITRAM_MODULES)])
        chroot_command(target_dir, ["sh", "-c",
                                    "mkinitramfs -o /boot/initrd.img-`uname -r` `ls /lib/modules`"])

def setup_pxe_dphys(target_dir):
    env = ChrootEnvironment(target_dir)
    with env:
        logger = logging.getLogger("pxe")
        logger.info("setup pxe_dphys")
        chroot_command(target_dir,
                       ["sh", "-c",
                        """cat > /usr/local/sbin/pxe-dphys-swapfile <<EOF
%s
EOF""" % (PXE_DPHYS_SWAPFILE)])
        chroot_command(target_dir,
                       ["sh", "-c",
                        """cat > /etc/init.d/pxe-dphys-swapfile <<EOF
%s
EOF""" % (PXE_DPHYS_SWAPFILE_INIT_D)])
        chroot_command(target_dir,
                       ["sh", "-c",
                        """cat > /etc/dphys-swapfile <<EOF
%s
EOF""" % (PXE_DPHYS_CONFIG)])
        chroot_command(target_dir,
                       ["chmod", "+x", "/usr/local/sbin/pxe-dphys-swapfile"])
        chroot_command(target_dir,
                       ["chmod", "+x", "/etc/init.d/pxe-dphys-swapfile"])
        chroot_command(target_dir,
                       ["update-rc.d", "pxe-dphys-swapfile", "defaults"])

        
def generate_pxe_filesystem(template_dir, target_dir, apt_sources,
                            user, passwd):
    logger = logging.getLogger("pxe")
    if not os.path.exists(template_dir):
        generate_pxe_template_filesystem(template_dir)
    if not os.path.exists(target_dir):
        copy_template_filesystem(template_dir, target_dir, apt_sources)
    check_call(["sudo", "cp", apt_sources,
                os.path.join(target_dir, "etc", "apt", "sources.list")])
    install_apt_packages(target_dir)
    setup_user(target_dir, user, passwd)
    setup_pxe_dphys(target_dir)
    update_initram(target_dir)

def generate_top_html(db, log):
    con = open_db(db)
    machines = all_hosts(con)
    host_strs = []
    hosts = sorted(machines.keys())
    for hostname in hosts:
        ip_mac = machines[hostname]
        ip = ip_mac["ip"]
        mac = ip_mac["macaddress"]
        root = ip_mac["root"]
        alivep = ping_host(ip)
        if alivep:
            template = Template(HTML_ALIVE_HOST_TMPL)
        else:
            template = Template(HTML_DEAD_HOST_TMPL)
        host_strs.append(template.substitute({"hostname": hostname,
                                              "ip": ip,
                                              "macaddress": mac,
                                              "root": root}))
    con.close()
    # generate log
    f = open(log)
    log_lines = f.readlines()[-1000:] # max 1000 lines
    log_strs = []
    for line in log_lines:
        log_strs.append("<p>" + xml.sax.saxutils.escape(line) + "</p>")
    log_strs.reverse()
    f.close()
    html_template = Template(HTML_TMPL)
    
    return html_template.substitute({"hosts": "\n".join(host_strs),
                                     "log": "\n".join(log_strs)})

def update_dhcp_from_web():
    if global_options.overwrite_dhcp:
        logger = logging.getLogger("pxe")
        generate_dhcp(global_options.db,
                      global_options.prefix_dhcp_file,
                      global_options.subnet,
                      global_options.netmask,
                      global_options.dhcp_range_start,
                      global_options.dhcp_range_stop,
                      global_options.broadcast,
                      global_options.dns_ip,
                      global_options.domain_name,
                      global_options.gateway,
                      global_options.pxe_server,
                      global_options.pxe_filename,
                      global_options.overwrite_dhcp,
                      global_options.dhcp_conf_file)
    if global_options.generate_pxe_config_files:
        generate_pxe_config_files(global_options.db,
                                  global_options.generate_pxe_config_files,
                                  global_options.tftp_dir)
    # restart dhcp server
    check_call(["sudo", "/etc/init.d/dhcp3-server", "restart"])

def generate_error_html(e, host, port):
    return ERROR_HTML_TMPL % ("http://" + host + ":" + str(port), e)
        
class WebHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(s):
        s.send_response(200)
        s.send_header("Content-type", "text/html")
        s.end_headers()
        try:
            if s.path.startswith("/delete"): # --delete
                delete_host = cgi.parse_qs(s.path.split("?")[1]).keys()[0]
                delete_machine(delete_host, db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ("http://" +
                                            global_options.web_hostname
                                            + ":" +
                                            str(global_options.web_port))
            elif s.path.startswith("/add"): # --add
                add_host_desc = cgi.parse_qs(s.path.split("?")[1])
                add_machine(add_host_desc["hostname"][0],
                            add_host_desc["macaddress"][0],
                            add_host_desc["ip"][0],
                            add_host_desc["root"][0],
                            db_name)
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ("http://" +
                                            global_options.web_hostname
                                            + ":" +
                                            str(global_options.web_port))
            elif s.path.startswith("/auto_add"): # --auto-add
                add_host_desc = cgi.parse_qs(s.path.split("?")[1])
                root_dir = add_host_desc["root"][0]
                try:
                    global_options.auto_add = root_dir
                    auto_add_vm(global_options)
                finally:
                    global_options.auto_add = None
                update_dhcp_from_web()
                html = SUCCESS_HTML_TMPL % ("http://" +
                                            global_options.web_hostname
                                            + ":" +
                                            str(global_options.web_port))
            elif s.path.startswith("/boot"):
                logger = logging.getLogger("pxe")
                html = SUCCESS_HTML_TMPL % ("http://" +
                                            global_options.web_hostname
                                            + ":" +
                                            str(global_options.web_port))
                params = cgi.parse_qs(s.path.split("?")[1])
                vmname = params["vmname"][0]
                physc = params["physical"][0]
                logger.info("invoke %s @ %s" % (vmname, physc))
                try:
                    global_options.generate_virtualbox_image = vmname
                    global_options.refer_physical_machine = physc
                    generate_virtualbox_image(global_options.db,
                                              global_options.generate_virtualbox_image,
                                              global_options.virtualbox_path,
                                              global_options.refer_physical_machine,
                                              global_options.virtualbox_cpunum,
                                              global_options.virtualbox_memsize,
                                              global_options.virtualbox_vramsize,
                                              global_options.virtualbox_macaddress,
                                              global_options.remote_user,
                                              False)
                finally:
                    global_options.generate_virtualbox_image = None
                    global_options.refer_physical_machine = None
                boot_vm(global_options.virtualbox_path, vmname, physc)
                html = SUCCESS_HTML_TMPL % ("http://" +
                                            global_options.web_hostname
                                            + ":" +
                                            str(global_options.web_port))
            else:
                html = generate_top_html(db_name, global_options.log)
            s.wfile.write(html)
        except Exception as e:
            logger = logging.getLogger("pxe")
            logger.info("error has occurred: %s" % (e))
            html = generate_error_html(e,
                                       global_options.web_hostname,
                                       global_options.web_port)
            s.wfile.write(html)
    
def run_web(options):
    db = options.db
    port = options.web_port
    overwritep = options.overwrite_dhcp
    # setup global variables
    global db_name              # dirty hack!
    global global_options
    db_name = db
    global_options = options
    BaseHTTPServer.HTTPServer((options.web_hostname, port),
                              WebHandler).serve_forever()

def generate_pxe_config_files(db, generate_pxe_config_files, tftp_dir):
    if generate_pxe_config_files:
        logger = logging.getLogger("pxe")
        logger.info("generate the pxe configuration files")
        con = open_db(db)
        machines = all_hosts(con)
        for hostname in machines.keys():
            template = Template(PXE_CONFIG_TMPL)
            mac = machines[hostname]["macaddress"]
            file_name = os.path.join(tftp_dir, "pxelinux.cfg",
                                     "01-" + mac.replace(":", "-"))
            file_str = template.substitute({"hostname": hostname,
                                            "root": machines[hostname]["root"],
                                            "tftp_dir": tftp_dir})

            f = open(file_name, "w")
            f.write(file_str)
            f.close()
            check_call(["sudo", "chown", "pxe.tftp", file_name])
            check_call(["sudo", "chmod", "g+rw", file_name])
            check_call(["sudo", "chmod", "u+rw", file_name])
            
def generate_uuid():
    pipe = os.popen("uuidgen")
    uuid = pipe.read().strip()
    pipe.close()
    return uuid

def generate_virtualbox_image(db,
                              generate_virtualbox_image,
                              virtualbox_path,
                              refer_physical_machine,
                              virtualbox_cpunum,
                              virtualbox_memsize,
                              virtualbox_vramsize,
                              virtualbox_macaddress,
                              remote_user,
                              register_vm = True):
                              
    vmname = generate_virtualbox_image
    logger = logging.getLogger("pxe")
    if not os.path.exists(virtualbox_path):
        os.makedirs(virtualbox_path)
    if refer_physical_machine:
        host = refer_physical_machine
        logger.info("get cpuinfo of %s" % (host))
        infos = parallel_util.cpuinfos([host],
                                       arch_filter = False,
                                       verbose = True,
                                       ros_filter = False,
                                       username = remote_user)
        logger.info("cpuinfo of %s => %s" % (host, infos[0]))
        cpunum = infos[0][host][0]
        memsize = int(infos[0][host][1] * 0.8 / 1000)
    else:
        cpunum = virtualbox_cpunum
        memsize = virtualbox_memsize
    vramsize = virtualbox_vramsize
    if virtualbox_macaddress:
        macaddress = virtualbox_macaddress
    else:
        # lookup macaddress from db
        con = open_db(db)
        hostname = find_by_hostname(con, vmname)
        macaddress = hostname["macaddress"]
        con.close()
    cd_uuid = "9f6f1044-98a6-406c-be64-eec39baef4cb"
    machine_uuid = generate_uuid()
    vm_path = os.path.join(virtualbox_path, vmname + ".vbox")
    f = open(vm_path, "w")
    template = Template(VIRTUALBOX_XML_TEMPLATE)
    content = template.substitute({"machine_uuid": machine_uuid,
                                   "cd_uuid": cd_uuid,
                                   "hostname": vmname,
                                   "cpunum": cpunum,
                                   "memsize": memsize,
                                   "vramsize": vramsize,
                                   "macaddress": macaddress.replace(":", "")})
    f.write(content)
    f.close()
    #print("please register %s on your virtualbox" % (vm_path))
    if register_vm:
        check_call(["VBoxManage", "registervm", vmname + ".vbox"])
    
def print_virtualbox_macaddress():
    print(generate_virtualbox_macaddress())
    
def generate_virtualbox_macaddress():
    # TODO: check duplication of macaddress
    return "08:00:27:%02x:%02x:%02x" % (int(random.random()*0xff), int(random.random()*0xff), int(random.random()*0xff))

def nslookup(ip):
    output = gethostbyaddr(ip)
    return output[0]

def print_lookup_free_host(options):
    string = lookup_free_host(options)
    print(string)

def lookup_free_host(db, dhcp_range_start, dhcp_range_stop):
    con = open_db(db)
    start = dhcp_range_start
    stop = dhcp_range_stop
    # it might be a bug
    ip_1 = start.split(".")[0]
    ip_2 = start.split(".")[1]
    ip_3 = start.split(".")[2]
    start_ip_suffix = int(start.split(".")[3])
    stop_ip_suffix = int(stop.split(".")[3])
    for ip in range(start_ip_suffix, stop_ip_suffix):
        full_ip = ".".join([ip_1, ip_2, ip_3, str(ip)])
        not_available = find_by_ip(con, full_ip)
        if not not_available:
            # TODO: check ping!
            return "%s %s" % (nslookup(full_ip), full_ip)
    print("none")

def auto_add_vm(options):
    logger = logging.getLogger("pxe")
    root = options.auto_add
    free_host_ip = lookup_free_host(options.db,
                                    options.dhcp_range_start,
                                    options.dhcp_range_stop)
    free_host = free_host_ip.split()[0]
    free_ip = free_host_ip.split()[1]
    mac_address = generate_virtualbox_macaddress()
    add_machine(free_host, mac_address, free_ip, root, options.db)
    options.overwrite_dhcp = True # force to be True
    generate_dhcp(options.db,
                  options.prefix_dhcp_file,
                  options.subnet,
                  options.netmask,
                  options.dhcp_range_start,
                  options.dhcp_range_stop,
                  options.broadcast,
                  options.dns_ip,
                  options.domain_name,
                  options.gateway,
                  options.pxe_server,
                  options.pxe_filename,
                  options.overwrite_dhcp,
                  options.dhcp_conf_file)
    options.generate_pxe_config_files = True # force to be True
    generate_pxe_config_files(options.db,
                              options.generate_pxe_config_files,
                              options.tftp_dir)
    print(free_host)

def boot_vm(vmdir, vmname, physical_machine):
    logger = logging.getLogger("pxe")
    cmd0 = ["ssh", "-t", "pxe@%s" % (physical_machine),
            "sh -c 'VBoxManage unregistervm %s || exit 0'" % (vmname)]
    cmd1 = ["ssh", "pxe@%s" % physical_machine, "mkdir -p .VirtualBox"]
    cmd2 = ["scp", os.path.join(vmdir, vmname + ".vbox"), 
            "pxe@%s:.VirtualBox" % (physical_machine)]
    cmd3 = ["ssh", "-t", "pxe@%s" % (physical_machine),
            "VBoxManage registervm %s" % (vmname + ".vbox")]
    cmd4 = ["ssh", "pxe@%s" % physical_machine,
            "screen -d -m VBoxHeadless -s %s" % vmname]
    logger.info("exec => %s" % (cmd0))
    check_call(cmd0)
    logger.info("exec => %s" % (cmd1))
    check_call(cmd1)
    logger.info("exec => %s" % (cmd2))
    check_call(cmd2)
    logger.info("exec => %s" % (cmd3))
    check_call(cmd3)
    logger.info("exec => %s" % (cmd4))
    check_call(cmd4)
