#!/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)

# ==============================================================================
#
#                           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)):
                    # 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:
                    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
        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))

                # 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

        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")
        for sdk_port_label in self.config_manager.port_desc.sdk_port_label_list :
            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
            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

            # port label syntax is different for csv syntax
            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)

# ==============================================================================
#
#                    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,
            'MaverickChip'              : MaverickChipManager }

    @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'
        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'

        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)):
            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

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

    # ------------------------------------------------------------------
    #
    #                        s e t __ c o n f i g
    #
    # ------------------------------------------------------------------
    def set_config (self, name, value) :
        if type(name) == list :
            if len(name) == 0:
                self.config_mgr.report_error( '%s set_config: no name' % self.name)
                return
            name = name[0]
        elif type(name) != str :
            self.config_mgr.report_error('%s set_config: name format %s not recognized' % (self.name, name))
        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' :
                if value == 'true' or value == 'True' or value == '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('Config %s value %s not recognized as an integer list' % (name, value))
                    return
            elif value_type == 'string' :
                typed_value = value
            else :
                self.config_mgr.report_error('Config value type %s not supported' % value_type)
                return
            setattr(self, name, typed_value)
        else :
            self.config_mgr.report_error('%s not supported in %s' % (name, self.name))

    # ------------------------------------------------------------------
    #
    #                        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: 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 = ''
                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) :
        if type(name) == list :
            num_levels = len(name)
            if num_levels == 0:
                self.config_mgr.report_error('%s set_config: no name' % self.name)
                return
            parameter_name = name[0]
            if parameter_name in self.config_dict :
                super(ConfigSet, self).set_config(parameter_name, value)
            elif parameter_name in self.set_dict :
                set_info = self.set_dict[parameter_name]
                set_info.set_config(name[1:], value)
            else :
                self.config_mgr.report_error('%s: parameter name %s in %s not recognized' % (self.name, parameter_name, name))
                return
        else :
            self.config_mgr.report_error('%s: name format %s not recognized' % (self.name, name))
            return

    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' }
        super(SchedulingConfig,self).__init__('Scheduling', config_dict)

# ==============================================================================
#
#                       F W D __ T A B L E __ C O N F I G
#
# ==============================================================================
class FwdTableConfig(Config):
    def __init__ (self) :
        self.profile = 'default'
        config_dict = { 'profile' : 'string' }
        super(FwdTableConfig,self).__init__('forwarding_table', config_dict)

class RiotConfig(Config):
    def __init__ (self) :
        self.profile = 'disable'
        config_dict = { 'profile' : 'string' }
        super(RiotConfig,self).__init__('vxlan_routing_overlay', config_dict)

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

    def check_config (self) :
        for name in self.config_dict :
            if not hasattr(self, name) :
                setattr(self, name, 0)
        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.configured    = False
        self.unlimited     = False
        self.cos_list      = []
        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) :
        if name[0] == 'cos_list' :
            self.configured = True
        super(PriorityGroupConfig,self).set_config(name, value)

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

    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) :
        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)

    def check_config (self) :
        super(PriorityGroupConfigSet,self).check_config()
        configured_count = 0
        for type, 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('Error: traffic group %s assigned Cos %d (already assigned)')
                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) :
        if name[0] == 'priority_remark':
            return
        if name[0] == 'priority_source' or name[0] == "packet_priorities":
            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:
                priority_value_list = value.strip('[')
                priority_value_list = priority_value_list.strip(']')
                priority_value_list = priority_value_list.split(',')
                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('%s: packet priority type %s not recognized' % (self.name, name[1]))
                return

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

    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 __ S E T
#
# ==============================================================================
class PortGroupConfig(Config):
    def __init__ (self, name, type_label) :
        config_dict = {'rx_enable'          : 'bool',
                       'tx_enable'          : 'bool',
                       'port_set'           : 'string',
                       'xoff_size'          : 'int',
                       'xon_delta'          : 'int',
                       'port_buffer_bytes'  : 'int',
                       'cos_list'           : 'int list' }
        self.sdk_port_list = []
        self.cos_list      = []
        self.type_label    = type_label
        super(PortGroupConfig,self).__init__('Port Group ' + name,
                                             config_dict)
    def check_config (self) :
        if not hasattr(self, 'rx_enable') :
            self.rx_enable = True
        if not hasattr(self, 'tx_enable') :
            self.tx_enable = True
        if not hasattr(self, 'xoff_size') :
            self.xoff_size = 10000
        if not hasattr(self, 'xon_delta') :
            self.xon_delta = 2000
        if not hasattr(self, 'port_buffer_bytes') :
            self.port_buffer_bytes = 25000

        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:
            error_msg = '\nPG headroom allocation %d is greater than the maximum value:' % pg_hdrm
            error_msg += ' reverting to max value %d' % max_pg_hdrm_cell_limit
            self.report_error(error_msg)
            self.pg_hdrm = max_pg_hdrm_cell_limit

        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

        super(PortGroupConfig,self).check_config()

    def set_config (self, name, value) :
        super(PortGroupConfig,self).set_config(name, value)
        if name[0] == 'port_set' :
            # parse the port set string
            port_group_list = value.split(',')
            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)
                        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)
                            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

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

    def check_config (self) :
        if not hasattr(self, 'port_group_list') :
            self.port_group_list = '[]'
        super(PortGroupConfigSet,self).check_config()

    def set_config (self, name, value) :
        if name[0] not in self.config_dict :
            port_group_name = name[0]
            if port_group_name not in self.set_dict :
                self.set_dict[port_group_name] = PortGroupConfig(port_group_name, self.fc_label)
                self.set_dict[port_group_name].init_config(self.config_mgr)
        super(PortGroupConfigSet,self).set_config(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',
                       'disable_custom_datapath_config' : 'bool'}
        cos_dict = {}
        self.cos_id_dict = {}
        super(TrafficConfigSet,self).__init__('Traffic Set', config_dict, cos_dict)

    def init_config (self, config_mgr) :
        self.packet_priority_source_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):
        if name[0] == 'packet_priority_remark_set' :
            return
        if name[0] == 'remark_packet_priority' :
            return
        if name[0] == 'packet_priority_remark' :
            return
        if 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:
                self.packet_priority_source_list.append(priority)
        elif name[0] == 'packet_priority_source' :
            self.packet_priority_source_list.append(value)
        else:
            super(TrafficConfigSet,self).set_config(name, value)

    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()

    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) :
        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' }
        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) :
        pass

    def check_config(self) :
        pass

    def process_config(self) :
        pass

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

    def __init__(self, platform) :

        super(ConfigManager,self).__init__()

        self.platform        = platform
        self.chip            = platform.switch.chip
        self.hardware        = HardwareConfig()
        self.traffic         = TrafficConfigSet()
        self.priority_group  = PriorityGroupConfigSet()
        self.ingress_service_pool = ServicePoolConfigSet('Ingress')
        self.egress_service_pool  = ServicePoolConfigSet('Egress')
        self.cos_queue       = CosQueueConfigSet()
        self.scheduling      = SchedulingConfig()
        self.link_pause      = PortGroupConfigSet('link pause')
        self.pfc             = PortGroupConfigSet('pfc')
        self.port_desc       = PortDesc(self)
        self.buffer_desc     = BufferDesc()
        self.ignore_config   = IgnoreConfig()
        self.forwarding_table = FwdTableConfig()
        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.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.ignore_config,
                              'remark'         : self.ignore_config,
                              'source'         : self.ignore_config,
                              'forwarding_table' : self.forwarding_table,
                              'vxlan_routing_overlay' : self.vxlan_routing_overlay,
                              'dos'            : self.ignore_config,
                              'dos_enable'     : self.ignore_config,
                              'cut_through_enable' : self.ignore_config,
                              'ecmp_max_paths' : self.ignore_config,
                              'symmetric_hash_enable' : self.ignore_config,
                              'resilient_hash_enable' : self.ignore_config,
                              'resilient_hash_entries_ecmp' : self.ignore_config}

        # for when order matters
        self.manager_list = [ self.hardware,
                              self.traffic,
                              self.priority_group,
                              self.ingress_service_pool,
                              self.egress_service_pool,
                              self.cos_queue,
                              self.link_pause,
                              self.pfc,
                              self.scheduling,
                              self.forwarding_table,
                              self.vxlan_routing_overlay,
                              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)
        for line in f:
            line = line.rstrip()
            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('configuration line not recognized: %s' % line)
                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('configuration parameter %s not recognized' % name)
                continue
            manager_name = name_level[0]
            if manager_name not in self.manager_dict :
                self.report_error('configuration parameter level %s in %s not supported' % (manager_name, name))
                continue
            manager = self.manager_dict[manager_name]
            manager.set_config(name_level[1:], value)
        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) :
        logger.error(error_msg)
        self.error_comment += '# %s\n' % error_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) :

        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)

        self.config_manager.read_config_file(datapath_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')

        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) :

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

    try:
        opts, args = getopt.getopt(argv,
                                   "hc:p:m:l:t:d:f:r:g:",
                                   ["hw=","traffic=","datapath=","forwarding=","register=","parameter="])
    except getopt.GetoptError:
        logger.error('input error: s.b. %s' % use_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

    logger.debug('Generating a register settings file: ')
    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)
    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)):
            logger.error('Chip %s not supported' % chip.__class__.__name__)
            return

    config_manager = ConfigManager(platform)
    chip_manager   = ChipManagerFactory.get_new_manager(chip, config_manager)
    if chip_manager == None :
        logger.error('Chip %s not supported' % chip.__class__.__name__)
    creator = DatapathCreator(chip, chip_manager, config_manager)
    creator.create_datapath(hw_desc,
                            traffic_config,
                            datapath_config,
                            forwarding_config,
                            bcm_register_file,
                            sdk_parameter_file)
    #creator.review_datapath()

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

if __name__ == "__main__":
    main(sys.argv[1:])

