#!/usr/bin/python
# Copyright 2017, 2018, 2019, 2020 Cumulus Networks, Inc.  All rights reserved.
#
# datapath-update --
#
#
#
#
#
#
#

import sys
import getopt
import os
import subprocess
import re
import math
import time
import logging
import logging.handlers
import traceback

import cumulus.platforms
import cumulus.portconfig

import nclu.NetDaemon
from nclu.netshow2 import get_master,get_bond_members

# initialize the global logger
global logger
logger = logging.getLogger('switchd')
fmt = logging.Formatter(fmt='%(name)s %(levelname)s: %(message)s')
handler = logging.handlers.SysLogHandler('/dev/log')
handler.setFormatter(fmt)
logger.setLevel(logging.INFO)
#logger.setLevel(logging.DEBUG)
logger.addHandler(handler)

# By default, read in the traffic.conf file only. Required for MLX platforms.
dryrun_traffic_file_only = True


"""

The datapath-update script is called from
* datapath-config during switchd init
* datapath-checker (using the --dryrun option) from the command line

"""

# ==============================================================================
#
#                           V A L U E __ P A I R
#
# ==============================================================================
class ValuePair(object) :

    def __init__ (self, name, value, format='none') :
        self.name = name
        self.value = value
        self.format = format

    def set_value(self, value) :
        self.value = value

    def get_name(self) :
        return self.name

    def get_value(self) :
        return self.value

# ==============================================================================
#
#                              R E G I S T E R
#
# ==============================================================================
class Register(object) :

    def __init__ (self, name, set_op_str, value, value_pair, comment) :
        self.name        = name
        self.set_op_str  = set_op_str
        self.value       = value
        self.value_pair  = value_pair
        self.modify      = []
        self.comment     = comment

    def add_value(self, value) :
        self.value.append(value)

    def set_value(self, value, index) :
        for i in range(len(self.value), index+1) :
            self.value.append(0)
        self.value[index] = value

    def get_value(self, index) :
        return self.value[index]

    def get_name(self) :
        return self.name

    def add_set_op_str(self, set_op_str) :
        self.set_op_str = set_op_str

    def add_modify(self, modify) :
        self.modify.append(modify)

    def print_entry (self, register, file) :
        file.write("%s %s" % (register.set_op_str, register.name))
        for value in register.value :
            file.write(" %s" % value)
        for pair in register.value_pair :
            if pair.format == 'hex' :
                file.write(" %s=0x%x" % (pair.get_name(), pair.get_value()))
            else :
                file.write(" %s=%s" % (pair.get_name(), pair.get_value()))
        if register.comment != None :
            file.write('    # %s\n' % register.comment)
        else :
            file.write('\n')

    def print_register (self, file) :
        self.print_entry(self, file)
        for mod in self.modify :
            self.generate_print(mod, file)


# ==============================================================================
#
#                       R E G I S T E R __ T E M P L A T E
#
# ==============================================================================
class RegisterTemplate(Register) :

      def __init__ (self, name, set_op_str, loop_list, value_list, value_pair_list) :
            self.loop_list = loop_list
            super(RegisterTemplate,self).__init__(name, set_op_str, value_list, value_pair_list, None)

      def print_entry (self, register, file) :
            file.write("for I=%s,%s,%s '%s %s $I " % (register.loop_list[0],
                                                      register.loop_list[1],
                                                      register.loop_list[2],
                                                      register.set_op_str,
                                                      register.name))
            for value in register.value :
                  file.write(value)
                  for pair in register.value_pair :
                        file.write(" %s=%s" % (pair.get_name(), pair.get_value()))
                  file.write("'\n")

      def print_register (self, file) :
            self.print_entry(self, file)
            for mod in self.modify :
                  self.print_entry(mod, file)

# ==============================================================================
#
#                              H W __ L I M I T S
#
# ==============================================================================
class BufferDesc(object):

      def __init__ (self) :
          self.cell_count = {}
          self.cell_count['ingress'] = { "pg_min"           : 0,
                                         "pg_hdrm"          : 0,
                                         "service_pool"     : 0,
                                         "shared"           : 0,
                                         "global_headroom"  : 0,
                                         "available"        : 0 }
          self.cell_count['egress']  = { "minimum"   : 0,
                                         "available" : 0 }

          self.pg_buffer_limit        = {}
          self.ing_sp_buffer_limit    = {}
          self.ing_sp_buffer_offset   = {}
          self.shared_buffer_limit    = 0
          self.eg_sp_buffer_limit     = {}
          self.queue_buffer_limit     = {}
          self.queue_buffer_unlimited = {}
          self.queue_buffer_color_aware = {}

      def init_desc (self, config_mgr) :
          self.config_mgr = config_mgr

      def init_available_cell_count (self, direction, cell_count) :
            self.cell_count[direction]['available'] = cell_count

      def allocate_cells (self, direction, counter, cell_count) :
            if direction in self.cell_count and counter in self.cell_count[direction] :
                  if self.cell_count[direction]['available'] >= cell_count :
                        self.cell_count[direction]['available'] -= cell_count
                        self.cell_count[direction][counter] += cell_count
                        return 0
                  else :
                        self.config_mgr.report_error('%s %s: %d cells not available (%d remaining)' % (direction,
                                                                                                       counter,
                                                                                                       cell_count,
                                                                                                       self.cell_count[direction]['available']))
            else :
                  self.config_mgr.report_error('direction %s or counter %s is not valid' % (direction, counter))
            return -1

      def get_remaining_cells (self, direction) :
            return self.cell_count[direction]['available']

# ==============================================================================
#
#                              F L O W __ C O N T R O L __ D E S C
#
# ==============================================================================
class FlowControlDesc(object):

    def __init__(self, type, config_manager) :
        self.type = type
        self.config_manager = config_manager
        if type == 'link pause' or type == 'pfc':
            self.fc_flag = 1
        else :
            self.fc_flag = 0
        self.rx    = False
        self.tx    = False
        self.pg_id = config_manager.get_fc_pg_id(type)
        self.cos_list           = []
        self.min_cell_limit     = 0
        self.shared_cell_limit  = 0
        self.shared_cell_floor  = 0
        self.pg_hdrm_cell_limit = 0

    def set_type(self, type):
        self.type = type
        self.pg_id = self.config_manager.get_fc_pg_id(type)
        if type == 'link pause' or type == 'pfc':
            self.fc_flag = 1

# ==============================================================================
#
#                              P O R T __ D E S C
#
# ==============================================================================
class PortDesc(object):

    def __init__(self, config_manager) :

        self.platform = config_manager.platform
        self.chip     = config_manager.chip
        self.config_manager = config_manager

        self.port_count         = 0
        self.port_bw_count      = {}
        self.port_bw_weight     = {}
        self.port_bw            = []
        self.port_map           = {'pipe0' : {}, 'pipe1': {}, 'label' : {}}
        # maintain a list of HSPs per-pipe
        self.hsp_port_map       = {'pipe0' : [], 'pipe1': []}
        self.hsp_bw             = self.chip.hsp_bw
        self.weighted_port_count  = 0
        self.sdk_port_label_list  = []
        self.phy_port_list        = []
        self.label_2_bw           = {}
        self.label_2_logical      = {}
        self.label_2_mmu          = {}
        self.label_2_flow_control = {}
        self.logical_2_label      = {}
        self.linux_2_label        = {}
        self.linux_intf_list      = []

        ports_conf_fname = '/etc/cumulus/ports.conf'
        self.sdk_config  = cumulus.portconfig.SDKConfig(self.platform)
        self.sdk_config.read_config(ports_conf_fname)
        logical_port_num = 0
        for port in self.sdk_config.ports:
            for sub in range(port.num_logical_ports):
                if self.platform.switch.chip.portmap_capable:
                    logical_port_num = port.sdk_logical_port_num(sub)
                if (isinstance(self.chip, cumulus.platform.TridentThreeX5Chip) or \
                    isinstance(self.chip, cumulus.platform.TridentThreeX5_56771_Chip) or \
                    isinstance(self.chip, cumulus.platform.TridentThreeX7Chip) or \
                    isinstance(self.chip, cumulus.platform.TridentThreeX7_56870_Chip) or \
                    isinstance(self.chip, cumulus.platform.TridentThreeX7_56873_Chip) or \
                    isinstance(self.chip, cumulus.platform.TomahawkChip) or \
                    isinstance(self.chip, cumulus.platform.TomahawkPlus_56965_Chip) or \
                    isinstance(self.chip, cumulus.platform.TomahawkPlus_56967_Chip) or \
                    isinstance(self.chip, cumulus.platform.TomahawkTwoChip) or \
                    isinstance(self.chip, cumulus.platform.TomahawkTwo_56970_Chip) or \
                    isinstance(self.chip, cumulus.platform.TomahawkThreeChip) or \
                    isinstance(self.chip, cumulus.platform.TomahawkThree_56980_Chip) or \
                    isinstance(self.chip, cumulus.platform.QumranChip) or \
                    isinstance(self.chip, cumulus.platform.Qumran_88370_Chip) or \
                    isinstance(self.chip, cumulus.platform.Qumran_88375_Chip)):
                    # CM-16096 prevents us from using the logical port number in the 'port <n> TPAU=off...' command
                    # so we use the SDK interface label of the form 'xe0' in place of the logical port number 
                    sdk_port_label = port.sdk_intf(sub)
                else:
                    # The sdk_port_label will be populated by the logical port number (e.g. '1') if it is
                    # available: otherwise it will be populated with the interface label (e.g. 'xe0').
                    # The logical port number is required if the SDK relabels the ports during initialization,
                    # e.g. xe0 becomes ge0 and xe1 becomes xe0.
                    sdk_port_label = port.sdk_config_intf(sub, num_units=1)
                phy_port = port.hw_intf_num(sub) + 1
                if port.num_logical_ports > 1:
                    linux_intf_label = '%s%ss%u' % (port.linux_prefix, port.gang_label, sub)
                elif port.num_logical_ports == 1:
                    if (isinstance(self.chip, cumulus.platform.TomahawkThreeChip)):
                        linux_intf_label = 'sw%s' % (port.gang_label[1:])
                    else:
                        linux_intf_label = '%s%s' % (port.linux_prefix, port.gang_label)
                self._add_port(sdk_port_label, port.speed, logical_port_num, linux_intf_label, phy_port)
                logical_port_num += 1
        sdk_port_label = 'cpu0'
        self._add_port('cpu0', 0, 0, 'None', -1)

        self.finish_port_config()

    def _add_port(self, sdk_port_label, port_bw, logical_port_num, linux_intf_label, phy_port):
        self.sdk_port_label_list.append(sdk_port_label)
        self.label_2_bw[sdk_port_label]           = port_bw
        self.label_2_logical[sdk_port_label]      = logical_port_num
        self.logical_2_label[logical_port_num]    = sdk_port_label
        self.linux_2_label[linux_intf_label]      = sdk_port_label
        self.linux_intf_list.append(linux_intf_label)
        self.label_2_flow_control[sdk_port_label] = FlowControlDesc('none', self.config_manager)
        self._set_port_map(sdk_port_label, logical_port_num, phy_port, port_bw)
        if port_bw == 0:
            return
        self.port_count += 1
        if port_bw not in self.port_bw_count :
            self.port_bw_count[port_bw] = 0
            self.port_bw.append(port_bw)
        self.port_bw_count[port_bw] += 1

    def get_port_count (self) :
        return self.port_count

    def get_port_bw_range_list (self, port_bw) :
        print 'get_port_bw_range_list stubbed out'
        return {}

    def get_weighted_port_count (self) :
        return self.weighted_port_count

    def get_port_bw (self) :
        return self.port_bw

    def get_weighted_per_port_cells (self, memory_cells, port_bw) :
        weight = 0
        if port_bw == 0 :
            return memory_cells
        weighted_cell_count = int((memory_cells / self.weighted_port_count) * self.port_bw_weight[port_bw])
        return weighted_cell_count

    def _set_port_map(self, sdk_port_label, logical_port_num, phy_port, bw):
        base_phy_port_y = 65
        pipe = 'pipe1'
        if phy_port < base_phy_port_y :
            pipe = 'pipe0'
        if bw not in self.port_map[pipe]:
            self.port_map[pipe][bw] = []

        if self.hsp_bw != None and bw >= self.hsp_bw :
            self.hsp_port_map[pipe].append(logical_port_num)

        #XXX do HSPs need to be a part of the per-bw list?
        self.port_map[pipe][bw].append({'label' : sdk_port_label,
                                        'phy'   : phy_port})
        self.port_map['label'][sdk_port_label] = {'phy_port' : phy_port,
                                                  'mmu_port' : -1,
                                                  'uc_queue' : -1}

    def is_hsp_port(self, sdk_port_label):
        if self.hsp_bw == None :
            return False
        bw = self.label_2_bw[sdk_port_label]
        return bw >= self.hsp_bw

    def _set_pipe_mmu_ports(self, pipe, mmu_port, pipe_uc_queue_base) :
        is_t2plus = False
        if isinstance(self.chip, cumulus.platform.TridentTwoPlusChip) :
            is_t2plus = True

        # the CPU port
        self.port_map['label']['cpu0']['mmu_port'] = 52
        self.port_map['label']['cpu0']['uc_queue'] = 2000

        # HSP ports get the lowest MMU port numbers in the pipe (sorted
        # by logical port number across all port speeds). Also HSP ports 
        # have 10 UC queues.
        if is_t2plus:
            # uc queue base is calculated at a fixed offset for each port
            mmu_offset = mmu_port
            if mmu_port >= 64 :
                mmu_offset = mmu_port - 64
            uc_queue_base = pipe_uc_queue_base + (mmu_offset * 10)
        else :
            uc_queue_base = pipe_uc_queue_base
        for logical_port_num in self.hsp_port_map[pipe]:
            sdk_port_label = self.logical_2_label[logical_port_num]
            self.port_map['label'][sdk_port_label]['mmu_port'] = mmu_port
            self.port_map['label'][sdk_port_label]['uc_queue'] = uc_queue_base
            mmu_port += 1
            uc_queue_base += 10

        bw_list = self.port_map[pipe].keys()
        bw_list.sort(reverse=True)

        # MMU port assignment for non-hsp UC queues
        # non-hsp ports have 12 UC queues
        for bw in bw_list :
            phy_port_dict = {}
            for port_info in self.port_map[pipe][bw] :
                phy_port_dict[port_info['phy']] = port_info['label']
            phy_port_list = phy_port_dict.keys()
            phy_port_list.sort()

            if is_t2plus:
                # uc queue base is calculated at a fixed offset for each port
                mmu_offset = mmu_port
                if mmu_port >= 64 :
                    mmu_offset = mmu_port - 64
                uc_queue_base = pipe_uc_queue_base + (mmu_offset * 12)
            else :
                # the base queue must be divisible by 4 to support PFC
                # (per comment in SDK 6.4.8)
                uc_queue_base = (uc_queue_base + 3) & (~3);
            for phy_port in phy_port_list :
                sdk_port_label = phy_port_dict[phy_port]
                #hsp ports have already been assigned mmu ports
                if not self.is_hsp_port(sdk_port_label):
                    self.port_map['label'][sdk_port_label]['mmu_port'] = mmu_port
                    self.port_map['label'][sdk_port_label]['uc_queue'] = uc_queue_base
                    mmu_port += 1
                    uc_queue_base += 12

    def finish_port_config(self):
        # keep the HSP lists sorted by logical port
        self.hsp_port_map['pipe0'].sort()
        self.hsp_port_map['pipe1'].sort()

        # used for T2/T2+ only
        self._set_pipe_mmu_ports('pipe0', 0, 0)
        self._set_pipe_mmu_ports('pipe1', 64, 2048)

        # process port description
        min_bw = sys.maxint
        for bw in self.port_bw_count :
            if bw < min_bw :
                min_bw = bw
        for bw in self.port_bw_count :
            weight = bw / min_bw
            self.port_bw_weight[bw] = weight

        for bw in self.port_bw_count :
            self.weighted_port_count += (self.port_bw_count[bw] * self.port_bw_weight[bw])

        #self.dump_port_map()

    def dump_port_map(self):
        sys.stderr.write('CL portmap\n')
        for sdk_port_label in self.sdk_port_label_list:
            sys.stderr.write('%s; mmu: %d; uc_q: %d\n' % 
                   (sdk_port_label,
                    self.port_map['label'][sdk_port_label]['mmu_port'],
                    self.port_map['label'][sdk_port_label]['uc_queue']))

# ==============================================================================
#
#                       P A R A M E T E R __ M A N A G E R
#
# ==============================================================================
class ParameterManager(object):

    def __init__(self, chip, config_manager) :
        self.chip           = chip
        self.config_manager = config_manager
        self.port_desc      = config_manager.port_desc
        self.parameter_group_list = []

    def generate_output_objects (self) :
        pass

    def print_output_objects (self, file_dict) :
        if self.config_manager.traffic.disable_custom_datapath_config == 1 :
            return
        for parameter_group in self.parameter_group_list :
            parameter_group.write_to_file(file_dict['parameter'])

# ==============================================================================
#
#             E S W __ B U F F E R __ P A R A M E T E R __ M G R
#
# ==============================================================================
class ESW_BufferParameterMgr(ParameterManager) :

    def __init__(self, chip, config_manager) :
        super(ESW_BufferParameterMgr,self).__init__(chip, config_manager)

    def generate_sp_limits(self, label, sp_config) :
        num_service_pools = self.config_manager.hardware.num_service_pools
        yellow_percent    = self.config_manager.traffic.yellow_limit_percent
        red_percent       = self.config_manager.traffic.red_limit_percent

        group = ParameterGroup('service pool size')
        for sp_id in range(num_service_pools) :
            service_pool = sp_config.pool_dict[sp_id]
            if service_pool.configured != True :
                sp_limit = 0
            else :
                sp_limit = service_pool.percent
                group.add_parameter(Parameter('buf.%spool%d.size' % (label, sp_id),
                                              sp_limit,
                                              suffix='%'))

                yellow_limit = 0
                if yellow_percent != None :
                    yellow_limit = (yellow_percent / 100) * sp_limit
                else :
                    yellow_limit = sp_limit
                    group.add_parameter(Parameter('buf.%spool%d.yellow_size' % (label, sp_id),
                                                  yellow_limit,
                                                  suffix='%'))
                red_limit = 0
                if red_percent != None :
                    red_limit = (red_percent / 100) * sp_limit
                else :
                    red_limit = sp_limit
                group.add_parameter(Parameter('buf.%spool%d.red_size' % (label, sp_id),
                                              red_limit,
                                              suffix='%'))

        self.parameter_group_list.append(group)

# ==============================================================================
#
#        E S W __ I N G R __ B U F F E R __ P A R A M E T E R __ M G R
#
# ==============================================================================
class ESW_IngrBufferParameterMgr(ESW_BufferParameterMgr):

    def __init__(self, chip, config_manager) :
        super(ESW_IngrBufferParameterMgr,self).__init__(chip, config_manager)

    def generate_pg_limits (self) :
        pg_buffer_limit     = self.config_manager.buffer_desc.pg_buffer_limit
        hdrm_group          = ParameterGroup('priority group headroom')
        min_group           = ParameterGroup('per priority group guarantee')
        shared_group        = ParameterGroup('per priority group shared limit and shared resume')

        lossless_flag = 0
        if (isinstance(self.chip, cumulus.platform.TomahawkThreeChip)):
            hdrm_group.add_parameter(Parameter('mmu_config_override', 0))

            #Append Scheduler profile ID 0 , valid only for TH3
            hdrm_group.add_parameter(Parameter('scheduler_profile_0.valid', 1))

            #Based on the config that TH3 has 8 UC and 4 MC Qs configured
            #Adding 8 Unicast queues
            hdrm_group.add_parameter(Parameter('scheduler_profile_0.num_unicast_queue', '1,1,1,1,1,1,1,1,0,0,0,0'))

            #Adding 4 multicast queues
            hdrm_group.add_parameter(Parameter('scheduler_profile_0.num_multicast_queue', '0,0,0,0,0,0,0,0,1,1,1,1'))

            #Setting Strict Priority for Ucsat Q7
            hdrm_group.add_parameter(Parameter('scheduler_profile_0.sched_strict_priority', '0,0,0,0,0,0,0,1,0,0,0,0'))

            #Setting that flowcontrol can be enabled on both UC and MC
            hdrm_group.add_parameter(Parameter('scheduler_profile_0.flow_control_only_unicast', '0,0,0,0,0,0,0,0,0,0,0,0'))

            #Applying the scheduler profile to all the ports
            hdrm_group.add_parameter(Parameter('scheduler_profile_map', '0'))

        for sdk_port_label in self.config_manager.port_desc.sdk_port_label_list :
            value_dict = {}
            bw = self.config_manager.port_desc.label_2_bw[sdk_port_label]
            pg_id_list = pg_buffer_limit.keys()
            fc_desc = self.config_manager.port_desc.label_2_flow_control[sdk_port_label]
            if fc_desc.type == 'link pause':
                pg_id_list = [fc_desc.pg_id]
            elif fc_desc.type == 'pfc':
                pg_id_list.append(fc_desc.pg_id)

            for pg_id in pg_id_list:
                # generate the weighted per-port, per-pg buffer allocation
                # field values for the minimum and pg headroom buffers

                fc_tx_flag = 0
                if fc_desc.fc_flag and pg_id == fc_desc.pg_id and fc_desc.tx:
                    fc_tx_flag = 1

                # set the pg min cell limit
                pg_min_cell_limit = 0
                if sdk_port_label == "cpu0" :
                    pg_min_cell_limit = self.config_manager.buffer_desc.cpu_pg_min_cells
                elif fc_tx_flag:
                    pg_min_cell_limit = fc_desc.min_cell_limit;
                elif pg_buffer_limit[pg_id]['pg_min'] > 0 :
                    per_port_min_cells = self.port_desc.get_weighted_per_port_cells(pg_buffer_limit[pg_id]['pg_min'], bw)
                    pg_min_cell_limit = int(per_port_min_cells)
                if pg_min_cell_limit < 100 :
                    # pg_min_cell_limit = 100 # XXX debugging
                    pass
                min_group.add_parameter(Parameter('buf.prigroup%s.guarantee_%s' % (pg_id,
                                                                                   sdk_port_label),
                                                                                   pg_min_cell_limit))
                if (isinstance(self.chip, cumulus.platform.TomahawkThreeChip)):
                    min_group.add_parameter(Parameter('buf.prigroup%s.priority_group_lossless_%s' % (pg_id,
                                                                                   sdk_port_label),
                                                                                   fc_tx_flag))

                # set the shared buffer limit
                if fc_tx_flag:
                    pg_shared_cell_limit = fc_desc.shared_cell_limit
                    pg_shared_cell_floor = fc_desc.shared_reset_floor
                elif pg_buffer_limit[pg_id]['shared'] > 0 :
                    pg_shared_cell_limit = pg_buffer_limit[pg_id]['shared']
                    reset_floor = pg_shared_cell_limit - 500
                    if reset_floor < 0 :
                        reset_floor = 0
                    pg_shared_cell_floor = reset_floor
                shared_group.add_parameter(Parameter('buf.prigroup%s.pool_scale_%s' % \
                                                     (pg_id, sdk_port_label), -1))
                shared_group.add_parameter(Parameter('buf.prigroup%s.pool_limit_%s' % \
                                                     (pg_id, sdk_port_label),
                                                     pg_shared_cell_limit))
                shared_group.add_parameter(Parameter('buf.prigroup%s.pool_resume_%s' % \
                                                     (pg_id, sdk_port_label), pg_shared_cell_floor))

                # set the global headroom enable and pg headroom limit fields
                gbl_hdrm_enable    = 1
                pg_hdrm_cell_limit = 0
                if fc_tx_flag:
                    pg_hdrm_cell_limit = fc_desc.pg_hdrm_cell_limit
                    lossless_flag = 1
                hdrm_group.add_parameter(Parameter('buf.prigroup%s.device_headroom_enable_%s' % \
                                                   (pg_id, sdk_port_label), gbl_hdrm_enable))
                hdrm_group.add_parameter(Parameter('buf.prigroup%s.headroom_%s' % \
                                                   (pg_id, sdk_port_label), pg_hdrm_cell_limit))
        hdrm_group.add_parameter(Parameter('mmu_lossless', lossless_flag))

        self.parameter_group_list.append(min_group)
        self.parameter_group_list.append(shared_group)
        self.parameter_group_list.append(hdrm_group)

    def generate_sp_mapping (self) :

        num_priority_groups = self.config_manager.hardware.num_priority_groups

        group = ParameterGroup('service pool mapping')
        value_str = ''
        for pg_id in range(num_priority_groups) :
            if pg_id in self.config_manager.priority_group.pg2sp :
                sp_id = self.config_manager.priority_group.pg2sp[pg_id]
            else :
                sp_id = 0
            if pg_id > 0 :
                value_str += ','
            value_str += '%s' % sp_id

        if (isinstance(self.chip, cumulus.platform.TomahawkThreeChip)):
            group.add_parameter(Parameter('buf.mapprofile0.priority_group_to_service_pool', value_str))
            group.add_parameter(Parameter('buf.mapprofile0.priority_group_to_headroom_pool', value_str))
            group.add_parameter(Parameter('buf.mapprofile1.priority_group_to_service_pool', value_str))
            group.add_parameter(Parameter('buf.mapprofile1.priority_group_to_headroom_pool', value_str))
            group.add_parameter(Parameter('buf.mapprofile2.priority_group_to_service_pool', value_str))
            group.add_parameter(Parameter('buf.mapprofile2.priority_group_to_headroom_pool', value_str))

        group.add_parameter(Parameter('buf.map.prigroup.pool', value_str))
        self.parameter_group_list.append(group)

    def generate_port_limits (self) :

        group = ParameterGroup('per-port minimum guaranteed and shared buffers')

        sp_config = self.config_manager.ingress_service_pool
        for sp_id in range(self.config_manager.hardware.num_service_pools) :
            service_pool = sp_config.pool_dict[sp_id]
            """
            if service_pool.configured != True :
                shared_limit = 0
            else :
                # shared_limit = service_pool.percent
            """
            # keep the per-port limit effectively unlimited
            shared_limit = 90

            # thdi_port_sp_config_x port_sp_min_limit, resume_limit, max_limit
            group.add_parameter(Parameter('buf.ingportpool%d.guarantee' % sp_id, 0))
            group.add_parameter(Parameter('buf.ingportpool%d.pool_limit' % sp_id,
                                          shared_limit, suffix='%'))
            group.add_parameter(Parameter('buf.ingportpool%d.pool_resume' % sp_id, 0))
        self.parameter_group_list.append(group)

    def generate_packet_size (self) :
        group = ParameterGroup('maximum packet size')
        group.add_parameter(Parameter('pkt_size', self.config_manager.hardware.max_frame_cells ))
        self.parameter_group_list.append(group)

    def generate_ing_sp_limits (self) :
        self.generate_sp_limits('ingr', self.config_manager.ingress_service_pool)

    def generate_output_objects (self) :
        self.generate_packet_size()
        self.generate_port_limits()
        self.generate_pg_limits()
        self.generate_sp_mapping()
        self.generate_ing_sp_limits()

# ==============================================================================
#
#       E S W __ I N G R __ P R I __ M A P __ P A R A M E T E R __ M G R
#
# ==============================================================================
class ESW_IngrPriMapParameterMgr(ParameterManager):

    def __init__(self, chip, config_manager) :
        super(ESW_IngrPriMapParameterMgr,self).__init__(chip, config_manager)

    def generate_pg_map (self) :
        max_priority_value        = self.config_manager.hardware.num_priorities
        priority_value_dict       = {}
        pause_priority_value_dict = {}
        for cos_id in reversed(xrange(max_priority_value)) :
            if cos_id in self.config_manager.priority_group.cos2group :
                pg_id = self.config_manager.priority_group.cos2group[cos_id]
            else :
                pg_id = 0
            if cos_id < 0 :
                pg_id = 7
            priority_value_dict[cos_id]       = pg_id
            pause_priority_value_dict[cos_id] = 7
        sorted_keys = priority_value_dict.keys()
        sorted_keys.sort()
        default_value_string = '%s' % priority_value_dict[sorted_keys[0]]
        for key in sorted_keys[1:] :
            default_value_string += ',%s' % priority_value_dict[key]
        pause_value_string = '%s' % pause_priority_value_dict[sorted_keys[0]]
        for key in sorted_keys[1:] :
            pause_value_string += ',%s' % pause_priority_value_dict[key]
        group = ParameterGroup("priority to priority group mapping")
        profile=0;
        profile_0_done=0;
        profile_1_done=0;
        profile_2_done=0
        for sdk_port_label in self.config_manager.port_desc.sdk_port_label_list :
            profile = 0;
            value_string = default_value_string
            fc_desc = self.config_manager.port_desc.label_2_flow_control[sdk_port_label]
            if fc_desc.type == 'link pause' :
                value_string = pause_value_string
                profile = 1;
            elif fc_desc.type == 'pfc' :
                pfc_priority_value_dict = priority_value_dict
                for cos_id in fc_desc.cos_list:
                    pfc_priority_value_dict[cos_id] = fc_desc.pg_id
                sorted_keys = pfc_priority_value_dict.keys()
                sorted_keys.sort()
                value_string = '%s' % pfc_priority_value_dict[sorted_keys[0]]
                for key in sorted_keys[1:] :
                    value_string += ',%s' % pfc_priority_value_dict[key]
                # print value_string
                profile = 2;

            # port label syntax is different for csv syntax
            if (isinstance(self.chip, cumulus.platform.TomahawkThreeChip)):
                if (profile == 0) and (profile_0_done == 0):
                    group.add_parameter(Parameter('buf.mapprofile0.profile_valid', '1'))
                    group.add_parameter(Parameter('buf.mapprofile0.input_pri_to_priority_group_uc', value_string))
                    group.add_parameter(Parameter('buf.mapprofile0.input_pri_to_priority_group_mc', value_string))
                    group.add_parameter(Parameter('buf.mapprofile0.pfcclass_to_priority_group', value_string))
                    profile_0_done = 1;
                if (profile == 1) and (profile_1_done == 0):
                    group.add_parameter(Parameter('buf.mapprofile1.profile_valid', '1'))
                    group.add_parameter(Parameter('buf.mapprofile1.input_pri_to_priority_group_uc', value_string))
                    group.add_parameter(Parameter('buf.mapprofile1.input_pri_to_priority_group_mc', value_string))
                    group.add_parameter(Parameter('buf.mapprofile1.pfcclass_to_priority_group', value_string))
                    profile_1_done = 1;
                if (profile == 2) and (profile_2_done == 0):
                    group.add_parameter(Parameter('buf.mapprofile2.profile_valid', '1'))
                    group.add_parameter(Parameter('buf.mapprofile2.input_pri_to_priority_group_uc', value_string))
                    group.add_parameter(Parameter('buf.mapprofile2.input_pri_to_priority_group_mc', value_string))
                    group.add_parameter(Parameter('buf.mapprofile2.pfcclass_to_priority_group', value_string))
                    profile_2_done = 1;

                group.add_parameter(Parameter('buf.port.input_pri_to_priority_group_profile_idx_%s' % sdk_port_label, profile))
                group.add_parameter(Parameter('buf.port.priority_group_profile_idx_%s' % sdk_port_label, profile))
            group.add_parameter(Parameter('buf.map.pri.prigroup_%s' % sdk_port_label, value_string))
        self.parameter_group_list.append(group)

    def generate_output_objects (self) :
        self.generate_pg_map()

# ==============================================================================
#
#         E S W __ E G R __ B U F F E R __ P A R A M E T E R __ M G R
#
# ==============================================================================
class ESW_EgrBufferParameterMgr(ESW_BufferParameterMgr) :

    def __init__(self, chip, config_manager) :
        super(ESW_EgrBufferParameterMgr,self).__init__(chip, config_manager)

    def generate_cos_map (self) :
        pass

    # ------------------------------------------------------------------
    #
    #             g e t __ q __ c o n f i g __ v a l u e s
    #
    # ------------------------------------------------------------------
    def get_q_config_values (self, q_type, q_id) :

        queue_buffer_limit       = self.config_manager.buffer_desc.queue_buffer_limit
        queue_buffer_unlimited   = self.config_manager.buffer_desc.queue_buffer_unlimited
        queue_buffer_color_aware = self.config_manager.buffer_desc.queue_buffer_color_aware
        values                   = {}

        sp_id = self.config_manager.priority_group.q2sp[q_type][q_id]
        values['spid'] = sp_id
        values['q_group_id'] = sp_id # not used, avoids an error message
        if queue_buffer_unlimited[q_type][q_id] == True :
            values['limit_enable'] = 0
            values['shared_limit'] = 0
        else :
            values['limit_enable'] = 1

            # allow access to the service pool buffer
            if queue_buffer_color_aware[q_type][q_id] :
                buffer_type = 'shared green'
            else :
                buffer_type = 'shared'
            if buffer_type in queue_buffer_limit[q_type][q_id] :
                q_shared_cells = queue_buffer_limit[q_type][q_id][buffer_type]
            else :
                q_shared_cells = 0
            values['shared_limit']  = q_shared_cells

        values['color_limit_enable'] = 0
        values['yellow_limit']       = 0
        values['red_limit']          = 0
        if queue_buffer_unlimited[q_type][q_id] == False \
               and queue_buffer_color_aware[q_type][q_id] :
            # set the red and yellow limits
            values['color_limit_enable'] = 1
            buffer_type = 'shared yellow'
            if buffer_type in queue_buffer_limit[q_type][q_id] :
                q_shared_cells = queue_buffer_limit[q_type][q_id][buffer_type]
            else :
                q_shared_cells = 0
            values['yellow_limit'] = q_shared_cells

            buffer_type = 'shared red'
            if buffer_type in queue_buffer_limit[q_type][q_id] :
                q_shared_cells = queue_buffer_limit[q_type][q_id][buffer_type]
            else :
                q_shared_cells = 0
            values['red_limit'] = q_shared_cells

        values['dynamic_limit'] = 0

        return values

    def get_q_min_value (self, q_type, q_id, bw) :

        queue_buffer_limit = self.config_manager.buffer_desc.queue_buffer_limit
        buffer_type              = 'minimum'
        values                   = {}

        if buffer_type in queue_buffer_limit[q_type][q_id] :
            q_min_cells = queue_buffer_limit[q_type][q_id][buffer_type]
        else :
            q_min_cells = 0
        values['min_limit']  = self.port_desc.get_weighted_per_port_cells(q_min_cells, bw)

        return values

    def generate_queue_type_regs(self, q_type, swp_flag) :

        num_priority_groups = self.config_manager.hardware.num_priority_groups
        if swp_flag == True :
            port_suffix = ''
        else :
            port_suffix = '_cpu0'

        qgroup_dict = {}
        if q_type == 'uc':
            for q_id in range(12):
                qgroup_dict[q_id] = 0

        for q_id in self.config_manager.priority_group.q2sp[q_type] :
            values = self.get_q_config_values (q_type, q_id)
            title_prefix = 'UC'
            q_prefix = ''
            if q_type == 'mc' :
                title_prefix = 'MC'
                q_prefix = 'm'
            elif q_type == 'cpu' :
                title_prefix = 'CPU'
                q_prefix = 'm'
            group = ParameterGroup('%s Queue %d buffers' % (title_prefix, q_id))
            if q_type == 'uc' :
                group.add_parameter(Parameter('buf.queue%d.qgroup_id%s' % (q_id, port_suffix), -1))
                qgroup_dict[q_id] = 1
            if q_type != 'cpu' :
                group.add_parameter(Parameter('buf.%squeue%d.pool' % (q_prefix, q_id), values['spid']))
            if values['limit_enable'] == 1 :
                q_pool_limit = values['shared_limit']
        
                #TH3 needs this to be set for Qs which do not have unlimited egress buffer
                #It sets the SHARED LIMIT and SHARED ALPHA to appropriate value for correct
                #distribution   #TH3 needs this to be set for Qs which do not have unlimited egress buffer
                #It sets the SHARED LIMIT and SHARED ALPHA to appropriate value for correct
                #distribution . SHARED_ALPHA gets set by picking up LSB(1 Byte) from SHARED_LIMIT
                if (isinstance(self.chip, cumulus.platform.TomahawkThreeChip)):
                    q_pool_limit = (q_pool_limit & 0xfffffff0) | (0x9)
                
                group.add_parameter(Parameter('buf.%squeue%d.discard_enable%s' % (q_prefix, q_id, port_suffix), 1))
                group.add_parameter(Parameter('buf.%squeue%d.pool_scale%s' % (q_prefix, q_id, port_suffix), -1))
                group.add_parameter(Parameter('buf.%squeue%d.pool_limit%s' % (q_prefix, q_id, port_suffix), q_pool_limit))
                group.add_parameter(Parameter('buf.%squeue%d.pool_resume%s' % (q_prefix, q_id, port_suffix), q_pool_limit - 100))

                if values['color_limit_enable'] :
                    group.add_parameter(Parameter('buf.%squeue%d.color_discard_enable%s' % (q_prefix, q_id, port_suffix), 1))
                    group.add_parameter(Parameter('buf.%squeue%d.pool_yellow_limit%s' % (q_prefix, q_id, port_suffix), values['shared_yellow_limit']))
                    group.add_parameter(Parameter('buf.%squeue%d.pool_yellow_resume%s' % (q_prefix, q_id, port_suffix), values['shared_yellow_limit'] - 100))
                    group.add_parameter(Parameter('buf.%squeue%d.pool_red_limit%s' % (q_prefix, q_id, port_suffix), values['shared_red_limit']))
                    group.add_parameter(Parameter('buf.%squeue%d.pool_red_resume%s' % (q_prefix, q_id, port_suffix), values['shared_red_limit'] - 100))
                else :
                    group.add_parameter(Parameter('buf.%squeue%d.color_discard_enable%s' % (q_prefix, q_id, port_suffix), 0))
                    group.add_parameter(Parameter('buf.%squeue%d.pool_yellow_limit%s' % (q_prefix, q_id, port_suffix), 0))
                    group.add_parameter(Parameter('buf.%squeue%d.pool_yellow_resume%s' % (q_prefix, q_id, port_suffix), 0))
                    group.add_parameter(Parameter('buf.%squeue%d.pool_red_limit%s' % (q_prefix, q_id, port_suffix), 0))
                    group.add_parameter(Parameter('buf.%squeue%d.pool_red_resume%s' % (q_prefix, q_id, port_suffix), 0))

                if swp_flag == False :
                    sdk_port_label_list = ['cpu0']
                else :
                    sdk_port_label_list = self.config_manager.port_desc.sdk_port_label_list
                for sdk_port_label in sdk_port_label_list :
                    if swp_flag == True and sdk_port_label == 'cpu0' :
                        continue
                    bw = self.config_manager.port_desc.label_2_bw[sdk_port_label]
                    values = self.get_q_min_value(q_type, q_id, bw)
                    min_limit_port_suffix = '_%s' % sdk_port_label
                    group.add_parameter(Parameter('buf.%squeue%d.guarantee%s' % (q_prefix, q_id, min_limit_port_suffix), values['min_limit']))
            else :
                group.add_parameter(Parameter('buf.%squeue%d.discard_enable%s' % (q_prefix, q_id, port_suffix), 0))

            # queue.qgroup_guarantee_enable bool ??  trident only
            self.parameter_group_list.append(group)

        # create a qgroup initialization parameter for unused queues: SDK work around
        group = ParameterGroup('Queue Group Init')
        parameter_count = 0
        for q_id, init_flag in qgroup_dict.iteritems():
            if init_flag == 0:
                group.add_parameter(Parameter('buf.queue%d.qgroup_id%s' % (q_id, port_suffix), -1))
                parameter_count += 1
        if parameter_count > 0:
            self.parameter_group_list.append(group)

    def generate_queue_regs(self) :
        self.generate_queue_type_regs('uc', True)
        self.generate_queue_type_regs('mc', True)
        self.generate_queue_type_regs('cpu', False)

    def generate_sp_shared_limits(self) :
        self.generate_sp_limits('egr', self.config_manager.egress_service_pool)

    def generate_output_objects (self) :
        # TOO has per port scheduling registers in a table on page 518
        self.generate_cos_map()
        self.generate_queue_regs()
        self.generate_sp_shared_limits()

# ==============================================================================
#
#                          C H I P __ M A N A G E R
#
# ==============================================================================
class ChipManager(object):

    def __init__(self, chip, config_manager) :
        self.manager_list = []
        self.config_manager = config_manager
        if getattr(self, 'creator_list', None) == None :
            self.creator_list = []
        for creator in self.creator_list :
            self.manager_list.append(creator(chip, config_manager))

    def generate_output_objects(self) :
        for manager in self.manager_list :
            manager.generate_output_objects()

    def print_output_objects(self, file_dict) :
        for type, file in file_dict.iteritems() :
            if self.config_manager.traffic.disable_custom_datapath_config == 1 :
                file.write('# custom datapath configuration is disabled from /etc/cumulus/datapath/traffic.conf\n')
        for manager in self.manager_list :
            manager.print_output_objects(file_dict)

# ==============================================================================
#
#              T 2__ C H I P __ M A N A G E R
#
# ==============================================================================
class T2_ChipManager(ChipManager):

    def __init__(self, chip, config_manager) :

        self.creator_list = [ESW_EgrBufferParameterMgr,
                             ESW_IngrBufferParameterMgr,
                             ESW_IngrPriMapParameterMgr,
                             ESW_PortsRegisterManager,
                             ForwardingRegisterManager]
        super(T2_ChipManager,self).__init__(chip, config_manager)

# ==============================================================================
#
#           T R I D E N T 3 __ X5 __ C H I P __ M A N A G E R
#
# ==============================================================================
class TridentThreeX5_ChipManager(ChipManager):

    def __init__(self, chip, config_manager) :
        self.creator_list = []
        self.creator_list = [ ESW_EgrBufferParameterMgr,
                              ESW_IngrBufferParameterMgr,
                              ESW_IngrPriMapParameterMgr,
                              ESW_PortsRegisterManager,
                              ForwardingRegisterManager ]
        super(TridentThreeX5_ChipManager,self).__init__(chip, config_manager)

# ==============================================================================
#
#           T R I D E N T 3 __ X7 __ C H I P __ M A N A G E R
#
# ==============================================================================
class TridentThreeX7_ChipManager(ChipManager):

    def __init__(self, chip, config_manager) :
        self.creator_list = []
        self.creator_list = [ ESW_EgrBufferParameterMgr,
                              ESW_IngrBufferParameterMgr,
                              ESW_IngrPriMapParameterMgr,
                              ESW_PortsRegisterManager,
                              ForwardingRegisterManager ]
        super(TridentThreeX7_ChipManager,self).__init__(chip, config_manager)

# ==============================================================================
#
#              T O M A H A W K __ C H I P __ M A N A G E R
#
# ==============================================================================
class TomahawkChipManager(ChipManager):

    def __init__(self, chip, config_manager) :
        self.creator_list = []
        self.creator_list = [ ESW_EgrBufferParameterMgr,
                              ESW_IngrBufferParameterMgr,
                              ESW_IngrPriMapParameterMgr,
                              ESW_PortsRegisterManager,
                              ForwardingRegisterManager ]
        super(TomahawkChipManager,self).__init__(chip, config_manager)

# ==============================================================================
#
#              M A V E R I C K __ C H I P __ M A N A G E R
#
# ==============================================================================
class MaverickChipManager(ChipManager):

    def __init__(self, chip, config_manager) :
        self.creator_list = []
        self.creator_list = [ ESW_EgrBufferParameterMgr,
                              ESW_IngrBufferParameterMgr,
                              ESW_IngrPriMapParameterMgr,
                              ESW_PortsRegisterManager,
                              ForwardingRegisterManager ]
        super(MaverickChipManager,self).__init__(chip, config_manager)

# ==============================================================================
#
#              H E L I X 4__ C H I P __ M A N A G E R
#
# ==============================================================================
class Helix4_ChipManager(ChipManager):

    def __init__(self, chip, config_manager) :
        self.creator_list = (ESW_EgrBufferParameterMgr,
                             ESW_IngrBufferParameterMgr,
                             ESW_IngrPriMapParameterMgr,
                             ESW_PortsRegisterManager,
                             ForwardingRegisterManager)
        super(Helix4_ChipManager,self).__init__(chip, config_manager)

# ==============================================================================
#
#              Q U M R A N - M X __ C H I P __ M A N A G E R
#
# ==============================================================================
class QumranChipManager(ChipManager):

    def __init__(self, chip, config_manager) :
        self.creator_list = []
        self.creator_list = [ ESW_EgrBufferParameterMgr,
                              ESW_IngrBufferParameterMgr,
                              ESW_IngrPriMapParameterMgr,
                              ESW_PortsRegisterManager,
                              ForwardingRegisterManager ]
        super(QumranChipManager,self).__init__(chip, config_manager)

# ==============================================================================
#
#              S P E C T R U M __ C H I P __ M A N A G E R
#
# ==============================================================================
class SpectrumChipManager(ChipManager):

    def __init__(self, chip, config_manager) :
        self.creator_list = []
        super(SpectrumChipManager,self).__init__(chip, config_manager)

# ==============================================================================
#
#                    C H I P __ M A N A G E R __ F A C T O R Y
#
# ==============================================================================
class ChipManagerFactory(object):

    map = { 'TridentTwo_56850_Chip'     : T2_ChipManager,
            'TridentTwo_56854_Chip'     : T2_ChipManager,
            'TridentTwoPlus_56860_Chip' : T2_ChipManager,
            'TridentTwoPlus_56864_Chip' : T2_ChipManager,
            'TridentThreeX5Chip'        : TridentThreeX5_ChipManager,
            'TridentThreeX5_56771_Chip' : TridentThreeX5_ChipManager,
            'TridentThreeX7Chip'        : TridentThreeX7_ChipManager,
            'TridentThreeX7_56870_Chip' : TridentThreeX7_ChipManager,
            'TridentThreeX7_56873_Chip' : TridentThreeX7_ChipManager,
            'Helix4Chip'                : Helix4_ChipManager,
            'TomahawkChip'              : TomahawkChipManager,
            'TomahawkPlus_56965_Chip'   : TomahawkChipManager,
            'TomahawkPlus_56967_Chip'   : TomahawkChipManager,
            'TomahawkTwoChip'           : TomahawkChipManager,
            'TomahawkTwo_56970_Chip'    : TomahawkChipManager,
            'TomahawkThreeChip'         : TomahawkChipManager,
            'TomahawkThree_56980_Chip'  : TomahawkChipManager,
            'MaverickChip'              : MaverickChipManager,
            'QumranChip'                : QumranChipManager,
            'Qumran_88370_Chip'         : QumranChipManager,
            'Qumran_88375_Chip'         : QumranChipManager,
            'SpectrumChip'              : SpectrumChipManager }

    @classmethod
    def get_new_manager(self, chip, config_manager) :
        chip_name = chip.__class__.__name__
        if chip_name not in self.map :
                        return None
        manager = self.map[chip_name](chip, config_manager)
        return manager

# ==============================================================================
#
#                              O U T P U T __ O B J E C T
#
# ==============================================================================
class OutputObject(object) :

    def __init__ (self, name, value, comment) :
        self.name     = name
        self.value    = value
        self.comment  = comment

    def write_to_file (self, file) :
        pass

# ==============================================================================
#
#                              P A R A M E T E R
#
# ==============================================================================
class Parameter(OutputObject) :

    def __init__ (self, name, value, suffix='', comment=None) :
        self.suffix = suffix
        super(Parameter,self).__init__(name, value, comment)

    def write_to_file (self, file) :
        if self.comment != None :
            file.write("%s=%s%s  # %s\n" % (self.name,
                                            str(self.value),
                                            self.suffix,
                                            self.comment))
        else :
            file.write("%s=%s%s\n" % (self.name, str(self.value), self.suffix))

# ==============================================================================
#
#                         P A R A M E T E R __ G R O U P
#
# ==============================================================================
class ParameterGroup(object) :

    def __init__ (self, comment=None) :
        self.parameter_list = []
        self.comment        = comment

    def add_parameter (self, parameter) :
        self.parameter_list.append(parameter)

    def write_to_file (self, file) :
        if self.comment != None :
            file.write('# %s\n' % self.comment)
        for parameter in self.parameter_list :
            parameter.write_to_file(file)
            file.write('\n')

# ==============================================================================
#
#                          R E G I S T E R __ M A N A G E R
#
# ==============================================================================
class RegisterManager(object):

    def __init__(self, chip, config_manager) :
        self.chip          = chip
        self.reg_dict      = {}
        self.reg_list      = []
        self.port_cmd_list = []
        self.section_dict  = {}
        self.config_manager = config_manager
        self.port_desc      = config_manager.port_desc
        self.hardware       = config_manager.hardware
        self.header_str     = ""

    # ------------------------------------------------------------------
    #
    #                 a d d __ n e w __ r e g i s t e r
    #
    # ------------------------------------------------------------------
    def add_new_register(self, name, section, operation, value_list, value_pairs, comment=None) :
        register = Register(name, operation, value_list, value_pairs, comment)
        self.reg_dict[name] = register
        self.reg_list.append(register)
        self.section_dict[section].append(register)

    # ------------------------------------------------------------------
    #
    #                 a d d __ n e w __ p o r t __ p a u s e __ c m d
    #
    # ------------------------------------------------------------------
    def add_port_pause_cmd(self, sdk_port_label, tx_pause, rx_pause) :
        tx_pause_label = 'off'
        if tx_pause:
            tx_pause_label = 'on'
        rx_pause_label = 'off'
        if rx_pause:
            rx_pause_label = 'on'
        if self.chip.dnx:
            port_cmd = "port %s TxPAU=%s RxPAU=%s\n" % (sdk_port_label, tx_pause_label, rx_pause_label)
        else:
            port_cmd = "port %s TPAU=%s RPAU=%s\n" % (sdk_port_label, tx_pause_label, rx_pause_label)
        self.port_cmd_list.append(port_cmd)

    # ------------------------------------------------------------------
    #
    #         a d d __ n e w __ r e g i s t e r __ t e m p l a t e
    #
    # ------------------------------------------------------------------
    def add_new_register_template(self, name, section, operation, loop_list, value_list, value_pairs) :

        register = RegisterTemplate(name, "write", loop_list, value_list, value_pairs)
        self.reg_dict[name] = register
        self.reg_list.append(register)
        self.section_dict[section].append(register)

    def generate_output_objects (self) :
        pass

    # ------------------------------------------------------------------
    #
    #              p r i n t __ o u t p u t __ o b j e c t s
    #
    # ------------------------------------------------------------------
    def print_output_objects (self, file_dict) :
        if self.config_manager.traffic.disable_custom_datapath_config == 1 :
            return
        file = file_dict['register']
        file.write("\n\n# --- %s ---\n" % self.header_str)
        for section in self.section_dict :
            if len(self.section_dict[section]) > 0 :
                file.write("\n# ----- %s ------\n" % section)
                for register in self.section_dict[section] :
                    register.print_register(file)

        file.write('\n# ------ port commands ------- \n')
        for command in self.port_cmd_list :
            file.write(command)

# ==============================================================================
#
#     E S W __ P O R T S __ R E G I S T E R __ M A N A G E R
#
# ==============================================================================
class ESW_PortsRegisterManager(RegisterManager):

    def __init__(self, chip, config_manager) :
        super(ESW_PortsRegisterManager,self).__init__(chip, config_manager)
        self.section_dict = {"ports" : []}
        self.header_str = "Port Registers"

    # ------------------------------------------------------------------
    #
    #                  g e n e r a t e __ r e g i s t e r s
    #
    # ------------------------------------------------------------------
    def generate_output_objects (self) :

        section = 'ports'

        if (isinstance(self.chip, cumulus.platform.TomahawkThreeChip)):
            port_table_size = 128
        else:
            port_table_size = 64 # XXX fixme: change to self.chip.port_table_size
        value_list = ['0','%d' % port_table_size]
        value_pairs = [ValuePair("port_pri",           "0"),
                       ValuePair("pri_mapping",        "0"),
                       ValuePair("trust_incoming_vid", "0"),
                       ValuePair("vt_enable",          "0") ]
        name = "port"
        if not (isinstance(self.chip, cumulus.platform.TridentThreeX5Chip) or \
                isinstance(self.chip, cumulus.platform.TridentThreeX5_56771_Chip) or \
                isinstance(self.chip, cumulus.platform.TridentThreeX7Chip) or \
                isinstance(self.chip, cumulus.platform.TridentThreeX7_56870_Chip) or \
                isinstance(self.chip, cumulus.platform.TridentThreeX7_56873_Chip) or \
                isinstance(self.chip, cumulus.platform.TomahawkThreeChip)):
            self.add_new_register(name, section, "modify", value_list, value_pairs)

        # adjust for ports with pause enabled
        for sdk_port_label in self.config_manager.port_desc.sdk_port_label_list :
            if sdk_port_label == 'cpu0' :
                continue
            tx_enable = False
            rx_enable = False
            fc_desc = self.config_manager.port_desc.label_2_flow_control[sdk_port_label]
            if fc_desc.type == 'link pause':
                tx_enable = fc_desc.tx
                rx_enable = fc_desc.rx
            self.add_port_pause_cmd(sdk_port_label, tx_enable, rx_enable)

# ==============================================================================
#
#                               C O N F I G
#
# ==============================================================================
class Config(object):

    # ------------------------------------------------------------------
    #
    #                          __ i n i t __
    #
    # ------------------------------------------------------------------
    def __init__ (self, name, config_dict) :
        self.name = name
        self.config_dict = config_dict
        self.configured_dict = {}
        self.file_name   = {}
        self.line_num    = {}

    # ------------------------------------------------------------------
    #
    #                     i n i t __ c o n f i g
    #
    # ------------------------------------------------------------------
    def init_config (self, config_mgr) :
        self.config_mgr = config_mgr

    # ------------------------------------------------------------------
    #
    #         c h e c k __ d u p l i c a t e __ p a r a m e t e r
    #
    # ------------------------------------------------------------------
    def check_duplicate_parameter(self, name, line, file_name, line_num):
        joined_name = '.'
        joined_name = joined_name.join(name)
        (prev_file, prev_line) = self.configured_dict.get(joined_name, (None, None))
        if prev_file and (prev_file != file_name or prev_line != line_num):
            self.config_mgr.report_error("%s is a duplicate parameter: first entry at %s:%d" % (joined_name, prev_file, prev_line),
                                         line,
                                         file_name,
                                         line_num)
            return True
        else:
            self.configured_dict[joined_name] = (file_name, line_num)
            return False

    # ------------------------------------------------------------------
    #
    #                   p a r s e __ i n t __ l i s t
    #
    # ------------------------------------------------------------------
    def parse_int_list(self, value, line, file_name, line_num):
        int_list_re = re.compile('\[' +'(?P<list>((\d+),\s*)*(\d+)?)' + '\]')
        m = int_list_re.search(value)
        if m :
            string_value_list = m.group('list').split(',')
            int_value_list = []
            for string_value in string_value_list :
                if string_value != '' :
                    int_value_list.append(int(string_value))
            return (0, int_value_list)
        else :
            self.config_mgr.report_error('value %s should be an integer list' % value, line, file_name, line_num)
            return(-1, None)

    # ------------------------------------------------------------------
    #
    #                        s e t __ c o n f i g
    #
    # ------------------------------------------------------------------
    def set_config (self, name, value, line, file_name, line_num) :
        if type(name) == list :
            if len(name) == 0:
                self.config_mgr.report_error('no name', line, file_name, line_num)
                return -1
            name = name[0]
        elif type(name) != str :
            self.config_mgr.report_error('name format %s not recognized' % name, line, file_name, line_num)
        if hasattr(self, name):
            self.config_mgr.report_error('parameter %s already configured' % name, line, file_name, line_num)
            return -1
        if name in self.config_dict :
            value_type = self.config_dict[name]
            if value_type == 'int' :
                typed_value = int(value)
            elif value_type == 'float' :
                typed_value = float(value)
            elif value_type == 'bool' :
                flag = value.lower()
                if flag == 'true':
                    typed_value = True
                else :
                    typed_value = False
            elif value_type == 'int list' :
                int_list_re = re.compile('\[' +'(?P<list>((\d+),\s*)*(\d+)?)' + '\]')
                m = int_list_re.search(value)
                if m :
                    typed_value = m.group('list').split(',')
                    temp_value_list = []
                    for idx in range(len(typed_value)) :
                        if typed_value[idx] != '' :
                            temp_value_list.append(int(typed_value[idx]))
                    typed_value = temp_value_list
                else :
                    self.config_mgr.report_error('value %s should be an integer list' % value, line, file_name, line_num)
                    return -1
            elif value_type == 'string' :
                typed_value = value
            elif value_type == 'string list' :
                string_list_re = re.compile('\[' +'(?P<list>((\w+).\s*)*(\w+)?)' + '\]')
                m = string_list_re.search(value)
                if m :
                    typed_value = m.group('list').split(',')
                    temp_value_list = []
                    for element in typed_value:
                        if element != '' :
                            element = element.strip()
                            temp_value_list.append(element)
                    typed_value = temp_value_list

            else :
                self.config_mgr.report_error('%s: value type %s not supported' % (self.name, value_type))
                return
            setattr(self, name, typed_value)
            self.file_name[name] = file_name
            self.line_num[name]  = line_num
        else :
            self.config_mgr.report_error('%s not supported in %s' % (name, self.name))

        return 0

    # ------------------------------------------------------------------
    #
    #                        c h e c k __ c o n f i g
    #
    # ------------------------------------------------------------------
    def check_config (self) :
        for name in self.config_dict :
            if not hasattr(self, name) :
                self.config_mgr.report_error("%s: Expected attribute '%s' not configured" % (self.name, name))

    # ------------------------------------------------------------------
    #
    #                      p r o c e s s __ c o n f i g
    #
    # ------------------------------------------------------------------
    def process_config (self) :
        # create and initialize missing configuration parameters
        for name in self.config_dict :
            if not hasattr(self, name) :
                value_type = self.config_dict[name]
                if value_type == 'int' :
                    typed_value = 0
                if value_type == 'bool' :
                    typed_value = False
                elif value_type == 'float' :
                    typed_value = float(0)
                elif value_type == 'int list' :
                    typed_value = []
                elif value_type == 'string' :
                    typed_value = ''
                elif value_type == 'string list' :
                    typed_value = []
                else :
                    self.config_mgr.report_error('%s: value type %s not recognized' % (self.name, value_type))
                setattr(self, name, typed_value)

# ==============================================================================
#
#                         C O N F I G __ S E T
#
# ==============================================================================
class ConfigSet(Config):
    def __init__ (self, name, config_dict, set_dict) :
        self.set_dict = set_dict
        super(ConfigSet,self).__init__(name, config_dict)

    def init_config (self, config_mgr) :
        super(ConfigSet,self).init_config(config_mgr)
        for member_name, member in self.set_dict.iteritems() :
            member.init_config(config_mgr)

    def set_config (self, name, value, line, file_name, line_num) :
        if type(name) == list :
            num_levels = len(name)
            if num_levels == 0:
                self.config_mgr.report_error('%s: parameter name is empty' % self.name)
                return -1
            parameter_name = name[0]
            if parameter_name in self.config_dict :
                rv = super(ConfigSet, self).set_config(parameter_name, value, line, file_name, line_num)
                return rv
            elif parameter_name in self.set_dict :
                set_info = self.set_dict[parameter_name]
                set_info.set_config(name[1:], value, line, file_name, line_num)
            else :
                self.config_mgr.report_error("parameter name '%s' not supported" % parameter_name, line, file_name, line_num)
                return -1
        else :
            self.config_mgr.report_error('name format %s not recognized' % name, line, file_name, line_num)
            return -1

    def check_config (self) :
        super(ConfigSet,self).check_config()
        for member_name, member in self.set_dict.iteritems() :
            member.check_config()

    def process_config (self) :
        super(ConfigSet,self).process_config()
        for member_name, member in self.set_dict.iteritems() :
            member.process_config()


# ==============================================================================
#
#                       S C H E D U L I N G __ C O N F I G
#
# ==============================================================================
class SchedulingConfig(Config):
    def __init__ (self) :
        config_dict = { 'algorithm' : 'string' }
        self.good_value_dict = {}
        self.good_value_dict['dwrr'] = 1
        super(SchedulingConfig,self).__init__('Scheduling', config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1

        if name[0] == 'algorithm' and not self.good_value_dict.get(value, None):
            self.config_mgr.report_error("scheduling algorithm '%s' not in supported list %s" % (value, self.good_value_dict.keys()),
                                         line,
                                         file_name,
                                         line_num)
        else:
            super(SchedulingConfig,self).set_config(name, value, line, file_name, line_num)

# ==============================================================================
#
#                       F W D __ T A B L E __ C O N F I G
#
# ==============================================================================
class FwdTableConfig(Config):
    def __init__ (self) :
        config_dict = { 'profile' : 'string' }
        self.good_value_dict = {}
        """
        Common
        """
        self.good_value_dict['default']      = 1
        self.good_value_dict['l2-heavy']     = 1
        self.good_value_dict['v4-lpm-heavy'] = 1
        self.good_value_dict['v6-lpm-heavy'] = 1
        self.good_value_dict['ipmc-heavy'] = 1

        """
        Mellanox only platforms
        """
        self.good_value_dict['l2-heavy-1']   = 1
        self.good_value_dict['l2-heavy-2']   = 1
        self.good_value_dict['v4-lpm-heavy-1']   = 1
        self.good_value_dict['rash-v4-lpm-heavy'] = 1
        self.good_value_dict['rash-custom-profile1'] = 1
        self.good_value_dict['rash-custom-profile2'] = 1
        self.good_value_dict['lpm-balanced'] = 1

        """
        Broadcom[XGS] only platforms
        """
        self.good_value_dict['mode-0'] = 1
        self.good_value_dict['mode-1'] = 1
        self.good_value_dict['mode-2'] = 1
        self.good_value_dict['mode-3'] = 1
        self.good_value_dict['mode-4'] = 1
        self.good_value_dict['mode-5'] = 1
        self.good_value_dict['mode-6'] = 1
        self.good_value_dict['mode-7'] = 1
        self.good_value_dict['mode-8'] = 1
        self.good_value_dict['lpm-test'] = 1
        super(FwdTableConfig,self).__init__('forwarding_table', config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1

        if name[0] == 'profile' and not self.good_value_dict.get(value, None):
            self.config_mgr.report_error("forwarding table profile '%s' not in supported list %s" % (value, self.good_value_dict.keys()),
                                         line,
                                         file_name,
                                         line_num)
        else:
            super(FwdTableConfig,self).set_config(name, value, line, file_name, line_num)

# ==============================================================================
#
#                       R I O T __ T A B L E __ C O N F I G
#
# ==============================================================================
class RiotConfig(Config):
    def __init__ (self) :
        config_dict = { 'profile' : 'string' }
        super(RiotConfig,self).__init__('vxlan_routing_overlay', config_dict)

    def check_config(self) :
        if not hasattr(self, 'profile'):
            self.profile = 'disable'

# ==============================================================================
#
#                         E N A B L E __ C O N F I G
#
# ==============================================================================
class EnableConfig(Config):
    def __init__ (self, name) :
        config_dict = {}
        super(EnableConfig,self).__init__(name, config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1

        flag = value.lower()
        if flag != 'true' and flag != 'false':
            self.config_mgr.report_error("'%s' should be 'true' or 'false'" % value, line, file_name, line_num)

        if (isinstance(self.config_mgr.chip, cumulus.platform.SpectrumChip)):
            if self.name == 'cut_through_enable' and flag == 'false':
                self.config_mgr.report_error('cut-through cannot be disabled for SpectrumChip',
                                             line, file_name, line_num)

    def check_config(self) :
        pass

    def process_config(self) :
        pass

# ==============================================================================
#
#                E C M P __ M A X __ P A T H S __ C O N F I G
#
# ==============================================================================
class EcmpMaxPathsConfig(Config):
    def __init__ (self) :
        config_dict = {}
        super(EcmpMaxPathsConfig,self).__init__('EcmpMaxPaths', config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        max_paths = int(value)
        filepath = '/cumulus/switchd/config/route/max_ecmp_path_limit'
        if os.path.isfile(filepath):
            cmd = "cat %s" % filepath
            f = os.popen(cmd)
            output    = f.read()
            max_value = int(output)
            f.close()
        else:
            self.config_mgr.report_info("'%s' does not exist, could not determine architecture limit on ECMP paths: using default value 200" % filepath)
            max_value = 200
        if max_paths < 0 or max_paths > max_value:
            self.config_mgr.report_error("'%s' out of range 0..%d'" % (value, max_value),
                                         line,
                                         file_name,
                                         line_num)
    def check_config(self) :
        pass

    def process_config(self) :
        pass

# ==============================================================================
#
#                E C M P __ H A S H __ S E E D __ C O N F I G
#
# ==============================================================================
class EcmpHashSeedConfig(Config):
    def __init__ (self) :
        config_dict = {}
        super(EcmpHashSeedConfig,self).__init__('EcmpHashSeed', config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        hash_seed = int(value)
        max_value = 0xffffffff
        if hash_seed < 0 or hash_seed > max_value:
            self.config_mgr.report_error("'%d' out of range 0..%d'" % (hash_seed, max_value),
                                         line,
                                         file_name,
                                         line_num)
    def check_config(self) :
        pass

    def process_config(self) :
        pass

# ==============================================================================
#
#     R E S I L I E N T __ H A S H __ E N T R I E S __ E C M P __ C O N F I G
#
# ==============================================================================
class ResilientHashEntriesEcmpConfig(Config):
    def __init__ (self) :
        config_dict = {}
        self.good_value_dict = {}
        self.good_value_dict[64]   = 1
        self.good_value_dict[128]  = 1
        self.good_value_dict[256]  = 1
        self.good_value_dict[512]  = 1
        self.good_value_dict[1024] = 1
        super(ResilientHashEntriesEcmpConfig,self).__init__('resilient_hash_entries_ecmp', config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        ecmp_entries = int(value)
        if not self.good_value_dict.get(ecmp_entries, None):
            self.config_mgr.report_error("size '%s' not in supported list %s" % (value, self.good_value_dict.keys()),
                                         line,
                                         file_name,
                                         line_num)
    def check_config(self) :
        pass

    def process_config(self) :
        pass

# ==============================================================================
#
#                     S F L O W __ C O N F I G
#
# ==============================================================================
class SflowConfig(Config):
    def __init__ (self, name) :
        config_dict = {}
        super(SflowConfig,self).__init__(name, config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        sflow_max_value = 16384
        sflow_value = int(value)
        if sflow_value < 0 or sflow_value > sflow_max_value:
            self.config_mgr.report_error("%s value '%s' out of range 0..%d" % (name[0], sflow_value, sflow_max_value),
                                         line,
                                         file_name,
                                         line_num)
    def check_config(self) :
        pass

    def process_config(self) :
        pass

# ==============================================================================
#
#                  C U S T O M __ H A S H __ C O N F I G
#
# ==============================================================================
class CustomHashConfig(Config):
    def __init__ (self, name) :
        config_dict = {}
        super(CustomHashConfig,self).__init__(name, config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1

        flag = value.lower()
        if flag != 'true' and flag != 'false':
            self.config_mgr.report_error("'%s' should be 'true' or 'false'" % value, line, file_name, line_num)

    def check_config(self) :
        pass

    def process_config(self) :
        pass


# ==============================================================================
#
#                             C O S __ Q U E U E __ C O N F I G
#
# ==============================================================================
class CosQueueConfig(Config):
    def __init__ (self, name) :
        config_dict = { 'uc' : 'int',
                        'cpu': 'int' }
        super(CosQueueConfig,self).__init__('CoS Queue Config %s' % name,
                                            config_dict)

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        int_value = int(value)
	# Max Unicast TC is 7
        max_value = 7
        if int_value < 0 or int_value > max_value:
            self.config_mgr.report_error("value(%d) for cos queue map to egress TC is incorrect - should be in the range 0..%d" % (int_value,max_value), line, file_name, line_num)
        super(CosQueueConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        super(CosQueueConfig,self).check_config()

    def set_mc_queue (self, mc) :
        self.mc = mc

    def match_mc_queue (self) :
        self.mc = self.uc

# ==============================================================================
#
#                   C O S __ Q U E U E __ C O N F I G __ S E T
#
# ==============================================================================
class CosQueueConfigSet(ConfigSet):
    def __init__ (self) :
        config_dict = {}
        self.cos_0 = CosQueueConfig('cos_0')
        self.cos_1 = CosQueueConfig('cos_1')
        self.cos_2 = CosQueueConfig('cos_2')
        self.cos_3 = CosQueueConfig('cos_3')
        self.cos_4 = CosQueueConfig('cos_4')
        self.cos_5 = CosQueueConfig('cos_5')
        self.cos_6 = CosQueueConfig('cos_6')
        self.cos_7 = CosQueueConfig('cos_7')
        cos_dict    = {'cos_0'  : self.cos_0,
                        'cos_1' : self.cos_1,
                        'cos_2' : self.cos_2,
                        'cos_3' : self.cos_3,
                        'cos_4' : self.cos_4,
                        'cos_5' : self.cos_5,
                        'cos_6' : self.cos_6,
                        'cos_7' : self.cos_7}
        self.cos_id_dict = {}
        super(CosQueueConfigSet,self).__init__('Cos Set', config_dict, cos_dict)

    def init_config (self, config_mgr) :
        for cos_id in range(len(self.set_dict)) :
            self.cos_id_dict[cos_id] = self.set_dict['cos_%d' % cos_id]
        super(CosQueueConfigSet,self).init_config(config_mgr)

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        super(CosQueueConfigSet,self).set_config(name, value, line, file_name, line_num)

    def check_config (self):
        super(CosQueueConfigSet,self).check_config()

    def match_mc_queues (self) :
        for cos_id, config in self.cos_id_dict.iteritems() :
            config.match_mc_queue()

    def set_mc_queues (self, cos_id, mc) :
        self.cos_id_dict[cos_id].set_mc_queue(mc)

# ==============================================================================
#
#                       S E R V I C E __ P O O L __ C O N F I G
#
# ==============================================================================
class ServicePoolConfig(Config):
    def __init__ (self, name, direction) :
        config_dict = { 'percent'        : 'float',
                        'mode'           : 'int',
                        'infinite_flag'  : 'bool'}
        self.configured = False
        super(ServicePoolConfig,self).__init__('%s Service Pool %s' % (direction, name), config_dict)

    def set_configured_flag (self) :
        self.configured = True

    def check_config (self) :
        for name in self.config_dict :
            if not hasattr(self, name) :
                setattr(self, name, 0)
        super(ServicePoolConfig,self).check_config()

    def set_config (self, name, value, line, file_name, line_num) :
        if name[0] == 'percent':
            float_value = float(value)
            max_value = 100.0
            if float_value < 0.0 or float_value > max_value:
                self.config_mgr.report_error("percent value '%f' out of range 0..%f" % (float_value, max_value),
                                            line, file_name, line_num)
        if name[0] == 'mode':
            int_value = int(value)
            max_value = 1
            if int_value < 0 or int_value > max_value:
                self.config_mgr.report_error("mode value '%d' or of range 0..%d" % (int_value, max_value),
                                            line, file_name, line_num)
        if name[0] == 'infinite_flag':
            if not re.search('^(true|false)$', value, re.I):
                self.config_mgr.report_error("infinite_flag value '%s' not bool" % (value),
                                            line, file_name, line_num)
        super(ServicePoolConfig,self).set_config(name, value, line, file_name, line_num)

    def process_config(self) :
        if self.configured == False:
            if hasattr(self, 'percent') :
                self.percent = 0
        super(ServicePoolConfig,self).process_config()

# ==============================================================================
#
#                  S E R V I C E __ P O O L  __ C O N F I G __ S E T
#
# ==============================================================================
class ServicePoolConfigSet(ConfigSet):
    def __init__ (self, prefix) :
        config_dict    = {}
        set_dict       = {}
        self.pool_dict = {}
        self.prefix = prefix
        super(ServicePoolConfigSet,self).__init__(prefix + ' Service Pool Set', config_dict, set_dict)

    def init_config (self, config_mgr) :
        for sp_id in range(config_mgr.hardware.num_service_pools) :
            label = '%d' % sp_id
            self.pool_dict[sp_id] = ServicePoolConfig(label, self.prefix)
            self.set_dict[label] = self.pool_dict[sp_id]
        super(ServicePoolConfigSet,self).init_config(config_mgr)

    def process_config (self) :
        for type, priority_group in self.config_mgr.priority_group.set_dict.iteritems() :
            if priority_group.configured == False:
                continue
            sp_id = priority_group.service_pool
            self.pool_dict[sp_id].set_configured_flag()
        super(ServicePoolConfigSet,self).process_config()

# ==============================================================================
#
#                    I N G R E S S __ B U F F E R __ C O N F I G
#
# ==============================================================================
class IngressBufferConfig(Config):
    def __init__ (self, name) :
        config_dict = { 'min_percent'    : 'float',
                        'shared_percent' : 'float',
                        'dynamic_quota'  : 'string'}
        self.alpha_str = ['alpha_0', 'alpha_1', 'alpha_2', 'alpha_4', 'alpha_8', 'alpha_16', 'alpha_32', 'alpha_64', 'alpha_infinity', 'alpha_1_2', 'alpha_1_4', 'alpha_1_8', 'alpha_1_16', 'alpha_1_32', 'alpha_1_64', 'alpha_1_128', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '255']
        super(IngressBufferConfig,self).__init__('%s Ingress Buffer' % name, config_dict)

    def set_config (self, name, value, line, file_name, line_num) :
        if name[0] == 'dynamic_quota':
            if value.upper() not in (name.upper() for name in self.alpha_str):
                self.config_mgr.report_error('alpha value configured for dynamic_quota incorrect : %s' % (value),
                                                line, file_name, line_num)
        if name[0] == 'shared_percent' or name[0] == 'min_percent':
            float_value = float(value)
            max_value = 100.0
            if float_value < 0.0 or float_value > max_value:
                self.config_mgr.report_error("percent value '%f' out of range 0..%f" % (float_value, max_value), 
						line, file_name, line_num)
        super(IngressBufferConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        for name in self.config_dict :
            if not hasattr(self, name) :
                setattr(self, name, 0)
        super(IngressBufferConfig,self).check_config()

# ==============================================================================
#
#                F L O W __ M G M T __ E G R E S S __ B U F F E R __ C O N F I G
#
# ==============================================================================
class FlowMgmtEgressBufferConfig(Config):
    def __init__ (self, name) :
        config_dict = { 'shared_percent'        : 'float',
                        'min_percent'       : 'float',
                        'dynamic_quota'     : 'string'}
        self.alpha_str = ['alpha_0', 'alpha_1', 'alpha_2', 'alpha_4', 'alpha_8', 'alpha_16', 'alpha_32', 'alpha_64', 'alpha_infinity', 'alpha_1_2', 'alpha_1_4', 'alpha_1_8', 'alpha_1_16', 'alpha_1_32', 'alpha_1_64', 'alpha_1_128', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '255']
        self.configured = False
        super(FlowMgmtEgressBufferConfig,self).__init__('%s Egress Queue Buffer' % name, config_dict)

    def set_config (self, name, value, line, file_name, line_num) :
        super(FlowMgmtEgressBufferConfig,self).set_config(name, value, line, file_name, line_num)
        if type(name) is list:
            name = name[0]
        if name in self.config_dict.keys():
            self.configured = True 
        if name == 'dynamic_quota':
            if value.upper() not in (name.upper() for name in self.alpha_str):
                self.config_mgr.report_error('alpha value configured for dynamic_quota incorrect : %s' % (value),
                                                line, file_name, line_num)
        if name == 'shared_percent' or name == 'min_percent':
            float_value = float(value)
            max_value = 100.0
            if float_value < 0.0 or float_value > max_value:
                self.config_mgr.report_error("percent value '%f' out of range 0..%f" % (float_value, max_value), 
                                                line, file_name, line_num)

    def check_config (self) :
        self.color_aware_flag = False
        self.unlimited        = True 
        if hasattr(self, 'min_percent') or hasattr(self, 'shared_percent') :
            self.unlimited = False

        for name in self.config_dict :
            if not hasattr(self, name) :
                setattr(self, name, None)
        super(FlowMgmtEgressBufferConfig,self).check_config()

# ==============================================================================
#
#                E G R E S S __ Q U E U E __ B U F F E R __ C O N F I G
#
# ==============================================================================
class EgressQueueBufferConfig(Config):
    def __init__ (self, name) :
        config_dict = { 'sp_percent'        : 'float',
                        'min_percent'       : 'float',
                        'sp_dynamic_quota'  : 'string',
                        'dynamic_quota'     : 'string'}
        self.alpha_str = ['alpha_0', 'alpha_1', 'alpha_2', 'alpha_4', 'alpha_8', 'alpha_16', 'alpha_32', 'alpha_64', 'alpha_infinity', 'alpha_1_2', 'alpha_1_4', 'alpha_1_8', 'alpha_1_16', 'alpha_1_32', 'alpha_1_64', 'alpha_1_128', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '255']
        self.configured = False
        super(EgressQueueBufferConfig,self).__init__('%s Egress Queue Buffer' % name, config_dict)

    def set_config (self, name, value, line, file_name, line_num) :
        super(EgressQueueBufferConfig,self).set_config(name, value, line, file_name, line_num)
        if type(name) is list:
            name = name[0]
        if name in self.config_dict.keys():
            self.configured = True
        if name == 'dynamic_quota':
            if value.upper() not in (name.upper() for name in self.alpha_str):
                self.config_mgr.report_error('alpha value configured for dynamic_quota incorrect : %s' % (value),
                                                line, file_name, line_num)
        if name == 'sp_percent' or name == 'min_percent':
            float_value = float(value)
            max_value = 100.0
            if float_value < 0.0 or float_value > max_value:
                self.config_mgr.report_error("percent value '%f' out of range 0..%f" % (float_value, max_value), 
						line, file_name, line_num)

    def check_config (self) :
        self.color_aware_flag = False
        self.unlimited        = True
        if hasattr(self, 'sp_dynamic_quota'):
            # There is a bug in older release wherein we are configuring sp_dynamic_quota for priority group
            # buffers from nclu "storage-optimized" command - give correct string to be used
            self.config_mgr.report_error("sp_dynamic_quota is not supported for {}. Use 'dynamic_quota' instead".format(self.name))

        if hasattr(self, 'min_percent') or hasattr(self, 'sp_percent') :
            self.unlimited = False

        for name in self.config_dict :
            if not hasattr(self, name) :
                setattr(self, name, None)
        super(EgressQueueBufferConfig,self).check_config()

# ==============================================================================
#
#                     E G R E S S __ B U F F E R __ C O N F I G
#
# ==============================================================================
class EgressBufferConfig(ConfigSet):
    def __init__ (self, name) :
        config_dict = {}
        queue_buffer_dict = { 'uc' :  EgressQueueBufferConfig('UC' + name),
                              'mc' :  EgressQueueBufferConfig('MC' + name),
                              'cpu' :  EgressQueueBufferConfig('CPU' + name)}
        super(EgressBufferConfig,self).__init__('%s Egress Buffer' % name, config_dict, queue_buffer_dict)

# ==============================================================================
#
#                P O R T __ B U F F E R __ C O N F I G 
#
# ==============================================================================
class PortBufferConfig(ConfigSet):
    def __init__ (self) :
        config_dict = {}
        self.ingress_buffer  = IngressBufferConfig('port')
        self.egress_buffer   = EgressBufferConfig('port')
        parameter_dict = { 'ingress_buffer' : self.ingress_buffer,
                            'egress_buffer'  : self.egress_buffer }
        super(PortBufferConfig,self).__init__('Port Buffer Config',
                                                config_dict,
                                                parameter_dict)

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        super(PortBufferConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        super(PortBufferConfig,self).check_config()

# ==============================================================================
#
#                F L O W C T R L __ B U F F E R __ C O N F I G
#
# ==============================================================================
class FlowCtrlBufferConfig(ConfigSet):
    def __init__ (self) :
        config_dict = {'service_pool'            : 'int'}
        self.ingress_buffer  = IngressBufferConfig('flow_control')
        self.egress_buffer   = FlowMgmtEgressBufferConfig('flow_control')
        parameter_dict = { 'ingress_buffer' : self.ingress_buffer,
                            'egress_buffer'  : self.egress_buffer }
        super(FlowCtrlBufferConfig,self).__init__('Flow Control Buffer Config',
                                                config_dict,
                                                parameter_dict)

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        super(FlowCtrlBufferConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        if not hasattr(self, 'service_pool'):
            self.service_pool = -1
        super(FlowCtrlBufferConfig,self).check_config()

# ==============================================================================
#
#                M G M T __ B U F F E R __ C O N F I G
#
# ==============================================================================
class MgmtBufferConfig(ConfigSet):
    def __init__ (self) :
        config_dict = {'service_pool'            : 'int'}
        self.ingress_buffer  = IngressBufferConfig('mgmt')
        self.egress_buffer   = FlowMgmtEgressBufferConfig('mgmt')
        parameter_dict = { 'ingress_buffer' : self.ingress_buffer,
                            'egress_buffer'  : self.egress_buffer }
        super(MgmtBufferConfig,self).__init__('Mgmt Buffer Config',
                                                config_dict,
                                                parameter_dict)

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        super(MgmtBufferConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        if not hasattr(self, 'service_pool'):
            self.service_pool = -1
        super(MgmtBufferConfig,self).check_config()


# ==============================================================================
#
#                E G R E S S __ Q U E U E __ B U F F E R __ C O N F I G __ S E T
#
# ==============================================================================
class EgressQueueBufferConfigSet(ConfigSet):
    def __init__ (self) :
        config_dict = {}
        self.egr_queue_0     = EgressBufferConfig('egr_queue_0')
        self.egr_queue_1     = EgressBufferConfig('egr_queue_1')
        self.egr_queue_2     = EgressBufferConfig('egr_queue_2')
        self.egr_queue_3     = EgressBufferConfig('egr_queue_3')
        self.egr_queue_4     = EgressBufferConfig('egr_queue_4')
        self.egr_queue_5     = EgressBufferConfig('egr_queue_5')
        self.egr_queue_6     = EgressBufferConfig('egr_queue_6')
        self.egr_queue_7     = EgressBufferConfig('egr_queue_7')
        group_dict  = { 'egr_queue_0'     : self.egr_queue_0,
                        'egr_queue_1'     : self.egr_queue_1,
                        'egr_queue_2'     : self.egr_queue_2,
                        'egr_queue_3'     : self.egr_queue_3,
                        'egr_queue_4'     : self.egr_queue_4,
                        'egr_queue_5'     : self.egr_queue_5,
                        'egr_queue_6'     : self.egr_queue_6,
                        'egr_queue_7'     : self.egr_queue_7 }
        super(EgressQueueBufferConfigSet,self).__init__('Egress Queue Buffer Config Set',
                                                        config_dict,
                                                        group_dict)

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1

        egr_queue_name = name[0]
        if egr_queue_name not in self.set_dict:
            self.config_mgr.report_error("Egress queue {}: not among supported egress queues. Should be one of following: {}".format(egr_queue_name,self.set_dict.keys()))
            self.set_dict[egr_queue_name] = EgressBufferConfig(egr_queue_name)
        super(EgressQueueBufferConfigSet,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        super(EgressQueueBufferConfigSet,self).check_config()

# ==============================================================================
#
#                  P R I O R I T Y __ G R O U P __ C O N F I G
#
# ==============================================================================

class PriorityGroupConfig(ConfigSet):
    def __init__ (self, name):
        config_dict    = { 'id'                      : 'int',
                           'service_pool'            : 'int',
                           'weight'                  : 'int',
                           'bw_percent'              : 'int',
                           'unlimited_egress_buffer' : 'bool',
                           'alias'                   : 'string',
                           'cos_list'                : 'int list' }
        self.config_label  = name
        self.configured    = False
        self.unlimited     = False
        self.ingress_buffer  = IngressBufferConfig(name)
        self.egress_buffer   = EgressBufferConfig(name)
        parameter_dict = { 'ingress_buffer' : self.ingress_buffer,
                           'egress_buffer'  : self.egress_buffer }

        super(PriorityGroupConfig,self).__init__('%s Priority Group' % name,
                                                config_dict,
                                                parameter_dict)

    def init_config (self, config_mgr) :
        super(PriorityGroupConfig,self).init_config(config_mgr)

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        self.configured = True
        if name[0] == 'weight':
            if isinstance(self.config_mgr.chip, cumulus.platform.SpectrumChip):
                max_value = 100
            else:
                max_value = 127
            int_value = int(value)
            if int_value < 0 or int_value > max_value:
                self.config_mgr.report_error("weight '%d' out of range 0..%d'" % (int_value, max_value),
                                             line,
                                             file_name,
                                             line_num)
        if name[0] == 'id':
            max_value = 7
            int_value = int(value)
            if int_value < 0 or int_value > max_value:
                self.config_mgr.report_error("id '%d' for priority_group %s out of range 0..%d" % (int_value, self.config_label, max_value),
                                             line,
                                             file_name,
                                             line_num)
                                            
        if name[0] == 'bw_percent':
            max_value = 100
            int_value = int(value)
            if int_value < 0 or int_value > max_value:
                self.config_mgr.report_error("bw_percent '%d' out of range 0..%d'" % (int_value, max_value),
                                             line,
                                             file_name,
                                             line_num)
        rv = super(PriorityGroupConfig,self).set_config(name, value, line, file_name, line_num)
        if name[0] == 'cos_list':
            if rv == 0:
                remove_list = []
                for cos_id in self.cos_list:
                    if cos_id >= 8:
                        remove_list.append(cos_id)
                        self.config_mgr.report_error("cos ID '%d' out of range 0..7'" % cos_id,
                                                     line,
                                                     file_name,
                                                     line_num)
                if len(remove_list) > 0:
                    for cos_id in remove_list:
                        self.cos_list.remove(cos_id)


    def check_config (self) :
        if not hasattr(self, 'unlimited_egress_buffer') :
            self.unlimited_egress_buffer = True

        if not hasattr(self, 'alias') :
            self.alias = self.config_label

        if isinstance(self.config_mgr.chip, cumulus.platform.SpectrumChip) and self.egress_buffer.set_dict['uc'].configured:
            # We were supporting this in older release - report only warning to support switchd reload when customer upgrades
            # since we have integrated CC check with switchd reload in latest release
            self.config_mgr.report_error("priority group '%s' does not support egress buffer for UC queue.\n\t Use egress_buffer.egr_queue_<egr_queue_num>.uc instead of priority_group.%s.egress_buffer.uc" % (self.config_label, self.config_label))

        if self.config_mgr.dryrun_flag:
            # we don't read in the datapath file: stub in these attributes
            if not hasattr(self, 'id') :
                self.id = 0
            if not hasattr(self, 'service_pool') :
                self.service_pool = 0
                
        if self.config_label in self.config_mgr.traffic.priority_group_list:
            logger.debug("Priority group name '%s' found in traffic priority group list" % (self.config_label))
            if self.configured == True :
                if not hasattr(self, 'cos_list') or len(self.cos_list) == 0:
                    if hasattr(self, 'weight') or hasattr(self, 'bw_percent') :
                        self.config_mgr.report_error("priority group '%s' without cos_list should not have any weight" % (self.config_label))
                    self.configured = False
            if self.configured == True :
                if hasattr(self, 'weight') or hasattr(self, 'bw_percent'):
                    if isinstance(self.config_mgr.chip, cumulus.platform.SpectrumChip):
                        self.config_mgr.report_warning("Configuring weight/bw_percent on priority group is deprecated for this platform. Use per switch_priority egress scheduler profile")
                if not hasattr(self, 'weight') :
                    if not hasattr(self, 'bw_percent') :
                        if not isinstance(self.config_mgr.chip, cumulus.platform.SpectrumChip):
                            self.config_mgr.report_error("priority group '%s' has neither weight nor bw_percent configured. Atleast one is expected" % (self.config_label)) 
                        self.bw_percent = -1
                        self.weight = -1
                    else:
                        # Either weight or bw_percent is supported: stub weight attribute
                        self.weight = -1
                else:
                    if hasattr(self, 'bw_percent') :
                        self.config_mgr.report_error("priority group '%s' has both weight and bw_percent configured. Only one is allowed" % (self.config_label))
                    else:
                        # Either weight or bw_percent is supported: stub bw_percent attribute
                        self.bw_percent = -1
            if self.configured == True :
                super(PriorityGroupConfig,self).check_config()
        else:
            if self.configured == True:
                self.config_mgr.report_error("priority group '%s' not found in traffic priority group list %s" % (self.config_label,
                                                                                                              self.config_mgr.traffic.priority_group_list))
            self.configured = False

    def process_config (self) :
        if self.configured == True:
            super(PriorityGroupConfig,self).process_config()
        else :
            return
        for q_type, buffer_config in self.egress_buffer.set_dict.iteritems() :
            if buffer_config.unlimited == True :
                # no configured buffer limits: check the flags
                if self.unlimited_egress_buffer == False :
                    self.config_mgr.report_error('%s: buffer limit conflicts with unlimited buffer flag' % self.name)
                    sys.exit(1)

        # XXX fixme!
        # awkward MC queue mapping management for Trident
        if self.config_mgr.hardware.num_mc_queues < 8 :
            if self.config_mgr.hardware.num_mc_queues < self.config_mgr.hardware.num_service_pools :
                mc_queue = 0
                self.config_mgr.report_error("%s: assigning all packets to MC queue %d (only %d queues available)" % \
                                             (mc_queue,
                                             self.config_mgr.hardware.num_mc_queues))
            else :
                mc_queue = self.service_pool
            for cos_id in self.cos_list :
                self.config_mgr.cos_queue.set_mc_queues(cos_id, mc_queue)

        self.queue = { 'uc'  : {},
                       'mc'  : {},
                       'cpu' : {} }
        for cos_id in self.cos_list :
            # cos to queue
            cos_queue_config = self.config_mgr.cos_queue.cos_id_dict[cos_id]

            # list each egress queue used by the  traffic group
            self.queue['uc'][cos_queue_config.uc]   = 1
            self.queue['mc'][cos_queue_config.mc]   = 1
            self.queue['cpu'][cos_queue_config.cpu] = 1

    def map_queue_to_sp (self, q2sp) :
            # map each egress queue to the service pool
            for cos_id in self.cos_list :
                cos_queue_config = self.config_mgr.cos_queue.cos_id_dict[cos_id]
                q2sp['uc'][cos_queue_config.uc]   = self.service_pool
                q2sp['mc'][cos_queue_config.mc]   = self.service_pool
                q2sp['cpu'][cos_queue_config.cpu] = self.service_pool

# ==============================================================================
#
#                  P R I O R I T Y __ G R O U P __ C O N F I G __ S E T
#
# ==============================================================================
class PriorityGroupConfigSet(ConfigSet):
    def __init__ (self) :
        config_dict = {}
        self.bulk     = PriorityGroupConfig('bulk')
        self.service  = PriorityGroupConfig('service')
        self.service1  = PriorityGroupConfig('service1')
        self.service2  = PriorityGroupConfig('service2')
        self.service3  = PriorityGroupConfig('service3')
        self.service4  = PriorityGroupConfig('service4')
        self.service5  = PriorityGroupConfig('service5')
        self.service6  = PriorityGroupConfig('service6')
        self.control  = PriorityGroupConfig('control')
        group_dict  = { 'bulk'     : self.bulk,
                        'service'  : self.service,
                        'service1' : self.service1,
                        'service2' : self.service2,
                        'service3' : self.service3,
                        'service4' : self.service4,
                        'service5' : self.service5,
                        'service6' : self.service6,
                        'control'  : self.control }
        super(PriorityGroupConfigSet,self).__init__('Priority Group Set',
                                                    config_dict,
                                                    group_dict)

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        
        priority_group_name = name[0]
        if priority_group_name not in self.set_dict:
            self.config_mgr.report_error("priority group {}: not among supported priority groups. Should be one of following: {}".format(priority_group_name,self.set_dict.keys()))
            self.set_dict[priority_group_name] = PriorityGroupConfig(priority_group_name)
            self.set_dict[priority_group_name].init_config(self.config_mgr)
        super(PriorityGroupConfigSet,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        super(PriorityGroupConfigSet,self).check_config()
        configured_count = 0
        bw_percent_configured = 0
        weight_configured = 0
        found_service_pg = 0
        found_serviceId_pg = 0
        unique_cos_list = []
        for pg_name, priority_group in self.set_dict.iteritems() :
            if priority_group.configured == True:
                if priority_group.bw_percent != -1:
                    bw_percent_configured = 1
                if priority_group.weight != -1:
                    weight_configured = 1
                for cos_id in priority_group.cos_list:
                    if cos_id in unique_cos_list:
                        self.config_mgr.report_error("priority group '%s': duplicate cos ID '%d' found in list '%s'" % (pg_name,
                                                                                                                    cos_id,
                                                                                                                    priority_group.cos_list))
                    else:
                        unique_cos_list.append(cos_id)
                if pg_name == 'service':
                    found_service_pg = 1
                if re.search('service[1-6]',pg_name):
                    found_serviceId_pg = 1
                configured_count += 1
        if len(unique_cos_list) != 8:
            self.config_mgr.report_error('%s: Not all cos are mapped to atleast one PG' % self.name)
        if bw_percent_configured and weight_configured:
            self.config_mgr.report_error('%s: mix of weight and bw_percent not supported' % self.name)
        if found_service_pg and found_serviceId_pg:
            self.config_mgr.report_error('%s: configuring service PG not support along with service<id> PG name' % self.name)
        if configured_count < 1 :
            self.config_mgr.report_error('%s: no configured priority groups' % self.name)
        if configured_count > 8 :
            self.config_mgr.report_error('%s: more than 8 priority groups configured' % self.name)
        if not isinstance(self.config_mgr.chip, cumulus.platform.SpectrumChip) and configured_count != 3:
            self.config_mgr.report_error('%s: Only 3 priority groups support for this platform' % self.name)

    def process_config (self) :

        super(PriorityGroupConfigSet,self).process_config()

        unmapped_cos    = { 0: 1,
                            1: 1,
                            2: 1,
                            3: 1,
                            4: 1,
                            5: 1,
                            6: 1,
                            7: 1 }
        self.cos2group = {}
        self.pg2sp     = {}
        self.q2sp      = { 'uc'   : {},
                           'mc'   : {},
                           'cpu'  : {} }

        for traffic_type in self.set_dict :
            priority_group = self.set_dict[traffic_type]
            if priority_group.configured == False :
                continue
            pg_id = priority_group.id
            sp_id = priority_group.service_pool

            # map each queue to a service pool
            priority_group.map_queue_to_sp(self.q2sp)

            #  map each CoS value to a priority group
            for cos_id in priority_group.cos_list :
                if unmapped_cos[cos_id] == 0 :
                    self.config_mgr.report_error('traffic group %s assigned cos ID %d, which is already assigned to a different group' % (priority_group.name, cos_id))
                unmapped_cos[cos_id] = 0
                self.cos2group[cos_id] = pg_id

            # map each priority group to a service pool
            self.pg2sp[pg_id] = sp_id

        # verify every CoS value has been assiged to a traffic group
        for cos_id in unmapped_cos:
            if unmapped_cos[cos_id] == 1 :
                self.config_mgr.report_error('CoS value %d is not mapped to a traffic group' % cos_id)

# ==============================================================================
#
#   R E S I L I E N T __ H A S H __ A C T I V E __ T I M E R __ C O N F I G
#
# ==============================================================================
class ResilientHashActiveTimer(Config):
    def __init__ (self):
        config_dict = {'value': 'int'}
        super(ResilientHashActiveTimer,self).__init__('Resilient Hash Active Timer Config', config_dict)
    
    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        self.value = value

    def check_config(self):
        if not hasattr(self, 'value'):
            self.value = 120000
        super(ResilientHashActiveTimer,self).check_config()

# ==============================================================================
#
#   R E S I L I E N T __ H A S H __ M A X __ U N B A L A N C E D __ T I M E R __ C O N F I G
#
# ==============================================================================
class ResilientHashMaxUnbalancedTimer(Config):
    def __init__ (self):
        config_dict = {'value': 'int'}
        super(ResilientHashMaxUnbalancedTimer,self).__init__('Resilient Hash Max Unbalanced Timer Config', config_dict)
    
    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        self.value = value

    def check_config(self):
        if not hasattr(self, 'value'):
            self.value = 100
        super(ResilientHashMaxUnbalancedTimer,self).check_config()

# ==============================================================================
#
#                             C O S __ P K T __ C O N F I G
#
# ==============================================================================
class CosPktConfig(Config):
    def __init__ (self, name) :
        config_dict = {}
        self.packet_priorities = {'802.1p' : [], 'dscp' : [], 'default' : []}
        super(CosPktConfig,self).__init__('CoS %s Packet Config' % name, config_dict)

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        if name[0] == 'priority_remark':
            return
        if name[0] == 'priority_source' or name[0] == "packet_priorities":
            print name
            if len(name) > 1:
                priority_type = name[1]
                if priority_type == '8021p':
                    priority_type = '802.1p'
            else:
                priority_type = 'default'
            packet_priority_list = self.packet_priorities.get(priority_type, None)
            if packet_priority_list != None:
                (rv, priority_value_list) = self.parse_int_list(value, line, file_name, line_num)
                """
                priority_value_list = value.strip('[')
                priority_value_list = priority_value_list.strip(']')
                priority_value_list = priority_value_list.split(',')
                """
                if rv == 0:
                    for priority_value in priority_value_list:
                        if priority_value != '':
                            self.packet_priorities[priority_type].append(int(priority_value))
                else:
                    self.config_mgr.report_error('packet priority type %s not supported' % name[1],
                                                 line,
                                                 file_name,
                                                 line_num)
                return
            else:
                print 'priority type %s has no packet priority list' % priority_type

        else :
            super(CosPktConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        super(CosPktConfig,self).check_config()

    def process_config (self) :
        super(CosPktConfig,self).process_config()


# ==============================================================================
#
#                   P O R T __ G R O U P __ C O N F I G
#
# ==============================================================================
class PortGroupConfig(Config):
    def __init__ (self, name, type_label, child_config_dict) :
        config_dict = {'port_set'           : 'string' }
        self.sdk_port_list = []
        self.linux_port_list = []
        self.type_label    = type_label
        config_dict.update(child_config_dict)
        super(PortGroupConfig,self).__init__(name,
                                             config_dict)

    def check_config (self) :
        super(PortGroupConfig,self).check_config()

    def check_egress_queue_list(self, egress_queue_list):
        if self.config_mgr.dryrun_flag and dryrun_traffic_file_only:
            return
        if len(egress_queue_list) == 0:
            return

    def check_cos_list(self, cos_list):
        if self.config_mgr.dryrun_flag and dryrun_traffic_file_only:
            return

        if len(cos_list) == 0:
            return

        # check that associated queue(s) have only these cos values
        cos_queue_dict = {}
        group_cos_dict = {}
        for cos_id in cos_list:
            group_cos_dict[cos_id] = 1
            uc_q = self.config_mgr.cos_queue.cos_id_dict[cos_id].uc
            cos_queue_dict[uc_q] = 1
            logger.debug('%s: port group cos id %d using queue %d' % (self.name, cos_id, uc_q))
        # XXX limit cos ID checks to values assigned to a priority group
        for cos_id, queue_config in self.config_mgr.cos_queue.cos_id_dict.iteritems():
            if group_cos_dict.get(cos_id, None):
                logger.debug('%s: cos id %d matches group cos id' % (self.name, cos_id))
                continue
            uc_q = queue_config.uc
            result = cos_queue_dict.get(uc_q, None)
            #logger.debug('%s: cos id %d using queue %d' % (self.name, cos_id, uc_q))
            if result:
                self.config_mgr.report_error("%s: cos ID value(s) %s share a queue with cos ID %d" % (self.name,
                                                                                                      cos_list,
                                                                                                      cos_id))

    def set_port_set(self, value, line, file_name, line_num):
        # parse the port set string
        port_group_list = value.split(',')
        if len(port_group_list) == 0:
            self.config_mgr.report_error('empty port member list', line, file_name, line_num)

        for port_group in port_group_list:
            port_group = port_group.strip()
            range_list = port_group.split('-')
            if len(range_list) > 0:
                start_linux_port = range_list[0]
                if start_linux_port not in self.config_mgr.port_desc.linux_2_label:
                    self.config_mgr.report_error('linux port %s not found in linux port map' % start_linux_port,
                                                 line,
                                                 file_name,
                                                 line_num)
                    return
                if len(range_list) > 1 :
                    end_linux_port = range_list[1]
                    if end_linux_port not in self.config_mgr.port_desc.linux_2_label:
                        self.config_mgr.report_error('linux port %s not found in linux port map' % end_linux_port,
                                                     line,
                                                     file_name,
                                                     line_num)
                        return
                else :
                    end_linux_port = range_list[0]
                
                in_range_flag = False
                for linux_port_label in self.config_mgr.port_desc.linux_intf_list :
                    if linux_port_label == start_linux_port :
                        in_range_flag = True
                    if in_range_flag == True :
                        sdk_port_label = self.config_mgr.port_desc.linux_2_label[linux_port_label]
                        self.sdk_port_list.append(sdk_port_label)
                        self.linux_port_list.append(linux_port_label)

                    if linux_port_label == end_linux_port :
                        if not in_range_flag:
                            self.config_mgr.report_error('invalid range %s defined in linux port map' % port_group,
                                                          line,
                                                          file_name,
                                                          line_num)
                        break

        # For PFC and LinkPause need not do this check
        if not re.search(r'(\bpfc\b|\blink pause\b)',self.type_label,re.I):
            # Verify each port in port-set has all member ports of a LAG(if any)
            for port_label in self.linux_port_list:
                bond_master = get_master(port_label)
                if bond_master:
                    #port is part of a bond
                    bond_mems = get_bond_members(bond_master)
                    if not all(port in self.linux_port_list for port in bond_mems):
                        self.config_mgr.report_error('port-set %s does not contain all members of bond %s' % (self.linux_port_list,bond_master),
                                                    line, file_name, line_num)
                        break

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        super(PortGroupConfig,self).set_config(name, value, line, file_name, line_num)
        if name[0] == 'port_set' :
            self.set_port_set(value, line, file_name, line_num)
        elif name[0] == 'cos_list':
            if len(self.cos_list) == 0:
                self.config_mgr.report_error("cos ID list is empty", line, file_name, line_num)
                return
        

# ==============================================================================
#
#               E C N __ P O R T __ G R O U P __ C O N F I G
#
# ==============================================================================
class EcnPortGroupConfig(PortGroupConfig):
    def __init__ (self, name, type_label) :
        config_dict = {'egress_queue_list'   : 'int list',
                       'ecn_enable'          : 'bool',
                       'red_enable'          : 'bool',
                       'min_threshold_bytes' : 'int',
                       'max_threshold_bytes' : 'int',
                       'probability'         : 'int' }
        super(EcnPortGroupConfig,self).__init__('ECN Port Group ' + name,
                                                type_label,
                                                config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        set_flag = True
        if name[0] == 'probability':
            probability_value = int(value)
            if probability_value < 0 or probability_value > 100:
                set_flag = False
                self.config_mgr.report_error("probability %d is out of range 0..100" % probability_value,
                                             line,
                                             file_name,
                                             line_num)
        elif name[0] == 'ecn_enable' or name[0] =='red_enable':
            flag = value.lower()
            if flag != 'true' and flag != 'false':
                set_flag = False
                self.config_mgr.report_error("'%s' should be 'true' or 'false'" % value,
                                             line,
                                             file_name,
                                             line_num)
        elif name[0] == 'cos_list':
            set_flag = False
            self.config_mgr.report_error("'%s' cos_list is deprecated from ECN, use egress_queue_list instead" % value, 
                                        line,file_name,line_num)

        if set_flag:
            super(EcnPortGroupConfig,self).set_config(name, value, line, file_name, line_num)
            if name[0] == 'egress_queue_list':
                remove_list = []
                for cos_id in self.egress_queue_list:
                    if cos_id >= 8:
                        remove_list.append(cos_id)
                        self.config_mgr.report_error("TC ID '%d' out of range 0..7'" % cos_id,
                                                     line,
                                                     file_name,
                                                     line_num)
                if len(remove_list) > 0:
                    for cos_id in remove_list:
                        self.egress_queue_list.remove(cos_id)

    def check_config (self) :
        if not hasattr(self, 'ecn_enable') :
            self.ecn_enable = True
            logger.debug('%s: ecn_enable defaulting to true' % self.name)
        if not hasattr(self, 'red_enable') :
            self.red_enable = False
            logger.debug('%s: red_enable defaulting to false' % self.name)
        if not hasattr(self, 'min_threshold_bytes') :
            logger.debug('%s: min_threshold_bytes defaulting to 40000' % self.name)
            self.min_threshold_bytes = 40000
        if not hasattr(self, 'max_threshold_bytes') :
            logger.debug('%s: max_threshold_bytes defaulting to 200000' % self.name)
            self.max_threshold_bytes = 200000
        if not hasattr(self, 'probability') :
            logger.debug('%s: probability value defaulting to 100' % self.name)
            self.probability = 100
        super(EcnPortGroupConfig,self).check_config()
        if not hasattr(self, 'egress_queue_list'):
            self.config_mgr.report_error("%s: TC ID list is missing" % (self.name))
            self.egress_queue_list = []
        else:
            self.check_egress_queue_list(self.egress_queue_list)

    def process_config(self) :
        pass
# ==============================================================================
#
#                   S H A P E R __ P O R T __ G R O U P __ C O N F I G
#
# ==============================================================================
class ShaperPortGroupConfig(PortGroupConfig):
    def __init__ (self, name, type_label) :

        config_dict = {'egr_queue_0.shaper'  : 'int list',
                      'egr_queue_1.shaper'  : 'int list',
                      'egr_queue_2.shaper'  : 'int list',
                      'egr_queue_3.shaper'  : 'int list',
                      'egr_queue_4.shaper'  : 'int list',
                      'egr_queue_5.shaper'  : 'int list',
                      'egr_queue_6.shaper'  : 'int list',
                      'egr_queue_7.shaper'  : 'int list',
                      'port.shaper' : 'int'}
        self.name = name
        self.type_label = type_label
        self.min_shaper_sum = 0
        self.port_max_shaper = 0
        super(ShaperPortGroupConfig,self).__init__(name,
                                                type_label,
                                                config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        if type(name) is list and name[-1] == 'shaper':
            name = '.'.join(name)
            if re.search('egr_queue_[0-7]\.shaper',name):
                (rv, shaper_list) = self.parse_int_list(value, line, file_name, line_num)
                if rv == 0:
                    if len(shaper_list) == 2 :
                        if shaper_list[0] > shaper_list[1] and shaper_list[1] != 0:
                            self.config_mgr.report_error("min_shaper '%d' is greater than max_shaper '%d' for %s in %s" % (shaper_list[0], shaper_list[1], name, self.name),
                                                         line,
                                                         file_name,
                                                         line_num)
                        else:
                            self.min_shaper_sum += shaper_list[0]
                    else:
                        self.config_mgr.report_error ("%s in %s is misconfigured length expected length:2 len :%d" % (name, self.name, len(shaper_list)),
                                                     line,
                                                     file_name,
                                                     line_num)
            elif re.search('port\.shaper',name):
                val = int(value)
                max_val = 400000000
                try:
                    if val < 0 or val > max_val:
                        self.config_mgr.report_error("shaping rate '%d' out of range 0..%d for %s in %s" % (val, max_val, name, self.name),
                                                     line,
                                                     file_name,
                                                     line_num)
                    else:
                        self.port_max_shaper = val
                except ValueError as ve:
                    self.config_mgr.report_error("Value configured in %s is invalid for %s" % (name,self.name))
        super(ShaperPortGroupConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config(self) :
        if (self.min_shaper_sum != 0 and self.port_max_shaper != 0 and
            self.min_shaper_sum > self.port_max_shaper):
            self.config_mgr.report_error("sum of min_shapers per eg_queue %d is greater than port.shaper %d "
                                        "in port-group %s"
                                        %(self.min_shaper_sum, self.port_max_shaper, self.name))
    def process_config(self) :
        pass
# ==============================================================================
#
#                   F C __ P O R T __ G R O U P __ C O N F I G
#
# ==============================================================================
class FcPortGroupConfig(PortGroupConfig):
    def __init__ (self, name, type_label, child_config_dict = None) :
        config_dict = {'rx_enable'          : 'bool',
                       'tx_enable'          : 'bool',
                       'xoff_size'          : 'int',
                       'xon_delta'          : 'int',
                       'cable_length'       : 'int',
                       'port_buffer_bytes'  : 'int' }
        if child_config_dict:
            config_dict.update(child_config_dict)
        super(FcPortGroupConfig,self).__init__(name,
                                               type_label,
                                               config_dict)
    def check_config (self) :
        if not hasattr(self, 'rx_enable') :
            logger.debug('%s: rx_enable defaulting to true' % self.name)
            self.rx_enable = True
        if not hasattr(self, 'tx_enable') :
            logger.debug('%s: tx_enable defaulting to true' % self.name)
            self.tx_enable = True
        if not hasattr(self, 'xoff_size') :
            logger.debug('%s: xoff_size defaulting to 10000' % self.name)
            self.xoff_size = 10000
        if not hasattr(self, 'xon_delta') :
            logger.debug('%s: xoff_delta defaulting to 2000' % self.name)
            self.xon_delta = 2000
        if not hasattr(self, 'cable_length') :
            logger.debug('%s: cable_length defaulting to 10' % self.name)
            self.cable_length = 10
        if not hasattr(self, 'port_buffer_bytes') :
            logger.debug('%s: port_buffer_bytes defaulting to 25000' % self.name)
            self.port_buffer_bytes = 25000
        # All the above parameters are optional now with dynamic PFC/Link Pause xoff/xon/HR calculations
        super(FcPortGroupConfig,self).check_config()

        if (self.port_buffer_bytes < self.xoff_size):
            self.config_mgr.report_error("port_buffer_bytes %d is less than the xoff_size %d" % (self.port_buffer_bytes,
                                                                                                 self.xoff_size),
                                         '',
                                         self.file_name['port_buffer_bytes'],
                                         self.line_num['port_buffer_bytes'])
            self.xoff_size = self.port_buffer_bytes
        pg_hdrm_cell_limit = int((self.port_buffer_bytes - self.xoff_size)  / self.config_mgr.hardware.cell_bytes)
        max_pg_hdrm_cell_limit = 200
        if pg_hdrm_cell_limit < 4:
            pg_hdrm_cell_limit = 4
        elif pg_hdrm_cell_limit > max_pg_hdrm_cell_limit:
            # this warning/info is suppressed(changed to debug) on customer request, as it may signal somiething is broken
            warning_msg = 'PG headroom allocation %d is greater than the maximum value %d:' % (pg_hdrm_cell_limit, max_pg_hdrm_cell_limit)
            warning_msg += ' reverting to max value %d' % max_pg_hdrm_cell_limit
            warning_msg += ' %s'  % self.file_name['xoff_size']
            warning_msg += ':%d'  % self.line_num['xoff_size']
            # logger.debug('%s' % warning_msg)
            info_msg = 'PG headroom allocation (%d) =' % pg_hdrm_cell_limit
            info_msg += ' (port_buffer_bytes (%d) - xoff_size (%d)) /' % (self.port_buffer_bytes, self.xoff_size)
            info_msg += ' hardware_cell_bytes (%d)' % self.config_mgr.hardware.cell_bytes
            # logger.debug('%s' % info_msg)
            self.pg_hdrm = max_pg_hdrm_cell_limit

        if not hasattr(self, 'cos_list'):
            self.cos_list = []

        for sdk_port_label in self.sdk_port_list:
            fc_desc      = self.config_mgr.port_desc.label_2_flow_control[sdk_port_label]
            fc_desc.set_type(self.type_label)
            fc_desc.rx = self.rx_enable
            fc_desc.tx = self.tx_enable
            fc_desc.cos_list           = self.cos_list
            fc_desc.min_cell_limit     = int(self.xoff_size  / self.config_mgr.hardware.cell_bytes)
            fc_desc.shared_cell_limit  = 4
            fc_desc.shared_reset_floor = 0
            fc_desc.pg_hdrm_cell_limit = pg_hdrm_cell_limit

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        set_flag = True
        if name[0] == 'xoff_size' or name[0] == 'port_buffer_bytes' or name[0] == 'xon_delta' or name[0] == 'cable_length':
            size = int(value)
            if size < 0:
                self.config_mgr.report_error("value %d is less than zero" % size,
                                             line,
                                             file_name,
                                             line_num)
        if name[0] == 'tx_enable' or name[0] =='rx_enable':
            flag = value.lower()
            if flag != 'true' and flag != 'false':
                set_flag = False
                self.config_mgr.report_error("'%s' should be 'true' or 'false'" % value,
                                             line,
                                             file_name,
                                             line_num)

        if set_flag:
            super(FcPortGroupConfig,self).set_config(name, value, line, file_name, line_num)
            if name[0] == 'cos_list':
                remove_list = []
                for cos_id in self.cos_list:
                    if cos_id >= 8:
                        remove_list.append(cos_id)
                        self.config_mgr.report_error("cos ID '%d' out of range 0..7'" % cos_id,
                                                     line,
                                                     file_name,
                                                     line_num)
                if len(remove_list) > 0:
                    for cos_id in remove_list:
                        self.cos_list.remove(cos_id)

# ==============================================================================
#
#         T C A M __ R E S O U R C E __ C O N F I G
#
# ==============================================================================
class TcamResourceConfig(Config):
    def __init__ (self):
        config_dict = {'profile'        : 'string',
                        'mode'          : 'string',
                        'replication'   : 'int',
                        'max_acl'          : 'int',
                        'max_ipmc'          : 'int' }
        super(TcamResourceConfig,self).__init__('tcam_resource',
                                                config_dict)
    
    def set_config(self, name, value, line, file_name, line_num) :
        super(TcamResourceConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        if not hasattr(self, 'profile') :
            self.profile = 'default'
        if not hasattr(self, 'mode') :
            self.mode = 'disabled'
        if not hasattr(self, 'replication') :
            self.replication = 0
        if not hasattr(self, 'max_acl') :
            self.max_acl = 0
        if not hasattr(self, 'max_ipmc') :
            self.max_ipmc = 0
        super(TcamResourceConfig,self).check_config()

# ==============================================================================
#
#         E G R E S S __ S C H E D __ P O R T __ G R O U P __ C O N F I G
#
# ==============================================================================
class EgressSchedGroupConfig(PortGroupConfig):
    def __init__ (self, name, type_label) :
        config_dict = {'egr_queue_0_bw_percent'  : 'int',
                      'egr_queue_1_bw_percent'  : 'int',
                      'egr_queue_2_bw_percent'  : 'int',
                      'egr_queue_3_bw_percent'  : 'int',
                      'egr_queue_4_bw_percent'  : 'int',
                      'egr_queue_5_bw_percent'  : 'int',
                      'egr_queue_6_bw_percent'  : 'int',
                      'egr_queue_7_bw_percent'  : 'int' }
        self.configured    = False
        self.name = name
        self.type_label = type_label
        super(EgressSchedGroupConfig,self).__init__(name,
                                                type_label,
                                                config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        self.configured    = True
        # For name as list with port_set - do not convert to string to get port validation done
        # Put length check to avoid validating input as "port_set.XYZ = swp1-2" with name = ["port_set", "XYZ"]
        if type(name) is list and len(name) >= 2:
            name = '_'.join(name)
            if re.search('egr_queue_[0-7]_bw_percent',name):
                max_value = 100
                try:
                    int_value = int(value)
                    if int_value < 0 or int_value > max_value:
                        self.config_mgr.report_error("bw_percent '%d' out of range 0..%d for %s in %s" % (int_value, max_value, name, self.name),
                                                        line, file_name, line_num)
                except ValueError as ve:
                    self.config_mgr.report_error("Value for %s is not integer for %s" % (name,self.name))

        super(EgressSchedGroupConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        if self.type_label == "default_profile":
            attr_count = 0
            for attr in self.config_dict.keys():
                if hasattr(self, attr):
                    attr_count += 1
            if attr_count == 0:
                # If default profile is not created, throw warning for spectrum ASICs
                if (isinstance(self.config_mgr.chip, cumulus.platform.SpectrumChip)):
                    self.config_mgr.report_warning("For current platform, configuring per priority egress scheduling is recommended")
        sum_bw_percent = 0
        for attr in self.config_dict.keys():
            if re.search('bw_percent',attr):
                if not hasattr(self,attr):
                    # We are no longer enforcing any profile to have bw_percent configured for all egr_queue_*
                    setattr(self,attr,-1)
                else:
                    value = int(getattr(self,attr))
                    if value > 0:
                        sum_bw_percent += value
        if sum_bw_percent > 100:
            self.config_mgr.report_error("Sum of bw_percent(%d) of all switch_prio for %s is greater than 100" % (sum_bw_percent,self.name))
        if not hasattr(self, 'cos_list'):
            self.cos_list = []
        if self.type_label == "default_profile":
            if hasattr(self, 'port_set'):
                self.config_mgr.report_error("port_set should not be configured for %s" % (self.name))
            else:
                # For default egress scheduler profiled we do not need port_set
                self.port_set = ['allports']
        super(EgressSchedGroupConfig,self).check_config()

# ==============================================================================
#
#                   P F C __ P O R T __ G R O U P __ C O N F I G
#
# ==============================================================================
class PfcPortGroupConfig(FcPortGroupConfig):
    def __init__ (self, name, type_label) :
        config_dict = {'cos_list'           : 'int list',
                       'rx_enable'          : 'bool',
                       'tx_enable'          : 'bool',
                       'xoff_size'          : 'int',
                       'cable_length'       : 'int',
                       'xon_delta'          : 'int',
                       'port_buffer_bytes'  : 'int' }
        self.name = 'PFC Port Group '
        super(PfcPortGroupConfig,self).__init__(name,
                                                type_label,
                                                config_dict)

    def check_config (self) :
        if not hasattr(self, 'cos_list'):
            self.config_mgr.report_error("%s: cos ID list is missing" % (self.name))
            self.cos_list = []
        else:
            self.check_cos_list(self.cos_list)
        super(PfcPortGroupConfig,self).check_config()

# ==============================================================================
#
#                 S O U R C E __ P O R T __ G R O U P__ C O N F I G
#
# ==============================================================================
class SourcePortGroupConfig(PortGroupConfig):

    def __init__ (self, name, type_label) :
        config_dict = { 'packet_priority_source_set'  : 'string list' }
        self.cos_label_dict = {'cos_0' : 1,
                               'cos_1' : 1,
                               'cos_2' : 1,
                               'cos_3' : 1,
                               'cos_4' : 1,
                               'cos_5' : 1,
                               'cos_6' : 1,
                               'cos_7' : 1 }
        self.priority_value_dict = {}
        self.configured_flag = False
        super(SourcePortGroupConfig,self).__init__(name,
                                                   type_label,
                                                   config_dict)

    def set_config (self, name, value, line, file_name, line_num) :
        if len(name) == 0:
            return -1
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        self.configured_flag = True

        pattern = r"cos_"
        if (re.search(pattern, name[0])):
            if not self.cos_label_dict.get(name[0], None):
                self.config_mgr.report_error("parameter '%s' is not supported" % name[0],
                                             file,
                                             file_name,
                                             line_num)
                priority_constructor = None
            else :
                priority_type = name[-1]
                if priority_type != 'dscp' and priority_type != '8021p':
                    self.config_mgr.report_error("priority type '%s' is not supported" % name[-1],
                                                 line,
                                                 file_name,
                                                 line_num)
                    priority_constructor = None
                elif name[1] == 'priority_source':
                    direction = 'source'
                    priority_constructor = SourcePriorityValueConfig
                else:
                    priority_constructor = None
                    self.config_mgr.report_error("parameter label '%s' is not supported" % name[1],
                                                 line,
                                                 file_name,
                                                 line_num)
            if priority_constructor:
                priority_config = self.priority_value_dict.get(priority_type, None)
                if priority_config == None:
                    priority_config = priority_constructor('%s ' % self.name, priority_type, self.config_mgr)
                    self.priority_value_dict[priority_type] = priority_config
                priority_config.set_config(name, value, line, file_name, line_num)

        else:
            if name[0] == 'port_set':
                super(SourcePortGroupConfig,self).set_config(name, value, line, file_name, line_num)
            elif name[0] == 'packet_priority_source_set':
                super(SourcePortGroupConfig,self).set_config(name, value, line, file_name, line_num)
                bad_type_list = []
                for source_type in self.packet_priority_source_set:
                    if source_type != 'dscp' and source_type != '802.1p':
                        bad_type_list.append(source_type)
                        self.config_mgr.report_error("source type '%s' is not supported" % source_type,
                                                     line,
                                                     file_name,
                                                     line_num)
                for source_type in bad_type_list:
                    self.packet_priority_source_set.remove(source_type)
            else:
                self.config_mgr.report_error("parameter '%s' is not supported" % name[0],
                                             line,
                                             file_name,
                                             line_num)

    def check_config (self) :
        if not self.configured_flag:
            return
        super(SourcePortGroupConfig,self).check_config()
        # check the priority configurations
        for priority_type in self.priority_value_dict.keys():
            source_type = priority_type
            if source_type == '8021p':
                source_type = '802.1p'
            if source_type not in self.packet_priority_source_set:
                self.config_mgr.report_error("packet priority '%s' mapping is configured, but not found in the packet priority source set %s" % (priority_type, self.packet_priority_source_set))
            priority_config = self.priority_value_dict[priority_type]
            if priority_config:
                priority_config.check_config()

    def process_config(self):
        pass


# ==============================================================================
#
#                R E M A R K __ P O R T __ G R O U P __ C O N F I G
#
# ==============================================================================
class RemarkPortGroupConfig(PortGroupConfig):

    def __init__ (self, name, type_label) :
        config_dict = { 'packet_priority_remark_set'  : 'string list' }
        self.cos_label_dict = {'cos_0' : 1,
                               'cos_1' : 1,
                               'cos_2' : 1,
                               'cos_3' : 1,
                               'cos_4' : 1,
                               'cos_5' : 1,
                               'cos_6' : 1,
                               'cos_7' : 1 }
        self.configured_flag = False
        self.priority_value_dict = {}
        super(RemarkPortGroupConfig,self).__init__(name,
                                                   type_label,
                                                   config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        self.configured_flag = True
        pattern = r"cos_"
        if (re.search(pattern, name[0])):
            if not self.cos_label_dict.get(name[0], None):
                self.config_mgr.report_error("parameter '%s' is not supported" % name[0],
                                             line,
                                             file_name,
                                             line_num)
                priority_constructor = None
            else :
                priority_type = name[-1]
                if priority_type != 'dscp' and priority_type != '8021p':
                    self.config_mgr.report_error("priority type '%s' is not supported" % name[-1],
                                                 line,
                                                 file_name,
                                                 line_num)
                    priority_constructor = None
                else:
                    if name[1] == 'priority_remark':
                        direction = 'remark'
                        priority_constructor = RemarkPriorityValueConfig
                    else:
                        priority_constructor = None
                        self.config_mgr.report_error("parameter label '%s' is not supported" % name[1],
                                                     line,
                                                     file_name,
                                                     line_num)
            if priority_constructor:
                priority_config = self.priority_value_dict.get(priority_type, None)
                if priority_config == None:
                    priority_config = priority_constructor('%s ' % self.name, priority_type, self.config_mgr)
                    self.priority_value_dict[priority_type] = priority_config
                priority_config.set_config(name, value, line, file_name, line_num)

        else:
            if name[0] == 'port_set':
                super(RemarkPortGroupConfig,self).set_config(name, value, line, file_name, line_num)
            elif name[0] == 'packet_priority_remark_set':
                super(RemarkPortGroupConfig,self).set_config(name, value, line, file_name, line_num)
                bad_type_list = []
                for remark_type in self.packet_priority_remark_set:
                    if remark_type != 'dscp' and remark_type != '802.1p':
                        bad_type_list.append(remark_type)
                        self.config_mgr.report_error("remark type '%s' is not supported" % remark_type,
                                                     line,
                                                     file_name,
                                                     line_num)
                for remark_type in bad_type_list:
                    self.packet_priority_remark_set.remove(remark_type)
            else:
                self.config_mgr.report_error("parameter '%s' is not supported" % name[0],
                                             line,
                                             file_name,
                                             line_num)


    def check_config(self) :
        if not self.configured_flag:
            return
        super(RemarkPortGroupConfig,self).check_config()
        # check the priority configurations
        for priority_type in self.priority_value_dict.keys():
            remark_type = priority_type
            if remark_type == '8021p':
                remark_type = '802.1p'
            if remark_type not in self.packet_priority_remark_set:
                self.config_mgr.report_error("packet priority '%s' mapping is configured, but not found in the packet priority remark set %s" % (priority_type, self.packet_priority_remark_set))
            priority_config = self.priority_value_dict[priority_type]
            if priority_config:
                priority_config.check_config()

    def process_config(self) :
        pass

# ==============================================================================
#
#                  P O R T __ G R O U P __  C O N F I G __ S E T
#
# ==============================================================================
class PortGroupConfigSet(ConfigSet):
    def __init__ (self, type_label, port_group_constructor) :
        config_dict     = {'port_group_list' : 'string list' }
        port_group_dict = {}
        self.type_label = type_label
        self.port_group_constructor = port_group_constructor
        self.is_configured = False
        super(PortGroupConfigSet,self).__init__('Port Group Set' + ' ' + type_label, config_dict, port_group_dict)

    def check_config (self) :
        if not self.is_configured:
            return
        for port_group in self.port_group_list:
            if port_group not in self.set_dict:
                self.config_mgr.report_error("port group name %s configured in port_group_list has no configs defined" % port_group)
        #if not hasattr(self, 'port_group_list') :
            #self.config_mgr.report_error("%s:  no port group list found" % (self.name + ' ' + self.fc_label))
        #else:
        super(PortGroupConfigSet,self).check_config()

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        self.is_configured = True
        if name[0] not in self.config_dict:
            port_group_name = name[0]
            if not hasattr(self, 'port_group_list') :
                self.config_mgr.report_error("port group name %s used, no port group list found" % port_group_name,
                                             line,
                                             file_name,
                                             line_num)
            elif port_group_name not in self.port_group_list:
                self.config_mgr.report_error("port group name '%s' not in port group list %s" % (port_group_name,
                                                                                                 self.port_group_list),
                                             line,
                                             file_name,
                                             line_num)

            if port_group_name not in self.set_dict:
                self.set_dict[port_group_name] = self.port_group_constructor(port_group_name, self.type_label)
                self.set_dict[port_group_name].init_config(self.config_mgr)
        super(PortGroupConfigSet,self).set_config(name, value, line, file_name, line_num)


# ==============================================================================
#
#                     P R I O R I T Y __ V A L U E __ C O N F I G
#
# ==============================================================================
class PriorityValueConfig(Config):

    def __init__(self, name, direction, priority_type, config_mgr) :
        config_dict = {}
        self.parameter_dict     = {}
        self.pkt_value_dict     = {}
        self.priority_direction = direction
        if priority_type == '8021p':
            self.max_value   = 7
        elif priority_type == 'dscp':
            self.max_value = 63
        else:
            self.max_value = 0
        self.priority_type = priority_type
        self.config_mgr    = config_mgr
        super(PriorityValueConfig, self).__init__(name + direction + ' ' + priority_type, config_dict)

    def set_config(self, name, value, line, file_name, line_num):
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1

        cos_id_list = name[0].split('cos_')
        cos_id = int(cos_id_list[1])

        # parse the value into a priority list
        (rv, priority_list) = self.parse_int_list(value, line, file_name, line_num)        
        """
        priority_list = value.strip('[')
        priority_list = priority_list.strip(']')
        priority_list = priority_list.split(',')
        """
        if rv != 0:
            return -1
        if self.priority_direction == 'remark':
            if len(priority_list) > 1:
                 self.config_mgr.report_error("cos ID '%d' mapped to multiple remark values '%s'" % (cos_id, priority_list),
                                              line,
                                              file_name,
                                              line_num)
        for priority_value_str in priority_list:
            if priority_value_str == '' :
                continue
            priority_value = int(priority_value_str)
            if (priority_value < 0) or (priority_value > self.max_value):
                self.config_mgr.report_error("priority value %d is out of range 0..%d" % (priority_value, self.max_value),
                                             line,
                                             file_name,
                                             line_num)
            if self.priority_direction == 'remark':
                value_key = cos_id
            else:
                value_key = priority_value
            (prev_file, prev_line) = self.pkt_value_dict.get(value_key, (None, None))
            if not prev_file:
                self.pkt_value_dict[value_key] = (file_name, line_num)

    def check_config(self):
        pass


# ==============================================================================
#
#            R E M A R K __ P R I O R I T Y __ V A L U E __ C O N F I G
#
# ==============================================================================
class RemarkPriorityValueConfig(PriorityValueConfig):

    def __init__(self, name, priority_type, config_mgr) :
        super(RemarkPriorityValueConfig, self).__init__(name, 'remark', priority_type, config_mgr)

    def check_config(self):
        # check cos ID mapping
        """
        it is ok have missing remark values, they will be assigned to a
        default packet field value
        """
        pass

# ==============================================================================
#
#           S O U R C E __ P R I O R I T Y __ V A L U E __ C O N F I G
#
# ==============================================================================
class SourcePriorityValueConfig(PriorityValueConfig):

    def __init__(self, name, priority_type, config_mgr) :
        super(SourcePriorityValueConfig, self).__init__(name, 'source', priority_type, config_mgr)

    def check_config(self):
        """
        it is ok have missing source values, they will be assigned to a
        default internal switchd priority
        """
        pass

# ==============================================================================
#
#                  T R A F F I C __ C O N F I G __ S E T
#
# ==============================================================================
class TrafficConfigSet(ConfigSet):
    def __init__ (self) :
        config_dict = {'yellow_limit_percent'     : 'float',
                       'red_limit_percent'        : 'float',
                       'yellow_packet_priorities' : 'int list',
                       'red_packet_priorities'    : 'int list',
                       'priority_group_list'      : 'string list',
                       'disable_custom_datapath_config' : 'bool'}
        self.cos_label_dict = {'cos_0' : 1,
                               'cos_1' : 1,
                               'cos_2' : 1,
                               'cos_3' : 1,
                               'cos_4' : 1,
                               'cos_5' : 1,
                               'cos_6' : 1,
                               'cos_7' : 1 }
        cos_dict = {}
        self.cos_id_dict = {}
        self.priority_value_dict = {}
        super(TrafficConfigSet,self).__init__('Traffic Set', config_dict, cos_dict)

    def init_config (self, config_mgr) :
        self.packet_priority_source_list = []
        self.packet_priority_remark_list = []
        for cos_id in range(config_mgr.hardware.num_priorities) :
            self.cos_id_dict[cos_id] = CosPktConfig('%s' % cos_id)
            self.set_dict['cos_%d' % cos_id] = self.cos_id_dict[cos_id]
        super(TrafficConfigSet,self).init_config(config_mgr)

    def set_config(self, name, value, line, file_name, line_num):
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        pattern = r"cos_"
        if (re.search(pattern, name[0])):
            if not self.cos_label_dict.get(name[0], None):
                self.config_mgr.report_error("parameter label '%s' is not supported" % name[0],
                                             line,
                                             file_name,
                                             line_num)
                priority_constructor = None
            else :
                priority_type = name[-1]
                if priority_type != 'dscp' and priority_type != '8021p':
                    self.config_mgr.report_error("priority_type '%s' is not supported" % name[-1],
                                                 line,
                                                 file_name,
                                                 line_num)
                    priority_constructor = None
                elif name[1] == 'priority_source':
                    direction = 'source'
                    priority_constructor = SourcePriorityValueConfig
                elif name[1] == 'priority_remark':
                    direction = 'remark'
                    priority_constructor = RemarkPriorityValueConfig
                else:
                    priority_constructor = None
                    self.config_mgr.report_error("parameter label '%s' is not supported" % name[1],
                                                 line,
                                                 file_name,
                                                 line_num)
            if priority_constructor:
                if self.priority_value_dict.get(direction, None) == None:
                    self.priority_value_dict[direction] = {}

                priority_config = self.priority_value_dict[direction].get(priority_type, None)
                if priority_config == None:
                    priority_config = priority_constructor('Traffic ', priority_type, self.config_mgr)
                    self.priority_value_dict[direction][priority_type] = priority_config
                priority_config.set_config(name, value, line, file_name, line_num)

        elif name[0] == 'packet_priority_remark_set' :
            # parse the packet priority remark set string
            priority_list = value.strip('[')
            priority_list = priority_list.strip(']')
            priority_list = priority_list.split(',')
            for priority in priority_list:
                priority = priority.strip()
                if priority == '':
                    continue
                if priority != 'dscp' and priority != '802.1p':
                    self.config_mgr.report_error("packet priority remark type '%s' is not supported" % priority,
                                                 line,
                                                 file_name,
                                                 line_num)
                else:
                    self.packet_priority_remark_list.append(priority)
        elif name[0] == 'packet_priority_source_set':
            # parse the packet priority source set string
            priority_list = value.strip('[')
            priority_list = priority_list.strip(']')
            priority_list = priority_list.split(',')
            for priority in priority_list:
                priority = priority.strip()
                if priority == '':
                    continue
                if priority != 'dscp' and priority != '802.1p':
                    self.config_mgr.report_error("packet priority source type '%s' is not supported." % priority,
                                                 line,
                                                 file_name,
                                                 line_num)
                else:
                    self.packet_priority_source_list.append(priority)
            if len(self.packet_priority_source_list) == 0:
                self.config_mgr.report_error('packet priority source list is empty', line, file_name, line_num)

        else:
            super(TrafficConfigSet,self).set_config(name, value, line, file_name, line_num)
            if name[0] == 'priority_group_list':
                if len(self.priority_group_list) == 0:
                    self.config_mgr.report_error('priority group list is empty',
                                                 line,
                                                 file_name,
                                                 line_num)

    def check_config (self) :
        if not hasattr(self, 'drop_behavior') :
            self.drop_behavior = 'color-blind'
        if not hasattr(self, 'yellow_packet_priorities') :
            self.yellow_packet_priorities = None
        if not hasattr(self, 'red_packet_priorities') :
            self.red_packet_priorities = None
        if not hasattr(self, 'yellow_limit_percent') :
            self.yellow_limit_percent = None
        if not hasattr(self, 'red_limit_percent') :
            self.red_limit_percent = None
        if not hasattr(self, 'disable_custom_datapath_config') :
            self.disable_custom_datapath_config = False
        super(TrafficConfigSet,self).check_config()
        if not hasattr(self, 'priority_group_list'):
            self.priority_group_list = []
        # check for a priority config object

        """
        default source packet fields and mapping, each selected packet field
        should have a block of mapped values. Any packet field value that
        is not specified in the configuration is assigned to a default
        internal switch priority

        This means:
        a. error out, if source values are specified but the source type is not
           configured
        b. it is ok to have missing source values, they will be assigned to a
           default internal switchd priority
        """
        direction = 'source'

        priority_label = '8021p'
        priority_type = '802.1p'
        if self.priority_value_dict.get(direction, None) and self.priority_value_dict[direction].get(priority_label, None):
            if not priority_type in self.packet_priority_source_list:
                self.config_mgr.report_error("%s: mapping '%s' configured but the source type is not listed. Add %s to traffic.packet_priority_source_set in traffic.conf" % (self.name,
                                                                                                                priority_label,priority_type))

        priority_label = 'dscp'
        priority_type = 'dscp'
        if self.priority_value_dict.get(direction, None) and self.priority_value_dict[direction].get(priority_label, None):
            if not priority_type in self.packet_priority_source_list:
                self.config_mgr.report_error("%s: mapping '%s' configured but the source type is not listed" % (self.name,
                                                                                                                priority_label))

        """
        default remark packet fields and mapping, each selected packet field
        should have a block of mapped values. Any internal switch priority value
        that is not specified in the configuration is assigned to a default
        packet field value.

        This means:
        a. error out, if remark values are specified but remark set is not
           configured
        b. it is ok to have missing remark values, they will be assigned to a
           default packet field value
        """
        direction = 'remark'

        priority_label = '8021p'
        priority_type = '802.1p'
        if self.priority_value_dict.get(direction, None) and self.priority_value_dict[direction].get(priority_label, None):
            if not priority_type in self.packet_priority_remark_list:
                self.config_mgr.report_error("%s: mapping '%s' configured but the remark type is not listed" % (self.name,
                                                                                                                priority_label))

        priority_label = 'dscp'
        priority_type = 'dscp'
        if self.priority_value_dict.get(direction, None) and self.priority_value_dict[direction].get(priority_label, None):
            if not priority_type in self.packet_priority_remark_list:
                self.config_mgr.report_error("%s: mapping '%s' configured but the remark type is not listed" % (self.name,
                                                                                                                priority_label))

        # check the priority configurations
        for direction in self.priority_value_dict.keys():
            for priority_type in self.priority_value_dict[direction].keys():
                priority_config = self.priority_value_dict[direction][priority_type]
                if priority_config:
                    priority_config.check_config()

    def process_config (self) :
        super(TrafficConfigSet,self).process_config()
        if self.red_limit_percent != None or self.yellow_limit_percent != None :
            self.color_aware = True
            if self.red_limit_percent == None or self.yellow_limit_percent == None :
                self.config_mgr.report_error('%s: color-aware drop limits are incomplete: red and yellow must both be configured.')
                sys.exit(1)
        else :
            self.color_aware = False
        self.pkt2cos   = {'802.1p' : {}, 'dscp' : {}}
        self.pkt2color = {'802.1p' : {}, 'dscp' : {}}
        drop_color_dict = { 'yellow' : self.yellow_packet_priorities,
                            'red'    : self.red_packet_priorities }
        for priority_type, priority_map in self.pkt2cos.items():
            if priority_type not in self.packet_priority_source_list:
                continue
            if priority_type == '802.1p' :
                num_priorities = 8
            elif priority_type == 'dscp' :
                num_priorities = 64

            # initialize with the default cos ID and color
            if len(self.config_mgr.priority_group.bulk.cos_list) < 1 :
                default_cos_id = None
            else :
                default_cos_id = self.config_mgr.priority_group.bulk.cos_list[0]
            for priority_idx in range(num_priorities):
                self.pkt2cos[priority_type][priority_idx] = default_cos_id
                self.pkt2color[priority_type][priority_idx] = 'green'

            # overwrite with the defined priority mappings
            for cos_id, cos_config in self.cos_id_dict.iteritems() :
                cos_config.process_config()
                priority_value_list = cos_config.packet_priorities[priority_type]
                if len(priority_value_list):
                    priority_value_list = cos_config.packet_priorities['default']
                for packet_priority in priority_value_list:
                    if packet_priority > num_priorities :
                        self.config_mgr.report_error('packet priority %d greater than maximum packet %s priority value %d' % (packet_priority,
                                                                                                                              priority_type,
                                                                                                                              num_priorities))
                        sys.exit(1)
                    else :
                        self.pkt2cos[priority_type][packet_priority] = cos_id

            for color, packet_priority_list in drop_color_dict.iteritems() :
                if packet_priority_list == None :
                    continue
                for packet_priority in packet_priority_list :
                    self.pkt2color[priority_type][packet_priority] = color


# ==============================================================================
#
#                       H A R D W A R E __ C O N F I G
#
# ==============================================================================
class HardwareConfig(Config):
    def __init__ (self, dryrun_flag) :
        config_dict = { 'total_buffer_cells'  : 'int',
                        'cell_bytes'          : 'int',
                        'max_frame_cells'     : 'int',
                        'num_priorities'      : 'int',
                        'num_priority_groups' : 'int',
                        'num_service_pools'   : 'int',
                        'num_mc_queues'       : 'int' }
        if dryrun_flag and dryrun_traffic_file_only:
            # default values are provided for the dryrun option since the hw_desc file is not used
            self.total_buffer_cells  = 100000
            self.cell_bytes          = 208
            self.max_frame_cells     = 45
            self.num_priorities      = 16
            self.num_priority_groups = 8
            self.num_service_pools   = 4
            self.num_mc_queues       = 8
        super(HardwareConfig,self).__init__('Hardware', config_dict)

# ==============================================================================
#
#                         I G N O R E __ C O N F I G
#
# ==============================================================================
class IgnoreConfig(object):

    def init_config(self, config_mgr) :
        pass

    def set_config(self, name, value, line, file_name, line_num) :
        pass

    def check_config(self) :
        pass

    def process_config(self) :
        pass

            
class Color:
    PURPLE = '\033[95m'
    CYAN = '\033[96m'
    DARKCYAN = '\033[36m'
    BLUE = '\033[94m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'
    END = '\033[0m'

# ==============================================================================
#
#                         C O N F I G __ M A N A G E R
#
# ==============================================================================
class ConfigManager(object):

    def __init__(self, platform, dryrun_flag, quiet_flag) :

        super(ConfigManager,self).__init__()

        self.dryrun_flag     = dryrun_flag
        self.quiet_flag      = quiet_flag
        self.platform        = platform
        self.chip            = platform.switch.chip
        self.hardware        = HardwareConfig(dryrun_flag)
        self.traffic         = TrafficConfigSet()
        self.priority_group  = PriorityGroupConfigSet()
        self.egress_buffer  = EgressQueueBufferConfigSet()
        self.port  = PortBufferConfig()
        self.management  = MgmtBufferConfig()
        self.flow_control  = FlowCtrlBufferConfig()
        self.tcam_resource  = TcamResourceConfig()
        self.resilient_hash_active_timer  = ResilientHashActiveTimer()
        self.resilient_hash_max_unbalanced_timer  = ResilientHashMaxUnbalancedTimer()
        self.default_egress_sched  = EgressSchedGroupConfig('default_port_group', 'default_profile')
        self.egress_sched  = PortGroupConfigSet('egress_sched_profile', EgressSchedGroupConfig)
        self.ingress_service_pool = ServicePoolConfigSet('Ingress')
        self.egress_service_pool  = ServicePoolConfigSet('Egress')
        self.cos_queue       = CosQueueConfigSet()
        self.source          = PortGroupConfigSet('source', SourcePortGroupConfig)
        self.remark          = PortGroupConfigSet('remark', RemarkPortGroupConfig)
        self.scheduling      = SchedulingConfig()
        self.link_pause      = PortGroupConfigSet('link pause', FcPortGroupConfig)
        self.pfc             = PortGroupConfigSet('pfc', PfcPortGroupConfig)
        self.ecn_red         = PortGroupConfigSet('ecn_red', EcnPortGroupConfig)
        self.shaper          = PortGroupConfigSet('shaping', ShaperPortGroupConfig)
        self.port_desc       = PortDesc(self)
        self.buffer_desc     = BufferDesc()
        self.ignore_config   = IgnoreConfig()
        self.forwarding_table = FwdTableConfig()
        self.dos_enable         = EnableConfig('dos_enable')
        self.cut_through_enable = EnableConfig('cut_through_enable')
        self.symmetric_hash_enable  = EnableConfig('symmetric_hash_enable')
        self.resilient_hash_enable  = EnableConfig('resilient_hash_enable')
        self.resilient_hash_entries_ecmp = ResilientHashEntriesEcmpConfig()
        self.ecmp_max_paths         = EcmpMaxPathsConfig()
        self.ecmp_hash_seed         = EcmpHashSeedConfig()
        self.sflow                  = SflowConfig('sflow')
        self.custom_ecmp_hash_config = CustomHashConfig('hash_config')
        self.custom_lag_hash_config = CustomHashConfig('lag_hash_config')
        self.vxlan_routing_overlay  = RiotConfig()
        self.forwarding_section = []
        self.error_comment   = ""
        self.num_cpu_queues  = 48
        self.exception_q_start  = 8
        self.exception_q_weight = 8
        self.input_file_error   = False

        self.manager_dict = { 'hardware'       : self.hardware,
                              'traffic'        : self.traffic,
                              'priority_group' : self.priority_group,
                              'egress_buffer' : self.egress_buffer,
                              'port' : self.port,
                              'management' : self.management,
                              'flow_control' : self.flow_control,
                              'tcam_resource' : self.tcam_resource,
                              'resilient_hash_active_timer' : self.resilient_hash_active_timer,
                              'resilient_hash_max_unbalanced_timer' : self.resilient_hash_max_unbalanced_timer,
                              'default_egress_sched' : self.default_egress_sched,
                              'egress_sched'   : self.egress_sched,
                              'ingress_service_pool' : self.ingress_service_pool,
                              'egress_service_pool'  : self.egress_service_pool,
                              'cos_egr_queue'  : self.cos_queue,
                              'scheduling'     : self.scheduling,
                              'link_pause'     : self.link_pause,
                              'pfc'            : self.pfc,
                              'ecn'            : self.ignore_config,
                              'ecn_red'        : self.ecn_red,
                              'shaping'        : self.shaper,
                              'source'         : self.source,
                              'remark'         : self.remark,
                              'forwarding_table' : self.forwarding_table,
                              'vxlan_routing_overlay' : self.vxlan_routing_overlay,
                              'dos'            : self.ignore_config,
                              'dos_enable'     : self.dos_enable,
                              'cut_through_enable' : self.cut_through_enable,
                              'symmetric_hash_enable' :self.symmetric_hash_enable, 
                              'ecmp_max_paths' : self.ecmp_max_paths,
                              'ecmp_hash_seed' : self.ecmp_hash_seed,
                              'resilient_hash_enable' : self.resilient_hash_enable,
                              'resilient_hash_entries_ecmp' : self.resilient_hash_entries_ecmp,
                              'sflow'          : self.sflow,
                              'hash_config' : self.custom_ecmp_hash_config,
                              'lag_hash_config': self.custom_lag_hash_config }

        # for when order matters
        self.manager_list = [ self.hardware,
                              self.traffic,
                              self.priority_group,
                              self.egress_buffer,
                              self.port,
                              self.management,
                              self.flow_control,
                              self.tcam_resource,
                              self.resilient_hash_active_timer,
                              self.resilient_hash_max_unbalanced_timer,
                              self.default_egress_sched,
                              self.egress_sched,
                              self.ingress_service_pool,
                              self.egress_service_pool,
                              self.source,
                              self.remark,
                              self.cos_queue,
                              self.link_pause,
                              self.pfc,
                              self.ecn_red,
                              self.shaper,
                              self.scheduling,
                              self.forwarding_table,
                              self.vxlan_routing_overlay,
                              self.dos_enable,
                              self.cut_through_enable,
                              self.symmetric_hash_enable,
                              self.ecmp_max_paths,
                              self.ecmp_hash_seed,
                              self.resilient_hash_enable,
                              self.resilient_hash_entries_ecmp,
                              self.sflow,
                              self.custom_ecmp_hash_config,
                              self.custom_lag_hash_config,
                              self.ignore_config]

    # ------------------------------------------------------------------
    #
    #                     i n i t __ c o n f i g
    #
    # ------------------------------------------------------------------
    def init_config (self) :
        for manager in self.manager_list:
            manager.init_config(self)
        self.buffer_desc.init_desc(self)

    # ------------------------------------------------------------------
    #
    #             r e a d __ c o n f i g __ f i l e
    #
    # ------------------------------------------------------------------
    def read_config_file (self, config_file) :
        line_buffer = ""
        f = open(config_file)
        line_num = 0
        for line in f:
            line_num += 1
            line = line.rstrip()
            #print '%d: %s' % (line_num, line)
            line_buffer = line_buffer + line
            line_buffer = line_buffer.rstrip("\\")
            if not re.match(r"(.*)\\", line) :
                line = line_buffer
                line_buffer = ""
            else :
                continue
            config = [x.strip() for x in line.partition('=') if len(x) > 0]
            if len(config) == 0 :
                continue
            if config[0].startswith("#") :
                continue
            if len(config) < 3 :
                self.report_error('malformed configuration line', line, config_file, line_num)
                continue
            name  = config[0]
            value = ' '.join(config[2:])
            # trim any comment
            value = value.split('#')
            value = value[0]
            # remove leading and trailing spaces
            value = value.strip()

            name_level = name.split('.')
            if not name_level :
                self.report_error('parameter %s not supported' % name, line, config_file, line_num)
                continue
            manager_name = name_level[0]
            if manager_name not in self.manager_dict :
                self.report_error('parameter %s not supported' % manager_name, line, config_file, line_num)
                continue
            manager = self.manager_dict[manager_name]
            line = line.lstrip()
            manager.set_config(name_level[1:], value, line, config_file, line_num)
        f.close()

    # ------------------------------------------------------------------
    #
    #        r e a d __ f o r w a r d i n g __ c o n f i g __ f i l e
    #
    # ------------------------------------------------------------------
    def read_forwarding_config_file (self, forwarding_file) :

        section_name = ""
        line_buffer = ""
        f = open(forwarding_file)
        for line in f:
            #print line
            config = line.split()
            if len(config) == 0 :
                continue
            if config[0] == "section:" :
                section_name = config[1]
                continue
            self.forwarding_section.append(line)

    def dump_port_map(self) :
        self.port_desc.dump_port_map()

    # ------------------------------------------------------------------
    #
    #                  r e p o r t __ e r r o r
    #
    # ------------------------------------------------------------------
    def report_error(self, error_msg, line=None, file_name=None, line_num=None) :
        error_str = error_msg
        if self.dryrun_flag and not self.quiet_flag:
            error_str = Color.RED + error_msg + Color.END
        if line:
            error_str = ('%s: ' % line) + error_str
        if file_name:
            error_str = error_str + ' (%s:%d)' % (file_name, line_num)
        if self.dryrun_flag and not self.quiet_flag:
            print >>sys.stderr, error_str
        else:
            logger.error(error_str)
        self.error_comment += '# %s\n' % error_str
        self.input_file_error = True;

    # ------------------------------------------------------------------
    #
    #                  r e p o r t __w a r n i n g
    #
    # ------------------------------------------------------------------
    def report_warning(self, warning_msg) :
        if self.dryrun_flag and not self.quiet_flag:
            warning_msg = Color.YELLOW + warning_msg + Color.END
            print warning_msg
        else:
            logger.warning(warning_msg)

    # ------------------------------------------------------------------
    #
    #                  r e p o r t __ i n f o
    #
    # ------------------------------------------------------------------
    def report_info(self, info_msg) :
        if self.dryrun_flag and not self.quiet_flag:
            print info_msg
        else:
            logger.info(info_msg)

    # ------------------------------------------------------------------
    #
    #                 g e t __ f c __ p g __ i d
    #
    # ------------------------------------------------------------------
    def get_fc_pg_id(self, type):

        if type == 'link pause':
            return 7
        if type == 'pfc':
            return 1 # XX fixme: find the first unused PG id

    # ------------------------------------------------------------------
    #
    #          c a l c u l a t e __ i n g r e s s __ b u f f e r s
    #
    # ------------------------------------------------------------------
    def __calculate_ingress_buffers (self) :

        self.buffer_comment += '# ------- ingress buffers ------- \n'

        pg_buffer_limit      = self.buffer_desc.pg_buffer_limit
        ing_sp_buffer_limit  = self.buffer_desc.ing_sp_buffer_limit
        ing_sp_buffer_offset = self.buffer_desc.ing_sp_buffer_offset
        color_aware_flag     = self.traffic.color_aware

        cpu_pg_min_cells    = 45
        wan_pg_min_cells    = 1

        total_mem_cells     = self.hardware.total_buffer_cells
        num_service_pools   = self.hardware.num_service_pools
        num_priority_groups = self.hardware.num_priority_groups

        self.buffer_desc.init_available_cell_count('ingress', total_mem_cells)
        self.buffer_comment += '# total mem cells: %d\n' % self.buffer_desc.get_remaining_cells('ingress')

        # calculate the service pool buffers
        for sp_id in range(num_service_pools) :
            buffer_limit = 0
            if sp_id in self.ingress_service_pool.pool_dict :
                service_pool_config = self.ingress_service_pool.pool_dict[sp_id]
                percent = service_pool_config.percent
                if color_aware_flag == True :
                    limit_name = 'green'
                else :
                    limit_name = ''
                buffer_limit = int((percent/100) * total_mem_cells)
                ing_sp_buffer_limit[sp_id]  = {}
                ing_sp_buffer_offset[sp_id] = {}
                error = self.buffer_desc.allocate_cells('ingress', 'service_pool', buffer_limit)
                if not error :
                    ing_sp_buffer_limit[sp_id]['green'] = buffer_limit
                else :
                    ing_sp_buffer_limit[sp_id]['green'] = 100
                self.buffer_comment += '# service pool %d %s limit %d\n' % (sp_id, limit_name, ing_sp_buffer_limit[sp_id]['green'])

                if color_aware_flag == False :
                    ing_sp_buffer_limit[sp_id]['yellow'] = None
                    ing_sp_buffer_limit[sp_id]['red'] = None
                else :
                    # set the color-aware limits
                    yellow_sp_cells = int(ing_sp_buffer_limit[sp_id]['green'] * float(self.traffic.yellow_limit_percent / 100))
                    ing_sp_buffer_limit[sp_id]['yellow'] = yellow_sp_cells
                    self.buffer_comment += '# service pool %d yellow limit %d\n' % (sp_id, ing_sp_buffer_limit[sp_id]['yellow'])
                    red_sp_cells = int(ing_sp_buffer_limit[sp_id]['green'] * float(self.traffic.red_limit_percent / 100))
                    ing_sp_buffer_limit[sp_id]['red'] = red_sp_cells
                    self.buffer_comment += '# service pool %d red limit %d\n' % (sp_id, ing_sp_buffer_limit[sp_id]['red'])

        # set the CPU port per-priority-group minimum buffer
        self.buffer_desc.cpu_pg_min_cells = cpu_pg_min_cells

        # calculate the per-priority group buffer allocations for the minimum and headroom buffers
        for traffic_type, priority_group in self.priority_group.set_dict.iteritems() :
            if priority_group.configured == False :
                continue
            pg_id = priority_group.id
            buffer_config = priority_group.set_dict['ingress_buffer']
            pg_min_percent  = buffer_config.min_percent
            if pg_id not in pg_buffer_limit :
                pg_buffer_limit[pg_id] = {}
            # minimum buffer calculation
            buffer_limit = int((pg_min_percent/100) * total_mem_cells)
            if buffer_limit == 0:
                # we always allocate at least one cell to avoid a blocked priority group (hardware issue)
                buffer_limit = wan_pg_min_cells * self.port_desc.get_weighted_port_count()
            error = self.buffer_desc.allocate_cells('ingress', 'pg_min', buffer_limit)
            if not error :
                pg_buffer_limit[pg_id]['pg_min'] = buffer_limit
            else :
                pg_buffer_limit[pg_id]['pg_min'] = wan_pg_min_cells
            self.buffer_comment +=  '# pg %d min limit %d\n' % (pg_id, pg_buffer_limit[pg_id]['pg_min'])

            # account for the CPU min cells
            error = self.buffer_desc.allocate_cells('ingress', 'pg_min', cpu_pg_min_cells)

            pg_buffer_limit[pg_id]['pg_hdrm'] = {}

        # global headroom allocation
        buffer_limit = (self.hardware.max_frame_cells
                        * self.port_desc.get_port_count())
        error = self.buffer_desc.allocate_cells('ingress', 'global_headroom', buffer_limit)
        if not error :
            self.buffer_desc.global_headroom = buffer_limit
        else :
            self.buffer_desc.global_headroom = 0
        self.buffer_comment += '# global headroom buffer: %d\n' % (self.buffer_desc.global_headroom)

        # calculate the total size of the shared buffer
        buffer_limit = self.buffer_desc.get_remaining_cells('ingress')
        error = self.buffer_desc.allocate_cells('ingress', 'shared', buffer_limit)
        if not error :
            self.buffer_desc.shared_buffer_limit = buffer_limit
        else :
            self.buffer_desc.shared_buffer_limit = 0
        self.buffer_comment += '# shared buffer limit: %d\n' % self.buffer_desc.shared_buffer_limit

        # calculate per-priority group limits on the shared buffer
        shared_buffer_limit = self.buffer_desc.shared_buffer_limit
        for traffic_type in self.priority_group.set_dict :
            priority_group     = self.priority_group.set_dict[traffic_type]
            if priority_group.configured == False :
                continue
            pg_id             = priority_group.id
            buffer_config     = priority_group.set_dict['ingress_buffer']
            pg_shared_percent = buffer_config.shared_percent
            buffer_limit      = int(shared_buffer_limit * (pg_shared_percent) / 100)
            if buffer_limit > shared_buffer_limit :
                buffer_limit = shared_buffer_limit
                error_msg = '%s traffic: ingress shared buffer limit' % traffic_type,
                error_msg += ' exceeds total shared buffer, reducing to %d\n' % shared_buffer_limit
                self.report_error(error_msg)
            if pg_id not in pg_buffer_limit :
                pg_buffer_limit[pg_id] = {}
            pg_buffer_limit[pg_id]['shared'] = buffer_limit
            self.buffer_comment += '# pg %d shared limit: %d\n' % (pg_id, pg_buffer_limit[pg_id]['shared'])
        self.buffer_comment += '\n'

    # ------------------------------------------------------------------
    #
    #          c a l c u l a t e __ e g r e s s __ b u f f e r s
    #
    # ------------------------------------------------------------------
    def __calculate_egress_buffers (self) :
        self.buffer_comment +=  '# egress: calculating buffer size\n'

        eg_sp_buffer_limit       = self.buffer_desc.eg_sp_buffer_limit
        queue_buffer_limit       = self.buffer_desc.queue_buffer_limit
        queue_buffer_unlimited   = self.buffer_desc.queue_buffer_unlimited
        queue_buffer_color_aware = self.buffer_desc.queue_buffer_color_aware
        color_aware_flag         = self.traffic.color_aware

        total_mem_cells   = self.hardware.total_buffer_cells
        num_service_pools = self.hardware.num_service_pools
        self.buffer_desc.init_available_cell_count('egress', total_mem_cells)
        self.buffer_comment += '# total mem cells: %d\n' % self.buffer_desc.get_remaining_cells('egress')

        for traffic_type in self.priority_group.set_dict :
            priority_group = self.priority_group.set_dict[traffic_type]
            if priority_group.configured == False:
                continue
            for q_type in priority_group.queue :
                if q_type not in queue_buffer_limit :
                    queue_buffer_limit[q_type] = {}
                if q_type not in queue_buffer_unlimited :
                    queue_buffer_unlimited[q_type] = {}
                num_queues = len(priority_group.queue[q_type])
                for q_id in priority_group.queue[q_type].keys() :
                    queue_buffer_limit[q_type][q_id] = {}
                    if priority_group.egress_buffer.set_dict[q_type].unlimited == True :
                        queue_buffer_unlimited[q_type][q_id] = 1
                    else :
                        queue_buffer_unlimited[q_type][q_id] = 0

                if priority_group.egress_buffer.set_dict[q_type].unlimited == True :
                    continue

                # allocate minimum buffer cells
                buffer_type = 'minimum'
                min_percent = priority_group.egress_buffer.set_dict[q_type].min_percent
                min_buffer_limit = int((min_percent / 100) * total_mem_cells)
                error = self.buffer_desc.allocate_cells('egress', buffer_type, min_buffer_limit)
                if error :
                    min_buffer_limit = 0
                # divide up the minimum cells amoung the assigned egress queues
                min_buffer_limit = int(min_buffer_limit / num_queues)
                for q_id in priority_group.queue[q_type].keys() :
                    queue_buffer_limit[q_type][q_id] = {}
                    queue_buffer_limit[q_type][q_id][buffer_type] = min_buffer_limit
                    self.buffer_comment += '# %s queue %d %s limit: %d\n' % (q_type,
                                                                             q_id,
                                                                             buffer_type,
                                                                             queue_buffer_limit[q_type][q_id][buffer_type])
        remaining_buffer_cells = self.buffer_desc.get_remaining_cells('egress')
        self.buffer_comment += '# total minimum buffer cells: %d\n' % (total_mem_cells - remaining_buffer_cells)

        # set service pool sizes: these can be oversubscribed and are not allocated from the total cells
        min_sp_limit = 3 * self.hardware.max_frame_cells  # hardware requirement, per register spec
        for sp_id in range(num_service_pools) :
            eg_sp_buffer_limit[sp_id] = {}
            eg_sp_buffer_limit[sp_id]['green']  = min_sp_limit
            if color_aware_flag == True :
                eg_sp_buffer_limit[sp_id]['yellow'] = min_sp_limit
                eg_sp_buffer_limit[sp_id]['red']    = min_sp_limit
            else :
                eg_sp_buffer_limit[sp_id]['yellow'] = None
                eg_sp_buffer_limit[sp_id]['red']    = None
            green_label = ''
            if color_aware_flag == True :
                green_label = 'green'
            if sp_id in self.egress_service_pool.pool_dict and \
                   self.egress_service_pool.pool_dict[sp_id].configured  == True :
                sp_percent = self.egress_service_pool.pool_dict[sp_id].percent
                green_sp_limit = int(total_mem_cells * (sp_percent / 100))
                eg_sp_buffer_limit[sp_id]['green'] = green_sp_limit
                if color_aware_flag == True :
                    # set the color-aware limits
                    yellow_sp_cells = int(eg_sp_buffer_limit[sp_id]['green'] * float(self.traffic.yellow_limit_percent / 100))
                    eg_sp_buffer_limit[sp_id]['yellow'] = yellow_sp_cells
                    red_sp_cells = int(eg_sp_buffer_limit[sp_id]['green'] * float(self.traffic.red_limit_percent / 100))
                    eg_sp_buffer_limit[sp_id]['red'] = red_sp_cells
            self.buffer_comment += '# service pool %d %s limit: %d\n' % (sp_id, green_label, eg_sp_buffer_limit[sp_id]['green'])
            if color_aware_flag == True :
                self.buffer_comment += '# service pool %d yellow limit: %d\n' % (sp_id, eg_sp_buffer_limit[sp_id]['yellow'])
                self.buffer_comment += '# service pool %d red limit: %d\n' % (sp_id, eg_sp_buffer_limit[sp_id]['red'])

        # set service pool buffer limits
        for traffic_type, priority_group in self.priority_group.set_dict.iteritems() :
            if priority_group.configured == False :
                continue
            for q_type in priority_group.queue :
                sp_id             = priority_group.service_pool
                sp_mem_cells      = eg_sp_buffer_limit[sp_id]['green']
                q_buffer_config = priority_group.egress_buffer.set_dict[q_type]
                num_queues = len(priority_group.queue[q_type])
                if q_type not in queue_buffer_unlimited :
                    queue_buffer_unlimited[q_type] = {}
                if q_type not in queue_buffer_color_aware :
                    queue_buffer_color_aware[q_type] = {}
                green_sp_cells  = None
                yellow_sp_cells = None
                red_sp_cells    = None
                buffer_dict = { 'shared' : green_sp_cells }
                if q_buffer_config.unlimited == False :
                    green_sp_cells  = int((q_buffer_config.sp_percent/100) * sp_mem_cells)
                    if color_aware_flag == True :
                        yellow_sp_cells = int(green_sp_cells * float(self.traffic.yellow_limit_percent / 100))
                        red_sp_cells = int(green_sp_cells * float(self.traffic.red_limit_percent / 100))
                        buffer_dict = { 'shared green'  : green_sp_cells,
                                        'shared yellow' : yellow_sp_cells,
                                        'shared red'    : red_sp_cells }
                    else :
                        buffer_dict = { 'shared' : green_sp_cells }

                for q_id in priority_group.queue[q_type].keys() :
                    for name in buffer_dict.keys() :
                        if q_buffer_config.unlimited == True :
                            self.buffer_comment += '# %s queue %d %s limit: unlimited\n' % (q_type, q_id, name)
                        else :
                            if q_id not in queue_buffer_limit[q_type] :
                                queue_buffer_limit[q_type][q_id] = {}
                            queue_buffer_limit[q_type][q_id][name] = buffer_dict[name]
                            self.buffer_comment += '# %s queue %d %s limit: %s\n' % (q_type, q_id, name,
                                                                                     str(buffer_dict[name]))
                        queue_buffer_unlimited[q_type][q_id]   = q_buffer_config.unlimited
                        queue_buffer_color_aware[q_type][q_id] = color_aware_flag

        self.buffer_comment += '\n'

    # ----------------------------------------------------------
    #
    #          c a l c u l a t e __ b u f f e r __ s i z e s
    #
    # ----------------------------------------------------------
    def __calculate_buffer_sizes (self) :
        self.buffer_comment = ""
        self.__calculate_ingress_buffers()
        self.__calculate_egress_buffers()

    # ----------------------------------------------------------
    #
    #            ____ s e t __ m c __ q u e u e s
    #
    # ----------------------------------------------------------
    def __set_mc_queues (self) :

        num_mc_queues = self.hardware.num_mc_queues

        if num_mc_queues == 8 :
            self.cos_queue.match_mc_queues()

    # ----------------------------------------------------------
    #
    #     ____ c r e a t e __ q u e u e __ d i c t
    #
    # ----------------------------------------------------------
    def __create_queue_dict (self) :
        self.queue_dict = {}
        cpu_queue = 'cpu'
        for traffic_type in self.priority_group.set_dict :
            priority_group = self.priority_group.set_dict[traffic_type]
            if priority_group.configured == False :
                continue
            if cpu_queue not in self.queue_dict :
                    self.queue_dict[cpu_queue] = {}
            for q_type in priority_group.queue :
                if q_type not in self.queue_dict :
                    self.queue_dict[q_type] = {}
                weight = priority_group.weight
                for q_id in priority_group.queue[q_type].keys() :
                    self.queue_dict[q_type][q_id] = weight
                    if q_type == 'uc' :
                        # use the same values for the CPU queue
                        self.queue_dict[cpu_queue][q_id] = weight
        # set the CPU exception queue weights
        for q_id in range(self.exception_q_start, self.num_cpu_queues) :
            self.queue_dict[cpu_queue][q_id] = self.exception_q_weight

    def __update_uft (self) :
        cmd = '/usr/lib/cumulus/uft-update -p %s' % self.forwarding_table.profile
        subprocess.call(cmd.split())

    def __update_riot (self) :
        cmd = '/usr/lib/cumulus/riot-update -p %s' % self.vxlan_routing_overlay.profile
        subprocess.call(cmd.split())

    # ----------------------------------------------------------
    #
    #                  c h e c k __ c o n f i g
    #
    # ----------------------------------------------------------
    def check_config (self) :
        for manager in self.manager_list:
            manager.check_config()
            
        # Below code for port_group egress_scheduler - placeholder for now - no checks
        for port_group in self.egress_sched.set_dict:
            obj = self.egress_sched.set_dict[port_group]
            for attr in obj.config_dict:
                #print "egress sched config dict attr:{} obj has attr:{} configured:{}".format(attr,hasattr(obj, attr),obj.configured)
                pass
        
        # Below code for default egress scheduler - placeholder for now - no checks
        for attr in self.default_egress_sched.config_dict:
            #print "Default egress sched config dict attr:{} obj has attr:{} configured:{}".format(attr,hasattr(self.default_egress_sched, attr),self.default_egress_sched.configured)
            pass
        
        # Check for weight/bw_percent config either in priority group or in default_egress_sched
        for pg_name, priority_group in self.priority_group.set_dict.iteritems() :
            if priority_group.configured and \
                    ((hasattr(priority_group,'weight') and priority_group.weight != -1) or \
                    (hasattr(priority_group,'bw_percent') and priority_group.bw_percent != -1)):
                        #Priority group is configured with weight or bw_percent
                        # Validate that default egress_sched is not configured nor port_group egress_sched is configured
                        port_group_egress_sched_configured = False
                        for port_group in self.egress_sched.set_dict:
                            if self.egress_sched.set_dict[port_group].configured:
                                port_group_egress_sched_configured = True
                                break
                        if self.default_egress_sched.configured or port_group_egress_sched_configured:
                            self.report_error("We do not support configuring egress scheduler bw_percent as well as priority_group.weight")
            elif priority_group.configured and not self.default_egress_sched.configured:
                # Bandwidth not configured in either priority_group nor any default egress scheduler per switch priority
                self.report_error("Need to configure bandwidth for egress scheduling either in priority_group or per switch_priority")

	# Validate that we do not have any common port_set between pfc and link_pause configs
        pfc_port_set = []
        link_pause_port_set = []
        for grp_name in self.pfc.set_dict:
            pfc_port_set.extend(self.pfc.set_dict[grp_name].sdk_port_list)
        for grp_name in self.link_pause.set_dict:
            link_pause_port_set.extend(self.link_pause.set_dict[grp_name].sdk_port_list)
        common_port = list(set(link_pause_port_set).intersection(pfc_port_set))
        if common_port:
            self.report_error("Common ports {} found between pfc and link_pause port set - invalid config".format(common_port))

        # Validate that flow_control pool is configured if pfc or link_pause is enabled
        if self.pfc.is_configured or self.link_pause.is_configured:
            if self.flow_control.service_pool == -1:
                self.report_error("Need to configure service pool for flow_control explicitly for PFC/LinkPause to work")

	# Validate that we do not have any common port_set between port_group configs of egress scheduler configs
        common_port = []
        all_port_set = []
        grp_port_set = []
        for grp_name in self.egress_sched.set_dict:
            grp_port_set = []
            grp_port_set.extend(self.egress_sched.set_dict[grp_name].sdk_port_list)
            common_port.extend(list(set(all_port_set).intersection(grp_port_set)))
            all_port_set = list(set(all_port_set).union(grp_port_set))

        if common_port:
            self.report_error("common port_set:%s found between egress scheduler configs of multiple port-groups" % (common_port))

    # ----------------------------------------------------------
    #
    #                  p r o c e s s __ c o n f i g
    #
    # ----------------------------------------------------------
    def process_config (self) :
        self.__set_mc_queues()
        for manager in self.manager_list:
            manager.process_config()
        self.__create_queue_dict()
        self.__calculate_buffer_sizes()
        self.__update_uft()
        self.__update_riot()

# ==============================================================================
#
#              F O R W A R D I N G __ R E G I S T E R __ M A N A G E R
#
# ==============================================================================
class ForwardingRegisterManager(RegisterManager):

    def __init__(self, chip, config_manager) :
        super(ForwardingRegisterManager,self).__init__(chip, config_manager)
        self.section_dict = {"hashing" : []}

    def generate_output_objects(self) :
        pass

    def print_output_objects(self, file_dict) :
        if self.config_manager.traffic.disable_custom_datapath_config == 1 :
            return
        file = (file_dict['register'])
        for line in self.config_manager.forwarding_section :
            file.write(line)

# ==============================================================================
#
#                      D A T A P A T H __ C R E A T O R
#
# ==============================================================================
class DatapathCreator:

    # ----------------------------------------------------------
    #
    #                    __ i n i t __
    #
    # ----------------------------------------------------------
    def __init__(self, chip, chip_manager, config_manager):
        self.chip             = chip
        self.config_manager   = config_manager
        self.chip_manager     = chip_manager

    # ----------------------------------------------------------
    #
    #                w r i t e __ d a t a p a t h
    #
    # ----------------------------------------------------------
    def write_datapath(self):

        # write out the parameter file preamble
        parameter_file = open(self.sdk_parameter_file, 'w')
        parameter_file.write("\n# ---------------------------------------------------------------------\n")
        parameter_file.write("#\n")
        parameter_file.write("# Automatically generated by /usr/lib/cumulus/datapath-update: \n")
        parameter_file.write("# BCM SDK datapath parameters for %s\n" % self.chip.__class__.__name__)
        parameter_file.write("#\n")
        parameter_file.write("# ---------------------------------------------------------------------\n")
        parameter_file.write("\n")

        if self.config_manager.error_comment != '' :
            parameter_file.write('# -------  Error Messages --------\n')
            parameter_file.write(self.config_manager.error_comment)
            parameter_file.write('\n')

        # write out the register file preamble
        register_file = open(self.bcm_register_file, 'w')
        register_file.write("# ------------------------------------------------------------\n")
        register_file.write("#\n")
        register_file.write("# Automatically generated by /usr/lib/cumulus/datapath-update: \n")
        register_file.write("# datapath register configuration for %s\n" % self.chip.__class__.__name__)
        register_file.write("#\n")
        register_file.write("# ------------------------------------------------------------\n")
        register_file.write("\n")
        register_file.write(self.config_manager.buffer_comment)

        self.chip_manager.print_output_objects({'parameter' : parameter_file, 'register' : register_file })
        parameter_file.close()
        register_file.close()

    # ----------------------------------------------------------
    #
    #          w r i t e __ d a t a p a t h __ c o n f i g
    #
    # ----------------------------------------------------------
    def write_datapath_config (self) :
        f = open(self.output_file, 'w')
        f.write("stubbed output")
        f.close()

    # ----------------------------------------------------------
    #
    #                c r e a t e __ d a t a p a t h
    #
    # ----------------------------------------------------------
    def create_datapath(self,
                        hw_desc,
                        traffic_config,
                        datapath_config,
                        forwarding_config,
                        bcm_register_file,
                        sdk_parameter_file,
                        dryrun_flag) :

        if hw_desc:
            self.config_manager.read_config_file(hw_desc)

        self.config_manager.init_config()  # order matters!  must be called just after hw desc read
        self.config_manager.read_config_file(traffic_config)

        if datapath_config:
            self.config_manager.read_config_file(datapath_config)

        if forwarding_config:
            self.config_manager.read_forwarding_config_file(forwarding_config)

        self.config_manager.check_config()
        if self.config_manager.traffic.disable_custom_datapath_config == True :
            logger.debug('Custom datapath configuration is disabled')

        if dryrun_flag == True:
            return;

        self.config_manager.process_config()

        self.sdk_parameter_file = sdk_parameter_file
        self.bcm_register_file  = bcm_register_file

        self.chip_manager.generate_output_objects()
        self.write_datapath()
        #self.config_manager.dump_port_map()


# ----------------------------------------------------------
#
#                         m a i n
#
# ----------------------------------------------------------
def main(argv) :

    bcm_register_file  = None
    sdk_parameter_file = None
    quiet_flag         = False
    dryrun_flag        = False

    use_msg  = "datapath-update"
    use_msg += " -c <hardware description file>"
    use_msg += " -p <port config file>"
    use_msg += " -m <port map config file>"
    use_msg += " -t <traffic config file>"
    use_msg += " -d <datapath config file>"
    use_msg += " -f <forwarding config file>"
    use_msg += " -r <output register file>"
    use_msg += " -g <output parameter file>"
    use_msg += " -q/--quiet"
    use_msg += " --dryrun"

    try:
        opts, args = getopt.getopt(argv,
                                   "hqc:p:m:l:t:d:f:r:g:",
                                   ["quiet", "dryrun", "hw=","traffic=","datapath=","forwarding=","register=","parameter="])
    except getopt.GetoptError:
        error_msg = 'input error: s.b. %s' % use_msg
        if self.dryrun_flag and not self.quiet_flag:
            print >>sys.stderr, error_msg
        else:
            logger.error(error_msg)
        sys.exit(2)
    for opt, arg in opts:
        if opt == '-h':
            print '%s' % use_msg
            sys.exit()
        elif opt in ("-t", "--traffic"):
            traffic_config = arg
        elif opt in ("-c", "--hw"):
            hw_desc = arg
        elif opt in ("-d", "--datapath"):
            datapath_config = arg
        elif opt in ("-f", "--forwarding"):
            forwarding_config = arg
        elif opt in ("-r", "--register"):
            bcm_register_file = arg
        elif opt in ("-g", "--parameter"):
            sdk_parameter_file = arg
        elif opt in ("--dryrun"):
            dryrun_flag = True
        elif opt in ("-q", "--quiet"):
            quiet_flag = True

    if dryrun_flag == False:
        logger.debug('Generating a register settings file: ')
    else:
        logger.debug('Checking input files: ')
    logger.debug('hw description is %s' % hw_desc)
    logger.debug('traffic config is %s' % traffic_config)
    logger.debug('datapath config is %s' % datapath_config)
    logger.debug('forwarding config is %s' % forwarding_config)
    if dryrun_flag == False:
        logger.debug('BCM register file is %s' % bcm_register_file)
        logger.debug('SDK parameter file is %s' % sdk_parameter_file)
        logger.debug('Generating %s and appending to %s from %s' % (bcm_register_file, sdk_parameter_file, datapath_config))

    # fetch the chip object
    platform = cumulus.platforms.probe()
    chip = platform.switch.chip
    if not (isinstance(chip, cumulus.platform.TridentTwo_56850_Chip) or \
            isinstance(chip, cumulus.platform.TridentTwo_56854_Chip) or \
            isinstance(chip, cumulus.platform.TridentTwoPlus_56860_Chip) or \
            isinstance(chip, cumulus.platform.TridentTwoPlus_56864_Chip) or \
            isinstance(chip, cumulus.platform.TridentThreeX5_56771_Chip) or \
            isinstance(chip, cumulus.platform.TridentThreeX7_56870_Chip) or \
            isinstance(chip, cumulus.platform.TridentThreeX7_56873_Chip) or \
            isinstance(chip, cumulus.platform.Helix4Chip) or \
            isinstance(chip, cumulus.platform.MaverickChip) or \
            isinstance(chip, cumulus.platform.TomahawkChip) or \
            isinstance(chip, cumulus.platform.TomahawkPlus_56965_Chip) or \
            isinstance(chip, cumulus.platform.TomahawkPlus_56967_Chip) or \
            isinstance(chip, cumulus.platform.TomahawkTwoChip) or \
            isinstance(chip, cumulus.platform.TomahawkTwo_56970_Chip) or \
            isinstance(chip, cumulus.platform.TomahawkThreeChip) or \
            isinstance(chip, cumulus.platform.TomahawkThree_56980_Chip) or \
            isinstance(chip, cumulus.platform.QumranChip) or \
            isinstance(chip, cumulus.platform.Qumran_88370_Chip) or \
            isinstance(chip, cumulus.platform.Qumran_88375_Chip)):
        if not dryrun_flag or (dryrun_flag and not isinstance(chip, cumulus.platform.SpectrumChip)):
            error_msg = 'Chip %s not supported' % chip.__class__.__name__
            if dryrun_flag and not quiet_flag:
                print >>sys.stderr, error_msg
            else:
                logger.error(error_msg)
            sys.exit(1)

    config_manager = ConfigManager(platform, dryrun_flag, quiet_flag)
    chip_manager   = ChipManagerFactory.get_new_manager(chip, config_manager)
    if chip_manager == None :
        config_manager.report_error('Chip %s not supported' % chip.__class__.__name__)
        sys.exit(1)

    # clear the file paths we don't want to read in
    if dryrun_flag and dryrun_traffic_file_only:
        hw_desc           = None
        forwarding_config = None

    # create the datapath from the input files
    creator = DatapathCreator(chip, chip_manager, config_manager)
    creator.create_datapath(hw_desc,
                            traffic_config,
                            datapath_config,
                            forwarding_config,
                            bcm_register_file,
                            sdk_parameter_file,
                            dryrun_flag)
    #creator.review_datapath()

    if config_manager.input_file_error == True:
        sys.exit(1)

# ----------------------------------------------------------
#
#                        e n t r y
#
# ----------------------------------------------------------

if __name__ == "__main__":
    try:
        main(sys.argv[1:])
    except SystemExit as exc:
        ret_value = exc.code
        sys.exit(ret_value)
    except:
        error_msg = 'Execution error in datapath-update: exiting'
        print >>sys.stderr, error_msg

        # if debugging, provide exception details and traceback
        ex_type, ex_value, ex_traceback = sys.exc_info()
        logger.debug("datapath-update Exception type: %s" % ex_type.__name__)
        logger.debug("datapath-update Exception message: %s" % ex_value)
        logger.debug("datapath-update Stack trace:")
        trace_back = traceback.extract_tb(ex_traceback)
        for trace in trace_back:
            logger.debug("%s:%d %s, %s" % (trace[0], trace[1], trace[2], trace[3]))

        # exit with error
        sys.exit(1)
    else:
        sys.exit(0)
