#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# This file is part of Karesansui Core.
#
# Copyright (C) 2009 HDE, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
""" 
<comment-ja>
Xenネットワークの設定を生成する
</comment-ja>
<comment-en>
Generate configuration file of Xen's networks.
</comment-en>

@file:   config_network.py

@author: Taizo ITO <taizo@karesansui-project.info>

@copyright:    

"""

import time
import os, stat
import re
from StringIO import StringIO
from xml.dom.ext import PrettyPrint
from xml.dom.DOMImplementation import implementation
import errno

import karesansui
from karesansui.lib.const import KARESANSUI_GROUP, \
     VIRT_NETWORK_CONFIG_DIR
from karesansui.lib.utils import get_xml_parse        as XMLParse
from karesansui.lib.utils import get_xml_xpath        as XMLXpath
from karesansui.lib.utils import r_chgrp, r_chmod
from karesansui.lib.networkaddress import NetworkAddress
from karesansui.lib.file.configfile import ConfigFile

class KaresansuiNetworkConfigParamException(karesansui.KaresansuiLibException):
    pass

class NetworkConfigParam:
    def __init__(self, arg):
        if isinstance(arg, basestring):
            # expect name as string
            self.name = arg
            self.uuid = None
            self.bridge = None
            self.forward_dev = None
            self.forward_mode = None
            self.ipaddr = None
            self.netmask = None
            self.dhcp_start = None
            self.dhcp_end = None
        else:
            # expect dict in KaresansuiVirtNetwork#get_info() format
            self.name = arg['name']
            self.uuid = arg['uuid']
            self.bridge = arg['bridge']['name']
            self.forward_dev = arg['forward']['dev']
            self.forward_mode = arg['forward']['mode']
            self.ipaddr = arg['ip']['address']
            self.netmask = arg['ip']['netmask']
            self.dhcp_start = arg['dhcp']['start']
            self.dhcp_end = arg['dhcp']['end']

    def get_network_name(self):
        return self.name

    def set_uuid(self, uuid):
        self.uuid = uuid
    def get_uuid(self):
        return self.uuid

    def set_bridge(self, bridge):
        """
        @param bridge: name of the bridge
        @type bridge: string
        @return nothing
        """
        self.bridge = bridge
    def get_bridge(self):
        return self.bridge

    def set_forward_dev(self, device):
        self.forward_dev = device
    def get_forward_dev(self):
        return self.forward_dev

    def set_forward_mode(self, mode='nat'):
        self.forward_mode = mode
    def get_forward_mode(self):
        return self.forward_mode

    def set_default_networks(self, addr, dhcp_start=None, dhcp_end=None):
        self.set_netmask(NetworkAddress(addr).get('netmask'))
        self.set_ipaddr(NetworkAddress(addr).get('first_ip'))
        if not dhcp_start:
            dhcp_start = NetworkAddress(addr).get('first_ip')
        if not dhcp_end:
            dhcp_end = NetworkAddress(addr).get('last_ip')
        self.set_dhcp_start(dhcp_start)
        self.set_dhcp_end(dhcp_end)

    def get_networks(self):
        return {"ipaddr": self.ipaddr, "netmask":self.netmask,
                "dhcp_start": self.dhcp_start, "dhcp_stop":self.dhcp_stop}

    def set_ipaddr(self, addr):
        self.ipaddr = addr
    def get_ipaddr(self):
        return self.ipaddr

    def set_netmask(self, addr):
        self.netmask = addr
    def get_netmask(self):
        return self.netmask

    def set_ipaddr_and_netmask(self, addr):
        """
        Set ip address and netmask from '192.168.0.1/24' or '192.168.0.1/255.255.255.0' styled strings.
        @param addr: Strings like '192.168.0.1/24' or '192.168.0.1/255.255.255.0'.
        @type addr: string
        @return: nothing
        """
        na = NetworkAddress(addr)
        self.set_ipaddr(na.get('ipaddr'))
        self.set_netmask(na.get('netmask'))

    def set_dhcp_start(self, addr):
        self.dhcp_start = addr
    def get_dhcp_start(self):
        return self.dhcp_start

    def set_dhcp_end(self, addr):
        self.dhcp_end = addr
    def get_dhcp_end(self):
        return self.dhcp_end

    def load_xml_config(self,path):

        if not os.path.exists(path):
            raise KaresansuiNetworkConfigParamException("no such file: %s" % path)

        document = XMLParse(path)
        uuid = XMLXpath(document,'/network/uuid/text()')
        self.set_uuid(str(uuid))

        forward_dev  = XMLXpath(document,'/network/forward/@dev')
        if forward_dev:
            self.set_forward_dev(forward_dev)
        forward_mode = XMLXpath(document,'/network/forward/@mode')
        if forward_mode:
            self.set_forward_mode(forward_mode)

        ipaddr = XMLXpath(document,'/network/ip/@address')
        self.set_ipaddr(ipaddr)

        netmask = XMLXpath(document,'/network/ip/@netmask')
        self.set_netmask(netmask)

        dhcp_start = XMLXpath(document,'/network/ip/dhcp/range/@start')
        self.set_dhcp_start(dhcp_start)

        dhcp_end = XMLXpath(document,'/network/ip/dhcp/range/@end')
        self.set_dhcp_end(dhcp_end)


    def validate(self):

        if not self.uuid:
            raise KaresansuiNetworkConfigParamException("ConfigParam: uuid is None")
        if not self.name or not len(self.name):
            raise KaresansuiNetworkConfigParamException("ConfigParam: illegal name")

class NetworkXMLGenerator:

    def _create_text_node(self, tag, txt):
        node = self.document.createElement(tag)
        self._add_text(node, txt)
        return node

    def _add_text(self, node, txt):
        txt_n = self.document.createTextNode(txt)
        node.appendChild(txt_n)

    def generate(self, config):
        tree = self.generate_xml_tree(config)
        out = StringIO()
        PrettyPrint(tree, out)
        return out.getvalue()

class NetworkXMLConfigGenerator(NetworkXMLGenerator):

    def __init__(self):
        self.config_dir = VIRT_NETWORK_CONFIG_DIR

    def generate_xml_tree(self, config):
        config.validate()
        self.config = config

        self.begin_build()
        self.build_bridge()
        self.build_forward()
        self.build_ip()
        self.end_build()

        return self.document

    def begin_build(self):
        self.document = implementation.createDocument(None,None,None)
        self.network = self.document.createElement("network")

        name = self._create_text_node("name", self.config.get_network_name())
        uuid = self._create_text_node("uuid", self.config.get_uuid())
        self.network.appendChild(name)
        self.network.appendChild(uuid)

        self.document.appendChild(self.network)

    def build_bridge(self):
        doc = self.document
        if self.config.get_bridge():
            bridge = doc.createElement("bridge")
            bridge.setAttribute("name", self.config.get_bridge())
            bridge.setAttribute("stp", "on")
            bridge.setAttribute("forwardDelay", "0")
            self.network.appendChild(bridge)

    def build_forward(self):
        doc = self.document
        if self.config.get_forward_dev() is not None or \
           self.config.get_forward_mode() is not None:

            forward = doc.createElement("forward")
            if self.config.get_forward_dev() is not None:
                forward.setAttribute("dev", self.config.get_forward_dev())
            if self.config.get_forward_mode() is not None:
                forward.setAttribute("mode", self.config.get_forward_mode())
            self.network.appendChild(forward)

    def build_ip(self):
        doc = self.document
        ip = doc.createElement("ip")
        ip.setAttribute("netmask", self.config.get_netmask())
        ip.setAttribute("address", self.config.get_ipaddr())
        self.network.appendChild(ip)

        dhcp = doc.createElement("dhcp")
        range = doc.createElement("range")
        range.setAttribute("start", self.config.get_dhcp_start())
        range.setAttribute("end", self.config.get_dhcp_end())
        dhcp.appendChild(range)
        ip.appendChild(dhcp)

    def end_build(self):
        pass

    def writecfg(self,cfg):
        try:
            os.makedirs(self.config_dir)
        except OSError, (err, msg):
            if err != errno.EEXIST:
                raise OSError(err,msg)

        filename = "%s/%s.xml" %(self.config_dir,self.config.get_network_name())
        ConfigFile(filename).write(cfg)
        r_chmod(filename,"o-rwx")
        r_chmod(filename,"g+rw")
        if os.getuid() == 0:
            r_chgrp(filename,KARESANSUI_GROUP)

