#!/usr/bin/python
# mstpctl_restart_cfg_gen -
# On MSTPD restart, readd the bridges which are configured currently.
# Also, parse the interfaces files to replay MSTPCTL confguration
#
#

import os
import glob
import subprocess
import re

mstpctl_br_cfg      = ['maxage', 'fdelay', 'maxhops', 'txholdcount', \
                      'forcevers', 'hello', 'ageing']
mstpctl_br_msti_cfg = ['treeprio']
mstpctl_prt_cfg      = ['portpathcost', 'portadminedge', 'portautoedge', \
                        'portp2p', 'portrestrrole', 'portrestrtcn', \
                        'bpduguard', 'treeportprio', 'treeportcost',
                        'portnetwork', 'portbpdufilter']

#List of bridges which are present currently
bridge_list = []

# Form a list of bridge interfaces present and add the bridge in
# mstpd using mstpctl to get mstpd know about the bridge and bridge ports.
#
def find_bridges():
    global bridge_list
    try:
        dirList = os.listdir('/sys/class/net/')
    except:
        return None
    for br_name in dirList:
	# Verify if interface is a bridge
        br_path = '/sys/class/net/' + br_name + '/bridge'
        if os.path.exists(br_path) :
	    # Verify if bridge was in user_stp mode earlier
            br_stp_file = br_path + '/' + 'stp_state'
            f = open(br_stp_file)
            stp_state = f.read()
            if '2' in stp_state :
                bridge_list.append(br_name)
                os.system ("mstpctl addbridge " + br_name)
            f.close()

# parse_glob() from ifupdown2 modulebase.py
def parse_glob(expr):
    errmsg = ('error parsing glob expression \'%s\'' %expr +
                ' (supported glob syntax: swp1-10 or swp[1-10])')
    start_index = 0
    end_index = 0
    try:
        regexs = [re.compile(r"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"),
                  re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")]
        for r in regexs:
            m = r.match(expr)
            if not m:
                continue
            mlist = m.groups()
            if len(mlist) != 4:
                raise Exception(errmsg + '(unexpected len)')
            prefix = mlist[0]
            suffix = mlist[3]
            start_index = int(mlist[1])
            end_index = int(mlist[2])
    except:
        print(errmsg)
        pass
    if not start_index and not end_index:
        print(errmsg)
        yield expr
    else:
        for i in range(start_index, end_index + 1):
            yield prefix + '%d' %i + suffix

# Parse the interfaces file and extract all mstpctl configuration
# present for bridge ports and replay the commands with the format
# mstpctl set<command> so that mstpd gets the configuration.
#
def apply_interfaces_config():
    if os.path.exists('/sbin/ifquery') == False:
        return
    if (os.path.realpath('/sbin/ifquery')) != '/usr/share/ifupdown2/ifupdown2' :
        return
    try:
        ifaceout=subprocess.check_output(['/sbin/ifquery', '-a'])
    except subprocess.CalledProcessError, e:
	print 'error: ifquery failed (%s)'
        return
    if not ifaceout:
	return
    intf = ''
    bridge = None
    newPrtConfig = False
    for line in ifaceout.splitlines():
        str = line.strip().split(' ')
        len_str = len(str)
        global_expr = False
        if 2 > len_str :
            continue
        # Get the bridge for the bridge port
        if str[0] == 'iface' :
            newPrtConfig = False
            intf = str[1]
            brport_path = '/sys/class/net/' + intf + '/brport'
            brport_br_path = brport_path + '/bridge'
            if os.path.exists(brport_path) :
                newPrtConfig = True
                bridge = ((os.path.realpath(brport_br_path)).partition("net/")[2])

        # Check if bridge is present in bridge_list
        if newPrtConfig and bridge not in bridge_list :
            continue
        if not newPrtConfig and intf not in bridge_list :
            continue

        # Find if mstpctl- keyword is present
        if 'mstpctl-' in str[0] :
            head, temp, mstp_cfg = str[0].partition('mstpctl-')
            # Replay bridge configuration
            if mstp_cfg in mstpctl_br_cfg :
                os.system ("mstpctl set{0} {1} {2}".format(mstp_cfg,intf,str[1]))
            # Replay Bridge CIST configuration, since MSTP not supported,
            # hardcode MSTID to 0.
            elif mstp_cfg in mstpctl_br_msti_cfg:
                os.system ("mstpctl set{0} {1} 0 {2}".format(mstp_cfg,intf,str[1]))
            # Replay Bridge port configuration
            elif mstp_cfg in mstpctl_prt_cfg and not newPrtConfig:
                i = 1
                # Find the list of ports present
                while i < len_str :
                    # If glob expression move on to find expression
                    if str[i] == 'glob' :
                        global_expr = True
                        i = i + 1
                        continue
                    portlist = []
                    # Find the port/glob port expression and configuration
                    portexpr, temp1, prt_cfg = str[i].partition('=')
                    if global_expr :
                        # Find the expanded portlist from glob port expression
                        for bport in parse_glob(portexpr):
                            portlist.append(bport)
                        global_expr = False
                    else:
                        portlist.append(portexpr)
                    # CIST Configuration - hardcode MSTID to 0.
                    for port in portlist:
                        if mstp_cfg == 'treeportprio' or mstp_cfg == 'treeportcost' :
                            os.system ("mstpctl set{0} {1} {2} 0 {3}"\
                                        .format(mstp_cfg,intf, port, prt_cfg))
                        else:
                            os.system ("mstpctl set{0} {1} {2} {3}"\
                                        .format(mstp_cfg,intf, port, prt_cfg))
                    i = i + 1
            elif newPrtConfig and mstp_cfg in mstpctl_prt_cfg:
                # CIST Configuration - hardcode MSTID to 0.
                if mstp_cfg == 'treeportprio' or mstp_cfg == 'treeportcost' :
                    os.system ("mstpctl set{0} {1} {2} 0 {3}"\
                                .format(mstp_cfg, bridge, intf, str[1]))
                else:
                    os.system ("mstpctl set{0} {1} {2} {3}"\
                                .format(mstp_cfg, bridge, intf, str[1]))
    return

find_bridges()
apply_interfaces_config()
