# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.
from OvmCommonModule import *
import traceback
import time
import re
 
logger = OvmLogger("OvmNetwork")

class Filter:
    class Network:
        IFNAME_LO     = r'(lo)'
        IFNAME_BRIDGE = r'(xenbr\d+|vlan\d+)'
        IFNAME_PIF    = r'(eth\d+$|bond\d+$)'
        IFNAME_VLAN   = r'(eth\d+.\d+$|bond\d+.\d+$)'


class Parser(object):
    '''
    classdocs
    '''    
    def findall(self, pattern, samples):
        """
        @param pattern: search pattern
        @param result: Parser line execution result
        @return : list of search
        find result of Parser which has same pattern
        findall Parser find all pattern in a string
        """
        result = []
        for line in samples:
            items = re.findall(pattern, line)
            for item in items:
                result.append(item)
        return result
    
    def checkPattern(self, pattern, cmd_result):
        """
        @param pattern: search pattern
        @param cmd_result: Parser line execution result
        @return : True (if pattern is occurred)
        """
        for line in cmd_result:
            items = re.findall(pattern, line)
            if len(items) > 0:
                return True
        return False
    
    def search(self, cmd_result, pattern):
        return None
    
class OvmVlanDecoder(json.JSONDecoder):
    def decode(self, jStr):
        deDict = asciiLoads(jStr)
        vlan = OvmVlan()
        setAttrFromDict(vlan, 'vid', deDict, int)
        setAttrFromDict(vlan, 'pif', deDict)
        return vlan

class OvmVlanEncoder(json.JSONEncoder):
    def default(self, obj):
        if not isinstance(obj, OvmVlan): raise Exception("%s is not instance of OvmVlan"%type(obj))
        dct = {}
        safeDictSet(obj, dct, 'name')
        safeDictSet(obj, dct, 'vid')
        safeDictSet(obj, dct, 'pif')
        return dct
    
def toOvmVlan(jStr):
    return json.loads(jStr, cls=OvmVlanDecoder)

def fromOvmVlan(vlan):
    return normalizeToGson(json.dumps(vlan, cls=OvmVlanEncoder))

class OvmBridgeDecoder(json.JSONDecoder):
    def decode(self, jStr):
        deDic = asciiLoads(jStr)
        bridge = OvmBridge()
        setAttrFromDict(bridge, 'name', deDic)
        setAttrFromDict(bridge, 'attach', deDic)
        return bridge

class OvmBridgeEncoder(json.JSONEncoder):
    def default(self, obj):
        if not isinstance(obj, OvmBridge): raise Exception("%s is not instance of OvmBridge"%type(obj))
        dct = {}
        safeDictSet(obj, dct, 'name')
        safeDictSet(obj, dct, 'attach')
        safeDictSet(obj, dct, 'interfaces')
        return dct
    
def toOvmBridge(jStr):
    return json.loads(jStr, cls=OvmBridgeDecoder)

def fromOvmBridge(bridge):
    return normalizeToGson(json.dumps(bridge, cls=OvmBridgeEncoder))

class OvmInterface(OvmObject):
    name = ''

class OvmVlan(OvmInterface):
    vid = 0
    pif = ''

class OvmBridge(OvmInterface):
    attach = ''
    interfaces = []

        
class OvmNetwork(OvmObject):
    '''
    Network
    '''

    @property
    def pifs(self):
        return self._getInterfaces("pif")

    @property
    def vlans(self):
        return self._getInterfaces("vlan")

    @property
    def bridges(self):
        return self._getInterfaces("bridge")
    
    def __init__(self):
        self.Parser = Parser()

    def _createVlan(self, vlan):
        """
        @param jsonString : parameter from client side
        @return : succ xxxxx
        ex. jsonString => {vid:100, pif:eth0}
        ex. return     => 
        """
        
        #Pre-condition
        #check Physical Interface Name
        if vlan.pif not in self.pifs.keys():    
            msg = "Physical Interface(%s) does not exist" % vlan.pif
            logger.debug(self._createVlan, msg)
            raise Exception(msg)

        #Pre-condition    
        #check Vlan Interface Name
        ifName = "%s.%s" % (vlan.pif, vlan.vid)
        if ifName in self.vlans.keys():
            msg = "Vlan Interface(%s) already exist, return it" % ifName
            logger.debug(self._createVlan, msg)
            return self.vlans[ifName]
            
        doCmd(['vconfig', 'add', vlan.pif, vlan.vid])
        self.bringUP(ifName)
        logger.debug(self._createVlan, "Create vlan %s successfully"%ifName)
        return self.vlans[ifName]
    
    def _deleteVlan(self, name):
        if name not in self.vlans.keys():
            raise Exception("No vlan device %s found"%name)
        
        vlan = self.vlans[name]
        self.bringDown(vlan.name)
        doCmd(['vconfig', 'rem', vlan.name])
        logger.debug(self._deleteVlan, "Delete vlan %s successfully"%vlan.name)
        
    
    def _createBridge(self, bridge):
        """
        @return : success
        ex. {bridge:xapi100, attach:eth0.100}
        create bridge interface, and attached it 
        cmd 1: ip link add bridge
        cmd 2: ip link set dev
        """
        
        if "xenbr" not in bridge.name and "vlan" not in bridge.name:
            raise Exception("Invalid bridge name %s. Bridge name must be in partten xenbr/vlan, e.g. xenbr0"%bridge.name)
        
        #pre-condition
        #check Bridge Interface Name
        if bridge.name in self.bridges.keys():
            msg = "Bridge(%s) already exist, return it" % bridge.name
            logger.debug(self._createBridge, msg)
            return self.bridges[bridge.name]

        #pre-condition
        #check attach must exist
        #possible to attach in PIF or VLAN 
        if bridge.attach not in self.vlans.keys() and bridge.attach not in self.pifs.keys():
            msg = "%s is not either pif or vlan" % bridge.attach
            logger.error(self._createBridge, msg)
            raise Exception(msg)

        doCmd(['ip', 'link', 'add', 'name', bridge.name, 'type', 'bridge'])
        doCmd(['ip', 'link', 'set', 'dev', bridge.attach, 'master', bridge.name])
        self.bringUP(bridge.name)
        logger.debug(self._createBridge, "Create bridge %s on %s successfully"%(bridge.name, bridge.attach))
        return self.bridges[bridge.name]
    
    def _getBridges(self):
        return self.bridges.keys()
    
    def _getVlans(self):
        return self.vlans.keys()
    
    def _deleteBridge(self, name):
        if name not in self.bridges.keys():
            raise Exception("Can not find bridge %s"%name)
        
        bridge = self.bridges[name]
        if bridge.attach in bridge.interfaces: bridge.interfaces.remove(bridge.attach)
        if len(bridge.interfaces) != 0:
            logger.debug(self._deleteBridge, "There are still some interfaces(%s) on bridge %s"%(bridge.interfaces, bridge.name))
            return False
        self.bringDown(bridge.name)
        doCmd(['ip', 'link', 'del', bridge.name])
        logger.debug(self._deleteBridge, "Delete bridge %s successfully"%bridge.name)
        return True
        
    def _getInterfaces(self, type):
        """
        @param type : ["pif", "bridge", "tap"]
        @return : dictionary of Interface Objects
        get All Interfaces based on type
        """