#!/usr/bin/python3
# Copyright (C) 2020-2023 NVIDIA Corporation. ALL RIGHTS RESERVED.
# 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 cumulus.porttab

from cumulus.utilities 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

global INT_MAX
INT_MAX = 2147483647
global MAX_CABLE_LENGTH_LOSSLESS_BUFFERS
# max cable-length for roce lossless - 100kms
MAX_CABLE_LENGTH_LOSSLESS_BUFFERS = 100000

"""

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):
    _port_speed_map = { 'sfp'    : 1000,
                        'sfp+'   : 10000,
                        'sfp28'  : 25000,
                        'qsfp+'  : 40000,
                        'qsfp28' : 100000,
                        'qsfp56' : 200000,
                        'qsfpdd' : 400000,
                        'osfp'   : 800000 }

    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      = []
        self.linux_2_sp_factor    = {}
        self.linux_2_speed        = {}

        sfp_path='/var/lib/cumulus/sfptab'
        phy_path='/var/lib/cumulus/phytab'
        try:
            sfpt = open(sfp_path, 'r')
        except IOError as e:
            if e.errno == errno.ENOENT:
                raise NotConfigured('sfptab not configured')
            else:
                raise e
        for line in sfpt.readlines():
            line = line.strip()
            if '#' in line or len(line) == 0:
                continue
            sfpinfo = line.split()
            self.linux_2_speed[sfpinfo[0]] = int(self._port_speed_map[sfpinfo[1]]) / 1000
        sfpt.close()

        try:
            phyt = open(phy_path, 'r')
        except IOError as e:
            if e.errno == errno.ENOENT:
                raise NotConfigured('phytab not configured')
            else:
                raise e
        for line in phyt.readlines():
            line = line.strip()
            if '#' in line or len(line) == 0:
                continue
            phyinfo = line.split()
            self.linux_2_sp_factor[phyinfo[1]] = phyinfo[3]
        phyt.close()

        logical_port_num = 0
        self.portt = cumulus.porttab.porttab()
        for pe in self.portt.porttab:
            sdk_port_label = pe.bcm
            linux_intf_label = pe.linux_intf
            try: 
                speed = int(self.linux_2_speed[linux_intf_label]) / int(self.linux_2_sp_factor[linux_intf_label][0])
            except:
                speed = int(self._port_speed_map['sfp']) / 1000

            phy_port = int(pe.bcm[2:]) + 1
            self._add_port(sdk_port_label, speed, logical_port_num, linux_intf_label, phy_port)
            logical_port_num += 1

        self.linux_intf_list.sort(key=lambda x: self._swp_sort_pattern(x))
        sdk_port_label = 'cpu0'
        self._add_port('cpu0', 0, 0, 'None', -1)

        self.finish_port_config()

    def _swp_sort_pattern(self, item):
        pattern = re.compile(r'''swp(\d+)s?(\d)?''')
        num = 0

        r = pattern.match(item)
        if r:
            if r.group(2):
                num = float(r.group(1) + '.' + r.group(2))
            else:
                num = float(r.group(1))
        return num

    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 = 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 = 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.maxsize
        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 = 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(range(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 = list(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 = list(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.items():
            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.items() :
            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)

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

    def __init__(self, chip, config_manager) :
        self.creator_list = []
        super(VxChipManager,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 = {
            'SpectrumChip'              : SpectrumChipManager,
            'SpectrumFourChip'          : SpectrumChipManager,
            'SpectrumFiveChip'          : SpectrumChipManager,
            'VXChip'                    : VxChipManager,
    }

    @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]
            try:
                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
            except ValueError:
                self.config_mgr.report_error('%s: value for "%s" type %s not supported' % (self.name, name, type(value)))
                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
                elif 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.items() :
            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.items() :
            member.check_config()

    def process_config (self) :
        super(ConfigSet,self).process_config()
        for member_name, member in self.set_dict.items() :
            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, list(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['l2-heavy-3']   = 1
        self.good_value_dict['v4-lpm-heavy-1']   = 1
        self.good_value_dict['v6-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
        self.good_value_dict['mpls'] = 1
        self.good_value_dict['ipmc-max'] = 1
        self.good_value_dict['ecmp-nh-heavy']   = 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, list(self.good_value_dict.keys())),
                                         line,
                                         file_name,
                                         line_num)
        else:
            super(FwdTableConfig,self).set_config(name, value, line, file_name, line_num)

# ==============================================================================
#
#                         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,
            cumulus.platform.SpectrumFourChip,
            cumulus.platform.SpectrumFiveChip,
        ))):
            if self.name == 'cut_through_enable' and flag == 'false':
                self.config_mgr.report_error('cut-through cannot be disabled for Spectrum switch',
                                             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
        self.good_value_dict[2048] = 1
        self.good_value_dict[4096] = 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, list(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.items() :
            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.items() :
            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 = { 'lossy_headroom': 'int',
                        'reserved'    : 'int',
                        'shared_size' : 'int',
                        '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)
        super(IngressBufferConfig,self).set_config(name, value, line, file_name, line_num)

        if name[0] == 'shared_size' or name[0] == 'reserved':
            val = getattr(self, name[0], None)
            if val and val < 0:
                self.config_mgr.report_error("buffer size '%d' cannot be negative" % (val),
						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_size'        : 'int',
                        'reserved'       : 'int',
                        '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 list(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_size' or name == 'reserved':
            int_value = int(value)
            if int_value < 0:
                self.config_mgr.report_error("buffer size '%d' cannot be negative" % (int_value),
                                                line, file_name, line_num)

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

        for name in self.config_dict :
            if not hasattr(self, name) :
                if name == 'reserved':
                    setattr(self, name, 0)
                else:
                    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 = { 'shared_size'        : 'int',
                        'service_pool'      : 'int',
                        'reserved'       : 'int',
                        '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 list(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_size' or name == 'reserved':
            int_value = int(value)
            if int_value < 0:
                self.config_mgr.report_error("buffer size '%f' cannot be negative" % (int_value),
						line, file_name, line_num)

    def check_config (self) :
        self.color_aware_flag = False
        self.unlimited        = True

        if hasattr(self, 'reserved') or hasattr(self, 'shared_size') :
            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.egress_buffer   = EgressBufferConfig('port')
        parameter_dict = { 'management' : MgmtBufferConfig(),
                            '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 name[0] == "service_pool" and len(name) >= 3:
            try:
                sp_id = int(name[1])
            except ValueError:
                self.config_mgr.report_error("Invalid service_pool id defined as {}".format(name[1]), line, file_name, line_num)
                return -1
            if sp_id > self.config_mgr.hardware.num_service_pools:
                self.config_mgr.report_error("Service pool id %d is out of range" % (sp_id), line, file_name, line_num)
            service_pool_name = name[0] + "_" + name[1] + "_" + name[2]
            if name[2] == "ingress_buffer":
                if service_pool_name not in self.set_dict:
                    self.set_dict[service_pool_name] = IngressBufferConfig(service_pool_name)
                    self.set_dict[service_pool_name].init_config(self.config_mgr)
            elif name[2] == "egress_buffer":
                if service_pool_name not in self.set_dict:
                    self.set_dict[service_pool_name] = EgressBufferConfig(service_pool_name)
                    self.set_dict[service_pool_name].init_config(self.config_mgr)
            name[2] = service_pool_name
            name = name[2:]

        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) :
        default_port_pool_ing_buff_name = 'service_pool_0_ingress_buffer'
        if default_port_pool_ing_buff_name not in self.set_dict:
            self.set_dict[default_port_pool_ing_buff_name] = IngressBufferConfig(default_port_pool_ing_buff_name)
        default_port_pool_egr_buff_name = 'service_pool_0_egress_buffer'
        if default_port_pool_egr_buff_name not in self.set_dict:
            self.set_dict[default_port_pool_egr_buff_name] = EgressBufferConfig(default_port_pool_egr_buff_name)

        # Set the default for iPort.Pool[0] and ePort.Pool[0] reserved buffer
        for name, buff in self.set_dict.items():
            if name.startswith('service_pool_') and 'ingress_buffer' in name and not hasattr(buff, 'reserved'):
                buff.reserved = 10240

            if name.startswith('service_pool_') and 'egress_buffer' in name and not hasattr(buff.set_dict['uc'], 'reserved'):
                buff.set_dict['uc'].reserved = 10240

            if name == "egress_buffer" and not hasattr(buff.set_dict['mc'], 'reserved'):
                buff.set_dict['mc'].reserved = 10240

        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 = {'ingress_service_pool'            : 'int',
                        'egress_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, 'ingress_service_pool'):
            self.ingress_service_pool = -1
        if not hasattr(self, 'egress_service_pool'):
            self.egress_service_pool = -1
        if not hasattr(self.egress_buffer, 'reserved'):
            self.egress_buffer.reserved = 0
        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 = {'ingress_service_pool'            : 'int',
                        'egress_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, 'ingress_service_pool'):
            self.ingress_service_pool = -1
        if not hasattr(self, 'egress_service_pool'):
            self.egress_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')
        self.cos_0     = EgressBufferConfig('cos_0')
        self.cos_1     = EgressBufferConfig('cos_1')
        self.cos_2     = EgressBufferConfig('cos_2')
        self.cos_3     = EgressBufferConfig('cos_3')
        self.cos_4     = EgressBufferConfig('cos_4')
        self.cos_5     = EgressBufferConfig('cos_5')
        self.cos_6     = EgressBufferConfig('cos_6')
        self.cos_7     = EgressBufferConfig('cos_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,
                        '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 }
        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,list(self.set_dict.keys())), line, file_name, line_num)
            self.set_dict[egr_queue_name] = EgressBufferConfig(egr_queue_name)
            self.set_dict[egr_queue_name].init_config(self.config_mgr)
        super(EgressQueueBufferConfigSet,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        for tc in range(8):
            egr_buff = getattr(self, f'egr_queue_{tc}')
            if not hasattr(egr_buff.set_dict['uc'], 'reserved'):
                egr_buff.set_dict['uc'].reserved = 1024
            if not hasattr(egr_buff.set_dict['mc'], 'reserved'):
                egr_buff.set_dict['mc'].reserved = 0

        for sp in range(8):
            egr_mc_sp_buff = getattr(self, f'cos_{sp}')
            if not hasattr(egr_mc_sp_buff.set_dict['mc'], 'reserved'):
                egr_mc_sp_buff.set_dict['mc'].reserved = 10240

        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',
                           '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] == '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)

        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.set_dict['ingress_buffer'], 'lossy_headroom') :
            self.set_dict['ingress_buffer'].lossy_headroom = 19456

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

        if isinstance(
            self.config_mgr.chip,
            (
                cumulus.platform.SpectrumChip,
                cumulus.platform.SpectrumFourChip,
                cumulus.platform.SpectrumFiveChip,
            ),
        ) and (
            self.egress_buffer.set_dict['uc'].configured or
            self.egress_buffer.set_dict['mc'].configured
        ):
            self.config_mgr.report_error("priority group '%s' does not support egress buffer configurations." % (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:
                    self.configured = False
            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.items() :
            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.service1  = PriorityGroupConfig('service1')
        self.service2  = PriorityGroupConfig('service2')
        self.service3  = PriorityGroupConfig('service3')
        self.service4  = PriorityGroupConfig('service4')
        self.service5  = PriorityGroupConfig('service5')
        self.service6  = PriorityGroupConfig('service6')
        self.service7  = PriorityGroupConfig('service7')
        group_dict  = { 'bulk'     : self.bulk,
                        'service1' : self.service1,
                        'service2' : self.service2,
                        'service3' : self.service3,
                        'service4' : self.service4,
                        'service5' : self.service5,
                        'service6' : self.service6,
                        'service7'  : self.service7 }
        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,list(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
        unique_cos_list = []
        for pg_name, priority_group in self.set_dict.items() :
            if priority_group.configured == True:
                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)
                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 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,
                    cumulus.platform.SpectrumFourChip,
                    cumulus.platform.SpectrumFiveChip,
                    cumulus.platform.VXChip
                ))
            )
            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.items():
            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) == 1 and range_list[0] == "allports":
                continue
            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, LinkPause and PFC Watchdog need not do this check
        if not re.search(r'(^pfc$|^link pause$|^pfc watchdog$)',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' }
        self.type_label = type_label
        self.configured_flag = False
        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
        self.configured_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
        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 ECN profile we do not need port_set
                self.port_set = ['allports']
        if self.configured_flag:
            super(EcnPortGroupConfig,self).check_config()
        if not hasattr(self, 'egress_queue_list'):
            if self.configured_flag:
                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 :
                        # We do not support min-shapers for SPC5 on TC 5-7
                        if re.search('sn5640', self.config_mgr.platform.name) and re.search('egr_queue_[5-7]\.shaper', name) and shaper_list[0] != 0:
                            self.config_mgr.report_error("egr_queue 5-7 does not support min-shaper on Spectrum5", line, file_name, line_num)

                        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 = 2147483647
                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
# ==============================================================================
#
#                   PTP_S H A P E R __ P O R T __ G R O U P __ C O N F I G
#
# ==============================================================================
class PtpShaperPortGroupConfig(PortGroupConfig):
    def __init__ (self, name, type_label) :

        config_dict = {'ptp_shaper_enable'  : 'bool'}
        self.name = name
        self.type_label = type_label
        super(PtpShaperPortGroupConfig,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
        if name[0] == 'port_set' :
            super(PtpShaperPortGroupConfig, self).set_config(name, value, line, file_name, line_num)
        elif name[0] == 'ptp_shaper_enable':
            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)
            else:
                super(PtpShaperPortGroupConfig, self).set_config(name, value, line, file_name, line_num)

    def check_config(self) :
        if not self.configured_flag:
            return
        if not hasattr(self, 'ptp_shaper_enable'):
            self.ptp_shaper_enable = False
            logger.debug('%s: ptp_shaper_enable defaulting to false' % self.name)
        super(PtpShaperPortGroupConfig,self).check_config()
    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 0' % self.name)
            self.xoff_size = 0
        if not hasattr(self, 'xon_delta') :
            logger.debug('%s: xoff_delta defaulting to 0' % self.name)
            self.xon_delta = 0
        if not hasattr(self, 'cable_length') :
            logger.debug('%s: cable_length defaulting to 100' % self.name)
            self.cable_length = 100
        if not hasattr(self, 'port_buffer_bytes') :
            logger.debug('%s: port_buffer_bytes defaulting to 0' % self.name)
            self.port_buffer_bytes = 0
        # All the above parameters are optional now with dynamic PFC/Link Pause xoff/xon/HR calculations
        super(FcPortGroupConfig,self).check_config()

        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':
            size = int(value)
            if size < 0:
                self.config_mgr.report_error("value %d should be a non-negative integer" % size,
                                             line,
                                             file_name,
                                             line_num)
        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 > INT_MAX:
                self.config_mgr.report_error("value %d is greater than maximum supported" % size,
                                             line,
                                             file_name,
                                             line_num)
        if name[0] == 'cable_length':
            size = int(value)
            if size <= 0:
                self.config_mgr.report_error("value %d should be a positive integer" % size,
                                             line,
                                             file_name,
                                             line_num)
            if size > MAX_CABLE_LENGTH_LOSSLESS_BUFFERS:
                self.config_mgr.report_error("cable-length %d is beyond maximum allowed cable length %d for lossless buffer config" % size,
                                             MAX_CABLE_LENGTH_LOSSLESS_BUFFERS,
                                             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))

            # On SPC5 - sn5640 - we do not support dwrr for TC5-7
            if re.search('egr_queue_[5-7]_bw_percent', name):
                int_value = int(value)
                if re.search('sn5640', self.config_mgr.platform.name) and int_value > 0:
                    self.config_mgr.report_error("DWRR is not supported for egr_queue 5-7 on Spectrum5", line, file_name, line_num)

        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 list(self.config_dict.keys()):
                if hasattr(self, attr):
                    attr_count += 1
        sum_bw_percent = 0
        for attr in list(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)

                    # We do not support DWRR weight 0 for TC5-7 on SPC5
                    if re.search('sn5640', self.config_mgr.platform.name) and re.search('egr_queue_[5-7]_bw_percent', attr):
                        self.config_mgr.report_error(f"{self.name}: egr_queue 5-7 only supports strict ETS on Spectrum5")
                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 __ W A T C H D O G __ P O R T __ G R O U P __ C O N F I G
#
# ==============================================================================
class PfcWatchdogPortGroupConfig(PortGroupConfig):
    def __init__ (self, name, type_label) :
        config_dict = {}
        self.configured    = False
        self.name = name
        self.type_label = type_label
        super(PfcWatchdogPortGroupConfig,self).__init__(name,
                                                type_label,
                                                config_dict)

    def set_config(self, name, value, line, file_name, line_num) :
        self.configured    = True
        super(PfcWatchdogPortGroupConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        super(PfcWatchdogPortGroupConfig,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' }
        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',
                        'port_default_priority'       : 'int'}
        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' and source_type != 'port':
                        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)
            elif name[0] == 'port_default_priority':
                super(SourcePortGroupConfig,self).set_config(name, value, line, file_name, line_num)
                sp = int(value)
                if sp < 0 or sp >= 8:
                    self.config_mgr.report_error("Invalid switch priority configured. Should be within 0-7",
                                             line,
                                             file_name,
                                             line_num)
            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
        if not hasattr(self, 'port_default_priority') :
            self.port_default_priority = 0
        super(SourcePortGroupConfig,self).check_config()
        if 'port' in self.packet_priority_source_set and len(self.packet_priority_source_set) != 1:
            self.config_mgr.report_error("cannot configure any other priority in priority_source_set if 'port' is configured")

        # check the priority configurations
        for priority_type in list(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 list(self.priority_value_dict.keys()):
            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' and priority != 'port':
                    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)
        elif name[0] == 'port_default_priority':
            sp = int(value)
            if sp < 0 or sp >= 8:
                self.config_mgr.report_error("Invalid switch priority configured. Should be within 0-7",
                                             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
        for pg_name in self.priority_group_list:
            if pg_name not in list(self.config_mgr.priority_group.set_dict.keys()):
                self.config_mgr.report_error("priority group {} not found in supported list of priority_groups:{}".format(pg_name,list(self.config_mgr.priority_group.set_dict.keys())))
            else:
                if not hasattr(self.config_mgr.priority_group.set_dict[pg_name], 'cos_list') or len(self.config_mgr.priority_group.set_dict[pg_name].cos_list) == 0:
                    self.config_mgr.report_error("priority group '%s' cos_list not defined or no cos_list mapped" % (pg_name))

        if 'port' in self.packet_priority_source_list and len(self.packet_priority_source_list) != 1:
            self.config_mgr.report_error("cannot configure any other priority in traffic priority_source_set if 'port' is configured")

        """
        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.

        For remark configuration, there are two different configuration knobs to
        enable. We need to enable re-write on the ingress port, and then the 
        remark mapping is taken from the egress port. We should allow configuration
        of remark mapping for dscp with rewrite set to pcpdei or None on the port.
        No validation check is required for mapping configuration with remark list
        """

        # check the priority configurations
        for direction in list(self.priority_value_dict.keys()):
            for priority_type in list(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 list(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.items() :
                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.items() :
                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.total_buffer_bytes  = 10000000
            self.cell_bytes          = 208
            self.max_frame_cells     = 45
            self.num_priorities      = 16
            self.num_priority_groups = 8
            self.num_service_pools   = 8
            self.num_mc_queues       = 8

        super(HardwareConfig,self).__init__('Hardware', config_dict)

    def init_config (self, config_mgr) :
        super(HardwareConfig,self).init_config(config_mgr)
        if isinstance(self.config_mgr.chip, (
            cumulus.platform.SpectrumChip,
            cumulus.platform.SpectrumFourChip,
            cumulus.platform.SpectrumFiveChip,
        )):
            from cumulus.mlx import mlx_get_shared_buff_limits
            from cumulus.mlx import mlx_open_connection
            from cumulus.mlx import mlx_close_connection

            mlx_open_connection(logger)
            shared_limits = mlx_get_shared_buff_limits()
            mlx_close_connection()

            self.cell_bytes = shared_limits.get('cell_unit_size', 144)
            self.total_buffer_bytes = shared_limits.get('pool_size', 10000000)
            self.total_buffer_cells = int(self.total_buffer_bytes / self.cell_bytes)

# ==============================================================================
#
#                         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.pfc_watchdog    = PortGroupConfigSet('pfc watchdog', PfcWatchdogPortGroupConfig)
        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.ptp_shaper      = PortGroupConfigSet('ptp_shaper', PtpShaperPortGroupConfig)
        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.mpls_enable  = EnableConfig('mpls_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.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.default_ecn_red_conf = EcnPortGroupConfig('default_ecn_red_conf', 'default_profile')

        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_watchdog'   : self.pfc_watchdog,
                              'pfc'            : self.pfc,
                              'ecn'            : self.ignore_config,
                              'ecn_red'        : self.ecn_red,
                              'shaping'        : self.shaper,
                              'ptp_shaper'     : self.ptp_shaper,
                              'source'         : self.source,
                              'remark'         : self.remark,
                              'forwarding_table' : self.forwarding_table,
                              '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,
                              'mpls_enable' : self.mpls_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,
                              'default_ecn_red_conf': self.default_ecn_red_conf }

        # 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_watchdog,
                              self.pfc,
                              self.ecn_red,
                              self.shaper,
                              self.ptp_shaper,
                              self.scheduling,
                              self.forwarding_table,
                              self.dos_enable,
                              self.cut_through_enable,
                              self.symmetric_hash_enable,
                              self.ecmp_max_paths,
                              self.ecmp_hash_seed,
                              self.resilient_hash_enable,
                              self.mpls_enable,
                              self.resilient_hash_entries_ecmp,
                              self.sflow,
                              self.custom_ecmp_hash_config,
                              self.custom_lag_hash_config,
                              self.ignore_config,
                              self.default_ecn_red_conf]

    # ------------------------------------------------------------------
    #
    #                     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) :
        if not os.path.isfile(config_file):
            return
        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(error_str, file=sys.stderr)
        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

    def calculate_headroom_buff(self):
        headroom_buffer = 0
        # Get iPort.PG[lossy] headroom buffer
        for pg_name, priority_group in self.priority_group.set_dict.items():
            # Do not consider the PG not configured
            if not priority_group.configured:
                continue

            buffer_config = priority_group.set_dict['ingress_buffer']
            pg_hr  = buffer_config.lossy_headroom
            headroom_buffer += pg_hr

        # Get the iPort.PG[mgmt] headroom buffer
        headroom_buffer += self.management.ingress_buffer.lossy_headroom

        # Multiply the lossy headroom buffers by number of ports available on the system
        headroom_buffer *= self.port_desc.port_count

        # Get the iPort.PG[lossless] headroom buffer on all the lossless ports
        port_cnt = 0
        lossless_hr = 0
        for _, pfc_pg in self.pfc.set_dict.items():
            if pfc_pg.port_set == "allports":
                port_cnt = self.port_desc.port_count
                lossless_hr = port_cnt * (pfc_pg.xoff_size + pfc_pg.port_buffer_bytes)
                break
            else:
                port_cnt = len(pfc_pg.linux_port_list)
                lossless_hr += port_cnt * (pfc_pg.xoff_size + pfc_pg.port_buffer_bytes)
        headroom_buffer += lossless_hr

        # Get the iPort.PG[lossless] headroom buffer on all the lossless ports
        port_cnt = 0
        lossless_hr = 0
        for _, lp_pg in self.link_pause.set_dict.items():
            if lp_pg.port_set == "allports":
                port_cnt = self.port_desc.port_count
                lossless_hr = port_cnt * (lp_pg.xoff_size + lp_pg.port_buffer_bytes)
                break
            else:
                port_cnt = len(lp_pg.linux_port_list)
                lossless_hr += port_cnt * (lp_pg.xoff_size + lp_pg.port_buffer_bytes)
        headroom_buffer += lossless_hr

        return headroom_buffer

    def calculate_ingress_reserved(self):
        ingress_buffer = 0

        # Get the iPort.PG[lossy] reserved ingress buffer
        for pg_name, priority_group in self.priority_group.set_dict.items():
            # Do not consider the PG not configured
            if not priority_group.configured:
                continue

            buffer_config = priority_group.set_dict['ingress_buffer']
            pg_reserved  = buffer_config.reserved
            ingress_buffer += pg_reserved

        # Get the iPort.PG[mgmt] reserved ingress buffer
        ingress_buffer += self.management.ingress_buffer.reserved

        # Get the iPort.Pool[0-8] reserved ingress buffer
        for name, buff in self.port.set_dict.items():
            if name.startswith('service_pool_') and 'ingress_buffer' in name:
                ingress_buffer += buff.reserved

        # Get the iPort.Pool[mgmt] reserved ingress buffer
        ing_mgmt_port = self.port.set_dict['management']
        ingress_buffer += getattr(ing_mgmt_port.set_dict['ingress_buffer'], 'reserved', 0)

        # Multiply the ingress_buffer by number of ports available on the system
        ingress_buffer *= self.port_desc.port_count

        return ingress_buffer

    def calculate_egress_reserved(self):
        egress_buffer = 0

        # Get the TCs on which lossless is configured on all the ports
        lossless_sp_list = {}
        for pfc_name, pfc_pg in self.pfc.set_dict.items():
            for sp in pfc_pg.cos_list:
                lossless_sp_list[sp] = True

        if self.link_pause.set_dict:
            for sp in range(8):
                lossless_sp_list[sp] = True

        # Get the ePort.TC[UC-lossy/lossless] reserved egress buffer
        egr_buff_tc_considered = {}
        for sp in range(8):
            tc = getattr(self.cos_queue.cos_id_dict[sp], 'uc', 0)

            # Continue if the tc is already considered in the egress
            # reserved buffer calculation. Multiple SPs mapped to same TC
            if tc in egr_buff_tc_considered:
                continue

            egr_buff_cfg = self.egress_buffer.set_dict[f'egr_queue_{tc}']
            if lossless_sp_list.get(sp):
                egress_buffer += self.flow_control.egress_buffer.reserved
            else:
                egress_buffer += egr_buff_cfg.set_dict['uc'].reserved

            # Add the ePort.TC[mc] reserved egress buffer
            egress_buffer += egr_buff_cfg.set_dict['mc'].reserved

            egr_buff_tc_considered[tc] = True

        # Add ePort.TC[mgmt] reserved egress buffer
        egress_buffer += self.management.egress_buffer.reserved

        # Add ePort.Pool[mgmt] reserved egress buffer
        egr_mgmt_port = self.port.set_dict['management']
        egress_buffer += getattr(egr_mgmt_port.set_dict['egress_buffer'], 'reserved', 0)

        # Add ePort.MC reserved egress buffer
        eport = self.port.set_dict['egress_buffer']
        egress_buffer += eport.set_dict['mc'].reserved

        # Add ePort.Pool[user] reserved egress buffer
        for name, buff in self.port.set_dict.items():
            if name.startswith('service_pool_') and 'egress_buffer' in name:
                egress_buffer += buff.set_dict['uc'].reserved

        # Multiply the egress_buffer by number of ports available on the system
        egress_buffer *= self.port_desc.port_count

        # Add MC.SP[] reserved egress buffer
        for sp in range(8):
            egr_buff_cfg = self.egress_buffer.set_dict[f'cos_{sp}']
            if lossless_sp_list.get(sp):
                egress_buffer += self.flow_control.egress_buffer.reserved
            else:
                egress_buffer += egr_buff_cfg.set_dict['mc'].reserved

        return egress_buffer

    # ------------------------------------------------------------------
    #
    #          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.items() :
            if priority_group.configured == False :
                continue
            pg_id = priority_group.id
            buffer_config = priority_group.set_dict['ingress_buffer']
            pg_reserved  = buffer_config.reserved
            if pg_id not in pg_buffer_limit :
                pg_buffer_limit[pg_id] = {}
            # minimum buffer calculation
            buffer_limit = int(pg_reserved / self.hardware.cell_bytes)
            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_size    = buffer_config.shared_size
            buffer_limit      = int(pg_shared_size / self.hardware.cell_bytes)
            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 list(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_reserved = priority_group.egress_buffer.set_dict[q_type].reserved
                min_buffer_limit = int(min_reserved / self.hardware.cell_bytes)
                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 list(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.items() :
            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.shared_size / self.hardware.cell_bytes)
                    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 list(priority_group.queue[q_type].keys()) :
                    for name in list(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] = {}
        # 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())

    # ----------------------------------------------------------
    #
    #                  c h e c k __ c o n f i g
    #
    # ----------------------------------------------------------
    def check_config (self) :
        # Set the defaults for ePort.TC[mgmt].reserved and iPort.PG[mgmt].reserved
        if not hasattr(self.management.ingress_buffer, 'lossy_headroom'):
            self.management.ingress_buffer.lossy_headroom = 10240

        if not hasattr(self.management.ingress_buffer, 'reserved'):
            self.management.ingress_buffer.reserved = 10240

        if not hasattr(self.management.egress_buffer, 'reserved'):
            self.management.egress_buffer.reserved = 1024

        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

        for egr_queue in self.egress_buffer.set_dict:
            if re.search('cos_', egr_queue) and self.egress_buffer.set_dict[egr_queue].set_dict['uc'].configured:
                self.report_error("Egress Buffer config not supported for UC queue for {}".format(egr_queue))

    # Validate that there are no common port_set between port shaper
    # and ptp_shaper
        port_shaper_set = []
        ptp_shaper_set = []
        if len(self.ptp_shaper.set_dict) > 1:
            self.report_error("PTP shaper can have only one port group {}".format(list(self.ptp_shaper.set_dict.keys())))
        for grp_name in self.shaper.set_dict:
            port_shaper_set.extend(self.shaper.set_dict[grp_name].linux_port_list)
        for grp_name in self.ptp_shaper.set_dict:
            ptp_shaper_set.extend(self.ptp_shaper.set_dict[grp_name].linux_port_list)
        common_shaper_port = list(set(port_shaper_set).intersection(ptp_shaper_set))
        if common_shaper_port:
            self.report_error("Common ports {} found between Port shaper and PTP shaper port set - invalid config".format(common_shaper_port))

	# 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.ingress_service_pool == -1 or self.flow_control.egress_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()

# ==============================================================================
#
#              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("# 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.output_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,
                        qos_infra_config,
                        qos_features_config,
                        forwarding_config,
                        ptp_shaper_config,
                        output_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 qos_infra_config:
            self.config_manager.read_config_file(qos_infra_config)

        if qos_features_config:
            self.config_manager.read_config_file(qos_features_config)

        if forwarding_config:
            self.config_manager.read_forwarding_config_file(forwarding_config)

        if ptp_shaper_config:
            self.config_manager.read_config_file(ptp_shaper_config)

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

        # Validate the Spectrum buffer configurations
        if isinstance(self.config_manager.chip, (
            cumulus.platform.SpectrumChip,
            cumulus.platform.SpectrumFourChip,
            cumulus.platform.SpectrumFiveChip,
        )):
            ing_res = self.config_manager.calculate_ingress_reserved()
            egr_res = self.config_manager.calculate_egress_reserved()
            headroom = self.config_manager.calculate_headroom_buff()

            total_available_mem = self.config_manager.hardware.total_buffer_bytes
            available_buff = total_available_mem - (ing_res + egr_res + headroom)
            logger.debug("Total ingress_reserved bytes {} egress_reserved bytes {} headroom bytes {} total_mem bytes {} available bytes {} available cells {}".format(ing_res, egr_res, headroom, total_available_mem, available_buff, available_buff/self.config_manager.hardware.cell_bytes))

            if (ing_res + egr_res + headroom > total_available_mem):
                self.config_manager.report_error("The shared buffer configuration is invalid. The total reserved buffer %d exceeds total available buffer %d" % (ing_res + egr_res + headroom, total_available_mem))

        if dryrun_flag == True:
            return;

        self.config_manager.process_config()

        self.sdk_parameter_file = sdk_parameter_file
        self.output_register_file  = output_register_file

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


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

    output_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 += " --qos_infra <qos_infra config file>"
    use_msg += " --qos_features <qos_features config file>"
    use_msg += " --ptp_shaper <ptp_shaper 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=","qos_infra=","qos_features=","ptp_shaper="])
    except getopt.GetoptError:
        error_msg = 'input error: s.b. %s' % use_msg
        if dryrun_flag and not quiet_flag:
            print(error_msg, file=sys.stderr)
        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"):
            output_register_file = arg
        elif opt in ("-g", "--parameter"):
            sdk_parameter_file = arg
        elif opt in ("-q", "--quiet"):
            quiet_flag = True
        elif opt in ("--dryrun"):
            dryrun_flag = True
        elif opt in ("--qos_infra"):
            qos_infra_config = arg
        elif opt in ("--qos_features"):
            qos_features_config = arg
        elif opt in ("--ptp_shaper"):
            ptp_shaper_config = arg

    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('qos_infra config is %s' % qos_infra_config)
    logger.debug('qos_features config is %s' % qos_features_config)
    logger.debug('forwarding config is %s' % forwarding_config)
    logger.debug('ptp_shaper config is %s' % ptp_shaper_config)
    if dryrun_flag == False:
        logger.debug('Output register file is %s' % output_register_file)
        logger.debug('SDK parameter file is %s' % sdk_parameter_file)
        logger.debug('Generating %s and appending to %s from %s' % (output_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.SpectrumChip,
            cumulus.platform.SpectrumFourChip,
            cumulus.platform.SpectrumFiveChip,
            cumulus.platform.VXChip,
        ),
    ):
        error_msg = 'Chip %s not supported' % chip.__class__.__name__
        if dryrun_flag and not quiet_flag:
            print(error_msg, file=sys.stderr)
        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,
                            qos_infra_config,
                            qos_features_config,
                            forwarding_config,
                            ptp_shaper_config,
                            output_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(error_msg, file=sys.stderr)

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

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