#!/usr/bin/python
# Copyright 2017, 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

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

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


"""

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

"""

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

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

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

    def get_name(self) :
        return self.name

    def get_value(self) :
        return self.value

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

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

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

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

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

    def get_name(self) :
        return self.name

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def __init__(self, config_manager) :

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

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

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

        self.finish_port_config()

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

    def get_port_count (self) :
        return self.port_count

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

    def get_weighted_port_count (self) :
        return self.weighted_port_count

    def get_port_bw (self) :
        return self.port_bw

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        #self.dump_port_map()

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

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

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

    def generate_output_objects (self) :
        pass

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

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

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

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

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

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

        self.parameter_group_list.append(group)

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

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

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

        lossless_flag = 0
        if (isinstance(self.chip, cumulus.platform.TomahawkThreeChip)):
            hdrm_group.add_parameter(Parameter('mmu_config_override', 0))
        for sdk_port_label in self.config_manager.port_desc.sdk_port_label_list :
            value_dict = {}
            bw = self.config_manager.port_desc.label_2_bw[sdk_port_label]
            pg_id_list = pg_buffer_limit.keys()
            fc_desc = self.config_manager.port_desc.label_2_flow_control[sdk_port_label]
            if fc_desc.type == 'link pause':
                pg_id_list = [fc_desc.pg_id]
            elif fc_desc.type == 'pfc':
                pg_id_list.append(fc_desc.pg_id)

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

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

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

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

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

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

    def generate_sp_mapping (self) :

        num_priority_groups = self.config_manager.hardware.num_priority_groups

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

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

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

    def generate_port_limits (self) :

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

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

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

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

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

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

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

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

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

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

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

    def generate_output_objects (self) :
        self.generate_pg_map()

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

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

    def generate_cos_map (self) :
        pass

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

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

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

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

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

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

        values['dynamic_limit'] = 0

        return values

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

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

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

        return values

    def generate_queue_type_regs(self, q_type, swp_flag) :

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

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

        for q_id in self.config_manager.priority_group.q2sp[q_type] :
            values = self.get_q_config_values (q_type, q_id)
            title_prefix = 'UC'
            q_prefix = ''
            if q_type == 'mc' :
                title_prefix = 'MC'
                q_prefix = 'm'
            elif q_type == 'cpu' :
                title_prefix = 'CPU'
                q_prefix = 'm'
            group = ParameterGroup('%s Queue %d buffers' % (title_prefix, q_id))
            if q_type == 'uc' :
                group.add_parameter(Parameter('buf.queue%d.qgroup_id%s' % (q_id, port_suffix), -1))
                qgroup_dict[q_id] = 1
            if q_type != 'cpu' :
                group.add_parameter(Parameter('buf.%squeue%d.pool' % (q_prefix, q_id), values['spid']))
            if values['limit_enable'] == 1 :
                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), values['shared_limit']))
                group.add_parameter(Parameter('buf.%squeue%d.pool_resume%s' % (q_prefix, q_id, port_suffix), values['shared_limit'] - 100))

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

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

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

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

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

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

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

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

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

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

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

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

    def __init__(self, chip, config_manager) :

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def write_to_file (self, file) :
        pass

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

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

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

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

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

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

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

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

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

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

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

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

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

    def generate_output_objects (self) :
        pass

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

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

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

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

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

        section = 'ports'

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

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

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

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

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

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

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

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

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

        return 0

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

    def check_config(self) :
        pass

    def process_config(self) :
        pass

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

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

    def process_config(self) :
        pass

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

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

    def process_config(self) :
        pass

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

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

    def process_config(self) :
        pass

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

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

    def process_config(self) :
        pass



# ==============================================================================
#
#                             C 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
        super(CosQueueConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        for name in self.config_dict :
            if not hasattr(self, name) :
                setattr(self, name, -1)
        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 = {}
        cos_dict    = {}
        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(config_mgr.hardware.num_priorities) :
            self.cos_id_dict[cos_id] = CosQueueConfig('cos_%d' % cos_id)
            self.set_dict['cos_%d' % cos_id] = self.cos_id_dict[cos_id]
        super(CosQueueConfigSet,self).init_config(config_mgr)

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

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

# ==============================================================================
#
#                       S E R V I C E __ P O O L __ C O N F I G
#
# ==============================================================================
class ServicePoolConfig(Config):
    def __init__ (self, name, direction) :
        config_dict = { 'percent'        : 'float' }
        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 process_config(self) :
        if self.configured == False:
            if hasattr(self, 'percent') :
                self.percent = 0
        super(ServicePoolConfig,self).process_config()

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

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

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

# ==============================================================================
#
#                    I N G R E S S __ B U F F E R __ C O N F I G
#
# ==============================================================================
class IngressBufferConfig(Config):
    def __init__ (self, name) :
        config_dict = { 'min_percent'    : 'float',
                        'shared_percent' : 'float' }
        super(IngressBufferConfig,self).__init__('%s Ingress Buffer' % name, config_dict)

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

# ==============================================================================
#
#                E G R E S S __ Q U E U E __ B U F F E R __ C O N F I G
#
# ==============================================================================
class EgressQueueBufferConfig(Config):
    def __init__ (self, name) :
        config_dict = { 'sp_percent'        : 'float',
                        'min_percent'       : 'float' }
        super(EgressQueueBufferConfig,self).__init__('%s Egress Queue Buffer' % name, config_dict)

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

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

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

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

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

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

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

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        self.configured = True
        if name[0] == 'weight':
            max_value = 127
            int_value = int(value)
            if int_value < 0 or int_value > max_value:
                self.config_mgr.report_error("weight '%d' out of range 0..%d'" % (int_value, max_value),
                                             line,
                                             file_name,
                                             line_num)
        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 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 :
                super(PriorityGroupConfig,self).check_config()
                if not hasattr(self, 'cos_list'):
                    self.configured = False
        else:
            self.configured == False
            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))


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

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

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

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

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

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

    def set_config (self, name, value, line, file_name, line_num) :
        if self.check_duplicate_parameter(name, line, file_name, line_num):
            return -1
        
        priority_group_name = name[0]
        if priority_group_name not in self.set_dict:
            self.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
        for pg_name, priority_group in self.set_dict.iteritems() :
            if priority_group.configured == True:
                configured_count += 1
        if configured_count < 1 :
            self.config_mgr.report_error('%s: no configured priority groups' % 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)

# ==============================================================================
#
#                             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.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_cos_list(self, cos_list):
        if self.config_mgr.dryrun_flag and dryrun_traffic_file_only:
            return

        if len(cos_list) == 0:
            return

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

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

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

                        if linux_port_label == end_linux_port :
                            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 = {'cos_list'            : 'int list',
                       'ecn_enable'          : 'bool',
                       'red_enable'          : 'bool',
                       'min_threshold_bytes' : 'int',
                       'max_threshold_bytes' : 'int',
                       'probability'         : 'int' }
        super(EcnPortGroupConfig,self).__init__('ECN Port Group ' + name,
                                                type_label,
                                                config_dict)

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

        if set_flag:
            super(EcnPortGroupConfig,self).set_config(name, value, line, file_name, line_num)

    def check_config (self) :
        if not hasattr(self, 'ecn_enable') :
            self.ecn_enable = True
            logger.debug('%s: ecn_enable defaulting to true' % self.name)
        if not hasattr(self, 'red_enable') :
            self.red_enable = False
            logger.debug('%s: red_enable defaulting to false' % self.name)
        if not hasattr(self, 'min_threshold_bytes') :
            logger.debug('%s: min_threshold_bytes defaulting to 40000' % self.name)
            self.min_threshold_bytes = 40000
        if not hasattr(self, 'max_threshold_bytes') :
            logger.debug('%s: max_threshold_bytes defaulting to 200000' % self.name)
            self.max_threshold_bytes = 200000
        if not hasattr(self, 'probability') :
            logger.debug('%s: probability value defaulting to 100' % self.name)
            self.probability = 100
        super(EcnPortGroupConfig,self).check_config()
        if not hasattr(self, '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)

    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',
                       '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) :
        super(FcPortGroupConfig,self).check_config()
        if not hasattr(self, 'rx_enable') :
            logger.debug('%s: rx_enable defaulting to true' % self.name)
            self.rx_enable = True
        if not hasattr(self, 'tx_enable') :
            logger.debug('%s: tx_enable defaulting to true' % self.name)
            self.tx_enable = True
        if not hasattr(self, 'xoff_size') :
            logger.debug('%s: xoff_size defaulting to 10000' % self.name)
            self.xoff_size = 10000
        if not hasattr(self, 'xon_delta') :
            logger.debug('%s: xoff_delta defaulting to 2000' % self.name)
            self.xon_delta = 2000
        if not hasattr(self, 'port_buffer_bytes') :
            logger.debug('%s: port_buffer_bytes defaulting to 25000' % self.name)
            self.port_buffer_bytes = 25000

        if (self.port_buffer_bytes < self.xoff_size):
            self.config_mgr.report_error("port_buffer_bytes %d is less than the xoff_size %d" % (self.port_buffer_bytes,
                                                                                                 self.xoff_size),
                                         '',
                                         self.file_name['port_buffer_bytes'],
                                         self.line_num['port_buffer_bytes'])
            self.xoff_size = self.port_buffer_bytes
        pg_hdrm_cell_limit = int((self.port_buffer_bytes - self.xoff_size)  / self.config_mgr.hardware.cell_bytes)
        max_pg_hdrm_cell_limit = 200
        if pg_hdrm_cell_limit < 4:
            pg_hdrm_cell_limit = 4
        elif pg_hdrm_cell_limit > max_pg_hdrm_cell_limit:
            info_msg = 'PG headroom allocation %d is greater than the maximum value %d:' % (pg_hdrm_cell_limit, max_pg_hdrm_cell_limit)
            info_msg += ' reverting to max value %d' % max_pg_hdrm_cell_limit
            info_msg += ' %s'  % self.file_name['xoff_size']
            info_msg += ':%d'  % self.line_num['xoff_size']
            self.config_mgr.report_info(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 is less than zero" % size,
                                             line,
                                             file_name,
                                             line_num)
        if name[0] == 'tx_enable' or name[0] =='rx_enable':
            flag = value.lower()
            if flag != 'true' and flag != 'false':
                set_flag = False
                self.config_mgr.report_error("'%s' should be 'true' or 'false'" % value,
                                             line,
                                             file_name,
                                             line_num)

        if set_flag:
            super(FcPortGroupConfig,self).set_config(name, value, line, file_name, line_num)

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

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

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

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

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

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

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

    def check_config (self) :
        if not self.configured_flag:
            return
        super(SourcePortGroupConfig,self).check_config()
        # check the priority configurations
        for priority_type in self.priority_value_dict.keys():
            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 != '8021p':
                        bad_type_list.append(remark_type)
                        self.config_mgr.report_error("remark type '%s' is not supported" % remark_type,
                                                     line,
                                                     file_name,
                                                     line_num)
                for remark_type in bad_type_list:
                    self.packet_priority_remark_set.remove(remark_type)
            else:
                self.config_mgr.report_error("parameter '%s' is not supported" % name[0],
                                             line,
                                             file_name,
                                             line_num)


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

    def process_config(self) :
        pass

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

    def check_config (self) :
        if not self.is_configured:
            return
        #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
        for pg_name, priority_group in self.config_mgr.priority_group.set_dict.iteritems():
            if not hasattr(priority_group, 'cos_list'):
                continue
            for cos_id in priority_group.cos_list:
                if not self.pkt_value_dict.get(cos_id, None):
                    self.config_mgr.report_error("%s: %s missing mapping for cos ID '%d'" % (self.name,
                                                                                             self.priority_type,
                                                                                             cos_id))

# ==============================================================================
#
#           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):
        for value in range(0,self.max_value+1):
            if not self.pkt_value_dict.get(value, None):
                self.config_mgr.report_error("%s: missing mapping for priority value '%d'" % (self.name, value))

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

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

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

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

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

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

    def check_config (self) :
        if not hasattr(self, 'drop_behavior') :
            self.drop_behavior = 'color-blind'
        if not hasattr(self, 'yellow_packet_priorities') :
            self.yellow_packet_priorities = None
        if not hasattr(self, 'red_packet_priorities') :
            self.red_packet_priorities = None
        if not hasattr(self, 'yellow_limit_percent') :
            self.yellow_limit_percent = None
        if not hasattr(self, 'red_limit_percent') :
            self.red_limit_percent = None
        if not hasattr(self, 'disable_custom_datapath_config') :
            self.disable_custom_datapath_config = False
        super(TrafficConfigSet,self).check_config()
        if not hasattr(self, 'priority_group_list'):
            self.priority_group_list = []
        # check for a priority config object
        direction = 'source'
        for priority_type in self.packet_priority_source_list:
            priority_label = priority_type
            if priority_type ==  '802.1p':
                priority_label = '8021p'
            if not self.priority_value_dict.get(direction, None) or not self.priority_value_dict[direction].get(priority_label, None):
                self.config_mgr.report_error("%s: source type '%s' is listed but the mapping is not configured" % (self.name,
                                                                                                                   priority_type))
        direction = 'remark'
        for priority_type in self.packet_priority_remark_list:
            priority_label = priority_type
            if priority_type ==  '802.1p':
                priority_label = '8021p'
            if not self.priority_value_dict.get(direction, None) or not self.priority_value_dict[direction].get(priority_label, None):
                self.config_mgr.report_error("%s: remark type '%s' is listed but the mapping is not configured" % (self.name,
                                                                                                                   priority_type))

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

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

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

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

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


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

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

    def init_config(self, config_mgr) :
        pass

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

    def check_config(self) :
        pass

    def process_config(self) :
        pass

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

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

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

        super(ConfigManager,self).__init__()

        self.dryrun_flag     = dryrun_flag
        self.quiet_flag      = quiet_flag
        self.platform        = platform
        self.chip            = platform.switch.chip
        self.hardware        = HardwareConfig(dryrun_flag)
        self.traffic         = TrafficConfigSet()
        self.priority_group  = PriorityGroupConfigSet()
        self.ingress_service_pool = ServicePoolConfigSet('Ingress')
        self.egress_service_pool  = ServicePoolConfigSet('Egress')
        self.cos_queue       = CosQueueConfigSet()
        self.source          = PortGroupConfigSet('source', SourcePortGroupConfig)
        self.remark          = PortGroupConfigSet('remark', RemarkPortGroupConfig)
        self.scheduling      = SchedulingConfig()
        self.link_pause      = PortGroupConfigSet('link pause', FcPortGroupConfig)
        self.pfc             = PortGroupConfigSet('pfc', PfcPortGroupConfig)
        self.ecn_red         = PortGroupConfigSet('ecn_red', EcnPortGroupConfig)
        self.port_desc       = PortDesc(self)
        self.buffer_desc     = BufferDesc()
        self.ignore_config   = IgnoreConfig()
        self.forwarding_table = FwdTableConfig()
        self.dos_enable         = EnableConfig('dos_enable')
        self.cut_through_enable = EnableConfig('cut_through_enable')
        self.symmetric_hash_enable  = EnableConfig('symmetric_hash_enable')
        self.resilient_hash_enable  = EnableConfig('resilient_hash_enable')
        self.resilient_hash_entries_ecmp = ResilientHashEntriesEcmpConfig()
        self.ecmp_max_paths         = EcmpMaxPathsConfig()
        self.ecmp_hash_seed         = EcmpHashSeedConfig()
        self.sflow                  = SflowConfig('sflow')
        self.vxlan_routing_overlay  = RiotConfig()
        self.forwarding_section = []
        self.error_comment   = ""
        self.num_cpu_queues  = 48
        self.exception_q_start  = 8
        self.exception_q_weight = 8
        self.input_file_error   = False

        self.manager_dict = { 'hardware'       : self.hardware,
                              'traffic'        : self.traffic,
                              'priority_group' : self.priority_group,
                              'ingress_service_pool' : self.ingress_service_pool,
                              'egress_service_pool'  : self.egress_service_pool,
                              'cos_egr_queue'  : self.cos_queue,
                              'scheduling'     : self.scheduling,
                              'link_pause'     : self.link_pause,
                              'pfc'            : self.pfc,
                              'ecn'            : self.ignore_config,
                              'ecn_red'        : self.ecn_red,
                              'source'         : self.source,
                              'remark'         : self.remark,
                              'forwarding_table' : self.forwarding_table,
                              'vxlan_routing_overlay' : self.vxlan_routing_overlay,
                              'dos'            : self.ignore_config,
                              'dos_enable'     : self.dos_enable,
                              'cut_through_enable' : self.cut_through_enable,
                              'symmetric_hash_enable' :self.symmetric_hash_enable, 
                              'ecmp_max_paths' : self.ecmp_max_paths,
                              'ecmp_hash_seed' : self.ecmp_hash_seed,
                              'resilient_hash_enable' : self.resilient_hash_enable,
                              'resilient_hash_entries_ecmp' : self.resilient_hash_entries_ecmp,
                              'sflow'          : self.sflow }

        # for when order matters
        self.manager_list = [ self.hardware,
                              self.traffic,
                              self.priority_group,
                              self.ingress_service_pool,
                              self.egress_service_pool,
                              self.source,
                              self.remark,
                              self.cos_queue,
                              self.link_pause,
                              self.pfc,
                              self.ecn_red,
                              self.scheduling,
                              self.forwarding_table,
                              self.vxlan_routing_overlay,
                              self.dos_enable,
                              self.cut_through_enable,
                              self.symmetric_hash_enable,
                              self.ecmp_max_paths,
                              self.ecmp_hash_seed,
                              self.resilient_hash_enable,
                              self.resilient_hash_entries_ecmp,
                              self.sflow,
                              self.ignore_config]

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

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

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

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

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

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

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

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

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

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

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

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

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

        cpu_pg_min_cells    = 45
        wan_pg_min_cells    = 1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.buffer_comment += '\n'

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

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

        num_mc_queues = self.hardware.num_mc_queues

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

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

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

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

    # ----------------------------------------------------------
    #
    #                  c h e c k __ c o n f i g
    #
    # ----------------------------------------------------------
    def check_config (self) :
        for manager in self.manager_list:
            manager.check_config()

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

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

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

    def generate_output_objects(self) :
        pass

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

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

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

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

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

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

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

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

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

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

        if hw_desc:
            self.config_manager.read_config_file(hw_desc)

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

        if datapath_config:
            self.config_manager.read_config_file(datapath_config)

        if forwarding_config:
            self.config_manager.read_forwarding_config_file(forwarding_config)

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

        if dryrun_flag == True:
            return;

        self.config_manager.process_config()

        self.sdk_parameter_file = sdk_parameter_file
        self.bcm_register_file  = bcm_register_file

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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