#!/usr/bin/python
#   cl-resource-est-tool
#   Version     :   1.0
#   Author      :   Amit Kr. Pal, amit@cumulusnetworks.com
#   Description :   This tool provides functionality to query and estimate TCAM/ACL resource usage on HW
#   
#   Copyright 2018 Cumulus Networks, Inc.

try:
    import sys, getopt
    import os
    import json
except ImportError, e:
	raise ImportError (str(e) + "- required module not found")

#JSON mapping of chip number and name
chip_map_json = {
    "BCM56850_A2":  { 
        "chip_name": "trident2" 
    },
    "BCM56854_A2":  { 
        "chip_name": "trident2" 
    },
    "BCM56860_A1": {
        "chip_name": "trident2plus"
    },
    "BCM56864_A1": {
        "chip_name": "trident2plus"
    },
    "BCM56846_A1":  { 
        "chip_name": "tridentplus" 
    },
    "BCM56845_A4":  { 
        "chip_name": "trident" 
    },
    "BCM56870_A0":  { 
        "chip_name": "trident3"
    },
    "BCM56873_A0":  { 
        "chip_name": "trident3"
    },
    "BCM56644":     { 
        "chip_name": "triumph3" 
    },
    "BCM56634_B0":     { 
        "chip_name": "triumph2" 
    },
    "BCM56540_B0":  { 
        "chip_name": "apollo2" 
    },
    "BCM56538_B0":  { 
        "chip_name": "firebolt3" 
    },
    "BCM56340_A0":  { 
        "chip_name": "helix4" 
    },
    "BCM56634_B0":  { 
        "chip_name": "triumph2" 
    },
    "BCM56150_A0":  { 
        "chip_name": "hurricane2" 
    },
    "BCM56960_A0": {
        "chip_name": "tomahawk"
    },
    "BCM56960_B0": {
        "chip_name": "tomahawk"
    },
    "BCM56960_B1": {
        "chip_name": "tomahawk"
    },
    "BCM56962_B1": {
        "chip_name": "tomahawk"
    },
    "BCM56963_B0": {
        "chip_name": "tomahawk"
    },
    "BCM56963_B1": {
        "chip_name": "tomahawk"
    },
    "BCM56967_A1": {
        "chip_name": "tomahawkplus"
    },
    "BCM56762_B0": {
        "chip_name": "maverick"
    },
    "BCM56768_B0": {
        "chip_name": "maverick"
    },
    "Spectrum": {
        "chip_name": "spectrum"
    },
    "Spectrum_A1": {
        "chip_name": "spectrum_a1"
    },
    "Spectrum2": {
        "chip_name": "spectrum2"
    },
    "Spectrum3": {
        "chip_name": "spectrum3"
    },
}

#Broadcom CHIP Base class
class BCM:

    chip_type       = "NULL"
    non_atomic_mode =   False
    
    total_ingress_slices    =   0
    num_256_in_slices       =   0
    num_512_in_slices       =   0
    num_768_in_slices       =   0
        
    num_256_eg_slices       =   0
    num_512_eg_slices       =   0
    num_768_eg_slices       =   0
    total_egress_slices     =   0

    total_entries           =   0
    default_in_v4mac_filter     = [0,0,0]
    default_in_v6_filter        = [0,0,0]
    default_in_mirror_filter    = [0,0,0]
    default_in_8021x_filter     = [0,0,0]
    default_in_v4mac_mangle     = [0,0,0]
    default_in_v6_mangle        = [0,0,0]
    default_eg_v4mac_filter     = [0,0,0]
    default_eg_v6_filter        = [0,0,0]
    default_empty               = []

    acl_def_table               = {}
    acl_curr_table              = {}

    tmp_in_v4mac_filter     = [0,0,0]
    tmp_in_v6_filter        = [0,0,0]
    tmp_in_mirror_filter    = [0,0,0]
    tmp_in_8021x_filter     = [0,0,0]
    tmp_in_v4mac_mangle     = [0,0,0]
    tmp_in_v6_mangle        = [0,0,0]
    tmp_eg_v4mac_filter     = [0,0,0]
    tmp_eg_v6_filter        = [0,0,0]
    tmp_empty               = []

    #Constructor for BCM
    def __init__(self, chip_name, non_atomic_mode = False):
        #read from file for chip type
        if chip_name:
            self.chip_type          = chip_name
            self.non_atomic_mode    = non_atomic_mode
            self.used_entries       =   {}
            self.total_entries      =   {}
            self.max_entries        =   {}

    #Function to update slice distribution on the chip
    def update_slices(self, direction, num_256 = 0, num_512 = 0, num_768 = 0):
        if direction == "ingress":
            self.num_256_in_slices      =   num_256
            self.num_512_in_slices      =   num_512
            self.num_768_in_slices      =   num_768
            self.total_ingress_slices   =   num_256 + num_512 + num_768
        else:
            self.num_256_eg_slices      =   num_256
            self.num_512_eg_slices      =   num_512
            self.num_768_eg_slices      =   num_768
            self.total_egress_slices    =   num_256 + num_512 + num_768


    #Function to get total number of slices on chip for given direction
    def get_total_slices(self, direction):
        if direction == "ingress":
            return  self.total_ingress_slices
        else:
            return self.total_egress_slices 

    #Function to get number of slices across various sizes
    def get_slices(self, direction = "ingress"):
        if direction == "ingress":
            num_256 = self.num_256_in_slices
            num_512 = self.num_512_in_slices
            num_768 = self.num_768_in_slices
        else:
            num_256 = self.num_256_eg_slices
            num_512 = self.num_512_eg_slices
            num_768 = self.num_768_eg_slices

        return num_256, num_512, num_768

    #Function to the chip type
    def get_chip_type(self, log = False):
        if log == True:
            print "Chip Type : ", self.chip_type

    #Function to get the atomic mode
    def get_atomic_mode(self, log = False):
        if self.non_atomic_mode == False:
            if log == True:
                print "Atomic Mode Enabled"
        else:
            if log == True:
                print "Atomic Mode Disabled (Non Atomic Mode)"

    #Function to update the default tables
    def update_default_tables(self):
        self.acl_def_table['v4mac'+'filter'+'ingress']  = self.default_in_v4mac_filter
        self.acl_def_table['v6'+'filter'+'ingress']     = self.default_in_v6_filter
        self.acl_def_table['mirror'+'filter'+'ingress'] = self.default_in_mirror_filter
        self.acl_def_table['8021x'+'filter'+'ingress']  = self.default_in_8021x_filter
        self.acl_def_table['v4mac'+'mangle'+'ingress']  = self.default_in_v4mac_mangle
        self.acl_def_table['v6'+'mangle'+'ingress']     = self.default_in_v6_mangle
        self.acl_def_table['mirror'+'mangle'+'ingress'] = self.default_empty
        self.acl_def_table['8021x'+'mangle'+'ingress']  = self.default_empty
        self.acl_def_table['v4mac'+'filter'+'egress']   = self.default_eg_v4mac_filter
        self.acl_def_table['v6'+'filter'+'egress']      = self.default_eg_v6_filter
        self.acl_def_table['mirror'+'filter'+'egress']  = self.default_empty
        self.acl_def_table['8021x'+'filter'+'egress']   = self.default_empty
        self.acl_def_table['v4mac'+'mangle'+'egress']   = self.default_empty
        self.acl_def_table['v6'+'mangle'+'egress']      = self.default_empty
        self.acl_def_table['mirror'+'mangle'+'egress']  = self.default_empty
        self.acl_def_table['8021x'+'mangle'+'egress']   = self.default_empty

    def get_default_table_entry(self, direction, tbl_type, tbl_id):
        return self.acl_def_table[tbl_type+tbl_id+direction]

    def update_current_tables(self):
        self.acl_curr_table['v4mac'+'filter'+'ingress'] = self.tmp_in_v4mac_filter
        self.acl_curr_table['v6'+'filter'+'ingress']    = self.tmp_in_v6_filter
        self.acl_curr_table['mirror'+'filter'+'ingress'] = self.tmp_in_mirror_filter
        self.acl_curr_table['8021x'+'filter'+'ingress']     = self.tmp_in_8021x_filter
        self.acl_curr_table['v4mac'+'mangle'+'ingress']     = self.tmp_in_v4mac_mangle
        self.acl_curr_table['v6'+'mangle'+'ingress']    = self.tmp_in_v6_mangle
        self.acl_curr_table['mirror'+'mangle'+'ingress'] = self.tmp_empty
        self.acl_curr_table['8021x'+'mangle'+'ingress']     = self.tmp_empty
        self.acl_curr_table['v4mac'+'filter'+'egress']  = self.tmp_eg_v4mac_filter
        self.acl_curr_table['v6'+'filter'+'egress']         = self.tmp_eg_v6_filter
        self.acl_curr_table['mirror'+'filter'+'egress']     = self.tmp_empty
        self.acl_curr_table['8021x'+'filter'+'egress']  = self.tmp_empty
        self.acl_curr_table['v4mac'+'mangle'+'egress']  = self.tmp_empty
        self.acl_curr_table['v6'+'mangle'+'egress']         = self.tmp_empty
        self.acl_curr_table['mirror'+'mangle'+'egress']     = self.tmp_empty
        self.acl_curr_table['8021x'+'mangle'+'egress']  = self.tmp_empty

    def get_current_table_entry(self, direction, tbl_type, tbl_id):
        return self.acl_curr_table[tbl_type+tbl_id+direction]

    def get_num_free_slices_entries_available(self, direction):

        num_total_slices = 0
        num_total_256_slices = 0
        num_total_512_slices = 0
        num_total_768_slices = 0

        num_256_slices, num_512_slices, num_768_slices = self.get_slices(direction)
        acl_table_ids = get_table_ids()

        jout = get_cl_res_json_data()
        for curr_table_id in acl_table_ids:

            curr_table_types = get_table_type(curr_table_id,direction)
            for table_type in curr_table_types:

                acl_table_name = get_table_name(table_type,curr_table_id,direction)

                if acl_table_name != "":
                    entry = jout.get(acl_table_name)

                    rqj_allocated = int(entry.get("allocated"))
                    num_total, num_256, num_512, num_768, num_entries = self.get_num_slices_entries_reqd_for_entries(rqj_allocated, direction, table_type, curr_table_id)
                    num_total_slices += num_total
                    num_total_256_slices += num_256
                    num_total_512_slices += num_512
                    num_total_768_slices += num_768

    
        free_256_slices = num_256_slices - num_total_256_slices
        free_512_slices = num_512_slices - num_total_512_slices
        free_768_slices = num_768_slices - num_total_768_slices
        total_free_slices = free_256_slices + free_512_slices + free_768_slices

        slice_256_factor = self.get_256_slice_factor(direction)
        total_free_entries = (256 * (free_256_slices/slice_256_factor)) + (512 * (free_512_slices/2)) + (768 * (free_768_slices/3))
        return total_free_slices, free_256_slices, free_512_slices, free_768_slices, total_free_entries

    def get_curr_num_free_slices_entries_available(self, direction, log = False):

        num_total_slices = 0
        num_total_256_slices = 0
        num_total_512_slices = 0
        num_total_768_slices = 0

        num_256_slices, num_512_slices, num_768_slices = self.get_slices(direction)
        acl_table_ids = get_table_ids()

        for curr_table_id in acl_table_ids:

            curr_table_types = get_table_type(curr_table_id,direction)
            for table_type in curr_table_types:

                acl_table_name = get_table_name(table_type,curr_table_id,direction)
                curr_tbl_entry = self.get_current_table_entry(direction, table_type, curr_table_id)

                if curr_tbl_entry:
                    rqj_allocated = curr_tbl_entry[2]
                    num_total, num_256, num_512, num_768, num_entries = self.get_num_slices_entries_reqd_for_entries(rqj_allocated, direction, table_type, curr_table_id, log)
                    num_total_slices += num_total
                    num_total_256_slices += num_256
                    num_total_512_slices += num_512
                    num_total_768_slices += num_768

    
        free_256_slices = num_256_slices - num_total_256_slices
        free_512_slices = num_512_slices - num_total_512_slices
        free_768_slices = num_768_slices - num_total_768_slices
        total_free_slices = free_256_slices + free_512_slices + free_768_slices

        slice_256_factor = self.get_256_slice_factor(direction)
        total_free_entries = (256 * (free_256_slices/slice_256_factor)) + (512 * (free_512_slices/2)) + (768 * (free_768_slices/3))
        return total_free_slices, free_256_slices, free_512_slices, free_768_slices, total_free_entries

    def update_current_entries_table(self, need_0_default_rules = False):

        jout = get_cl_res_json_data()
        for direction in get_table_directions():
            for tbl_id in get_table_ids():
                for tbl_type in get_table_type(tbl_id, direction):
                    acl_tbl_name = get_table_name(tbl_type, tbl_id, direction)

                    if acl_tbl_name != "":
                        entry = jout.get(acl_tbl_name)

                        rqj_count = 0
                        rqj_max = 0
                        rqj_allocated = 0

                        if need_0_default_rules == False:
                            rqj_count       = int(entry.get("count"))
                            rqj_max         = int(entry.get("max"))
                            rqj_allocated   = int(entry.get("allocated"))

                        self.update_current_table_entry(direction, tbl_type, tbl_id, rqj_count, rqj_max, rqj_allocated)

        self.update_current_tables()

    def update_current_table_entry(self, direction, tbl_type, tbl_id, num_count, num_max, num_alloced):
        if direction == "ingress" and tbl_type == "v4mac" and tbl_id == "filter":
            self.tmp_in_v4mac_filter[0]     = num_count
            self.tmp_in_v4mac_filter[1]     = num_max
            self.tmp_in_v4mac_filter[2]     = num_alloced

        if direction == "ingress" and tbl_type == "v6" and tbl_id == "filter":
            self.tmp_in_v6_filter[0]    = num_count
            self.tmp_in_v6_filter[1]    = num_max
            self.tmp_in_v6_filter[2]    = num_alloced

        if direction == "ingress" and tbl_type == "mirror" and tbl_id == "filter":
            self.tmp_in_mirror_filter[0]    = num_count
            self.tmp_in_mirror_filter[1]    = num_max
            self.tmp_in_mirror_filter[2]    = num_alloced

        if direction == "ingress" and tbl_type == "8021x" and tbl_id == "filter":
            self.tmp_in_8021x_filter[0]     = num_count
            self.tmp_in_8021x_filter[1]     = num_max
            self.tmp_in_8021x_filter[2]     = num_alloced

        if direction == "ingress" and tbl_type == "v4mac" and tbl_id == "mangle":
            self.tmp_in_v4mac_mangle[0]     = num_count
            self.tmp_in_v4mac_mangle[1]     = num_max
            self.tmp_in_v4mac_mangle[2]     = num_alloced

        if direction == "ingress" and tbl_type == "v6" and tbl_id == "mangle":
            self.tmp_in_v6_mangle[0]    = num_count
            self.tmp_in_v6_mangle[1]    = num_max
            self.tmp_in_v6_mangle[2]    = num_alloced

        if direction == "egress" and tbl_type == "v4mac" and tbl_id == "filter":
            self.tmp_eg_v4mac_filter[0]     = num_count
            self.tmp_eg_v4mac_filter[1]     = num_max
            self.tmp_eg_v4mac_filter[2]     = num_alloced

        if direction == "egress" and tbl_type == "v6" and tbl_id == "filter":
            self.tmp_eg_v6_filter[0]    = num_count
            self.tmp_eg_v6_filter[1]    = num_max
            self.tmp_eg_v6_filter[2]    = num_alloced


class BCM_TRIDENT2(BCM):
    
    def __init__(self, non_atomic_mode = False):
        BCM.__init__(self, "trident2", non_atomic_mode)
        if self.non_atomic_mode == False:
            num_in_256 = 4
            num_in_512 = 2
            num_in_768 = 0
            num_eg_256 = 2
            num_eg_512 = 0
            num_eg_768 = 0
            #entry table info
            self.default_in_v4mac_filter    = [36,512,256]
            self.default_in_v6_filter       = [29,768,512]
            self.default_in_mirror_filter   = [0,0,0]
            self.default_in_8021x_filter    = [0,0,0]
            self.default_in_v4mac_mangle    = [0,0,0]
            self.default_in_v6_mangle       = [0,0,0]
            self.default_eg_v4mac_filter    = [29,256,256]
            self.default_eg_v6_filter       = [0,0,0]
        else:
            num_in_256 = 8
            num_in_512 = 4
            num_in_768 = 0
            num_eg_256 = 4
            num_eg_512 = 0
            num_eg_768 = 0
            #entry table info
            self.default_in_v4mac_filter    = [36,512,256]
            self.default_in_v6_filter       = [29,768,512]
            self.default_in_mirror_filter   = [0,0,0]
            self.default_in_8021x_filter    = [0,0,0]
            self.default_in_v4mac_mangle    = [0,0,0]
            self.default_in_v6_mangle       = [0,0,0]
            self.default_eg_v4mac_filter    = [29,256,256]
            self.default_eg_v6_filter       = [0,0,0]

        BCM.update_slices(self,"ingress",num_in_256,num_in_512,num_in_768)
        BCM.update_slices(self,"egress",num_eg_256,num_eg_512,num_eg_768)
        BCM.update_default_tables(self)

    #slices are grouped as 2
    def get_256_slice_factor(self, direction):
        return 2

    #this function returns
    #   total number of slices reqd for entries
    #   distribution of slices across various sizes
    #in addition is also returns number of entries that get allocated
    #   for those slices
    def get_num_slices_entries_reqd_for_entries (self, entries, direction = "ingress", tbl_type = "v4mac", tbl_id = "filter", log = False):
    
        num_256     =   0
        num_512     =   0
        num_768     =   0
        num_total   =   0
        num_entries =   0

        if log == True:
            print direction, " ", tbl_type, " ", tbl_id, " | Entries : ",entries

        #Atomic Mode
        if self.non_atomic_mode == False:
            if tbl_type == 'v6' and tbl_id == 'filter':
                if entries <= 512:
                    if (entries%512) != 0:
                        num_512 = ((entries / 512)+1) * 2
                    else:
                        num_512 = ((entries / 512) * 2)
                
                else:
                    num_256_entries = entries - 512
                    num_512 = ((512 / 512)) * 2
                    num_256 = ((num_256_entries/256)+1)*2
                    if (num_256_entries%256) != 0:
                        num_256 = ((num_256_entries / 256)+1) * 2
                    else:
                        num_256 = ((num_256_entries / 256) * 2)
            

            else:
                if (entries%256) != 0:
                    num_256 = ((entries / 256)+1) * 2
                else:
                    num_256 = ((entries / 256) * 2)

        else:
            #Non Atomic Mode
            if (tbl_type == 'v6' or tbl_type == 'v4mac') and tbl_id == 'filter':
                if entries <= 256:
                    if (entries%256) != 0:
                        num_256 = ((entries / 256)+1) * 2
                    else:
                        num_256 = ((entries / 256) * 2)

                elif entries <= 512:
                    if (entries%512) != 0:
                        num_512 = ((entries / 512)+1) * 2
                    else:
                        num_512 = ((entries / 512) * 2)
                
                else:
                    num_256_entries = entries - 512
                    num_512 = ((512 / 512)) * 2
                    num_256 = ((num_256_entries/256)+1)*2
                    if (num_256_entries%256) != 0:
                        num_256 = ((num_256_entries / 256)+1) * 2
                    else:
                        num_256 = ((num_256_entries / 256) * 2)

            else:
                if (entries%256) != 0:
                    num_256 = ((entries / 256)+1) * 2
                else:
                    num_256 = ((entries / 256) * 2)
            
                

        num_total   = num_256 + num_512 + num_768
        num_entries = ((num_512/2)*512) + ((num_256/2)*256)

        if log == True:
            print "Total Slices : ",num_total," 256 : ", num_256, " | 512 : ", num_512, " | 768 : ", num_768," | Total Entries : ",num_entries
        return num_total, num_256, num_512, num_768, num_entries

    #Check if toggle of slices (IPv4MAC Filter or IPv6 Filter) is required
    #   These conditions are valid for ***NON ATOMIC MODE*** Only
    # Conditions in which this toggle is required :
    #   1.  IPv4 MAC Filter will toggle under following circumstances :
    #       a.  Number of IPv4 MAC Filter rules <= 256
    #       b.  Ingress IPv4 MAC Filter or Ingress IPv4 MAC Mangle or Egress IPv4 MAC Filter is written
    #       c.  (b) above and Ingress IPv6 Filter And/Or IPv6 Mangle is written
    #   2.  IPv6 Filter will toggle under following cusrcumstances  :
    #       a.  Number of IPv6 Filter rules <= 256
    #       b.  ***ONLY*** either or both of Ingress IPv6 Filter or Ingress IPv6 Mangle is written
    #       c.  If any of IPv4 is written, then IPv6 Filter will ***NOT*** toggle, as it falls in condition (1) above

    def check_if_toggle_slices_required(self, req_entries, comb_req_entries, log = False):
        flag_toggle_ipv4_fltr_reqd  =   False
        flag_toggle_ipv6_fltr_reqd  =   False

        flag_ipv4_write_req         =   False
        flag_ipv6_write_req         =   False

        if log == True:
            print "Checking if toggle slices required"
            if self.non_atomic_mode == False:
                print "Trident2 :   Atomic Mode"
            else:
                print "Trident2 :   Non Atomic Mode"

        if self.non_atomic_mode == False:
            return False, flag_toggle_ipv4_fltr_reqd, flag_toggle_ipv6_fltr_reqd

        #Go over each request and identify if any IPv4 or IPv6 Write is requested
        direction = "ingress"
        for tbl_id in get_table_ids():
            for tbl_type in get_table_type(tbl_id, direction):

                if log == True:
                    print "check_if_toggle_slices_required | Direction : ",direction," tbl_type : ",tbl_type," tbl_id : ",tbl_id

                if tbl_type == "v4mac":
                    if req_entries[tbl_type+tbl_id+direction] > 0:
                        flag_ipv4_write_req = True

                if tbl_type == "v6":
                    if req_entries[tbl_type+tbl_id+direction] > 0:
                        flag_ipv6_write_req = True


        if log == True:
            print "check_if_toggle_slices_required | Write Requested for IPv4 : ", flag_ipv4_write_req, " | IPv6 : ", flag_ipv6_write_req


        #get current allocation
        if comb_req_entries['v4mac'+'filter'+'ingress'] > 256:
            flag_toggle_ipv4_fltr_reqd = False

        if comb_req_entries['v6'+'filter'+'ingress'] > 256:
            flag_toggle_ipv6_fltr_reqd = False

    
        #if (flag_ipv4_write_req == True and flag_ipv6_write_req == False) and (comb_req_entries['v4mac'+'filter'+'ingress'] <= 256) and \
        #   ((req_entries['v4mac'+'filter'+'ingress'] > 0) or (req_entries['v4mac'+'mangle'+'ingress'] > 0) or (req_entries['v4mac'+'filter'+'egress'] > 0)):
        #   flag_toggle_ipv4_fltr_reqd = True
            
        #if (flag_ipv4_write_req == False and flag_ipv6_write_req == True) and (comb_req_entries['v6'+'filter'+'ingress'] <= 256) and \
        #   ((req_entries['v4mac'+'filter'+'ingress'] == 0) and (req_entries['v4mac'+'mangle'+'ingress'] == 0) and (req_entries['v4mac'+'filter'+'egress'] == 0) and \
        #   ((req_entries['v6'+'filter'+'ingress'] > 0) or (req_entries['v6'+'mangle'+'ingress'] > 0))):
        #   flag_toggle_ipv6_fltr_reqd = True

        if (flag_ipv4_write_req == True and flag_ipv6_write_req == False):
            v4mac_curr_alloc    =   0
            v4mac_curr_max      =   0
            v6_curr_alloc       =   0
            v6_curr_max         =   0

            curr_tbl_entry = self.get_current_table_entry(direction, "v4mac", "filter")
            if curr_tbl_entry:
                v4mac_curr_alloc    =   curr_tbl_entry[2]
                v4mac_curr_max      =   curr_tbl_entry[1]

            curr_tbl_entry = self.get_current_table_entry(direction, "v6", "filter")
            if curr_tbl_entry:
                v6_curr_alloc   =   curr_tbl_entry[2]
                v6_curr_max     =   curr_tbl_entry[1]

            if log == True:
                print "check_if_toggle_slices_required | IPv4 Only Write | v4mac_curr_alloc : ", v4mac_curr_alloc, " v6_curr_alloc : ",v6_curr_alloc, " v4mac_curr_max : ",v4mac_curr_max," v6_curr_max : ",v6_curr_max
            if (v4mac_curr_alloc == v6_curr_alloc) and (v4mac_curr_max == v6_curr_max):
                flag_toggle_ipv4_fltr_reqd = True
                if log == True:
                    print "check_if_toggle_slices_required | Toggling IPv4 Filter"
            elif (v4mac_curr_alloc == 256) and (v6_curr_alloc == 512):
                flag_toggle_ipv4_fltr_reqd = True
                if log == True:
                    print "check_if_toggle_slices_required | Toggling IPv4 Filter"

        if (flag_ipv6_write_req == True and flag_ipv4_write_req == False):
            v4mac_curr_alloc    =   0
            v4mac_curr_max      =   0
            v6_curr_alloc       =   0
            v6_curr_max         =   0

            curr_tbl_entry = self.get_current_table_entry(direction, "v4mac", "filter")
            if curr_tbl_entry:
                v4mac_curr_alloc    =   curr_tbl_entry[2]
                v4mac_curr_max      =   curr_tbl_entry[1]

            curr_tbl_entry = self.get_current_table_entry(direction, "v6", "filter")
            if curr_tbl_entry:
                v6_curr_alloc   =   curr_tbl_entry[2]
                v6_curr_max     =   curr_tbl_entry[1]

            if log == True:
                print "check_if_toggle_slices_required | IPv6 Only Write | v4mac_curr_alloc : ", v4mac_curr_alloc, " v6_curr_alloc : ",v6_curr_alloc, " v4mac_curr_max : ",v4mac_curr_max," v6_curr_max : ",v6_curr_max
            if (v4mac_curr_alloc == v6_curr_alloc) and (v4mac_curr_max == v6_curr_max):
                flag_toggle_ipv6_fltr_reqd = True
                if log == True:
                    print "check_if_toggle_slices_required | Toggling IPv6 Filter"
            elif (v4mac_curr_alloc == 512) and (v6_curr_alloc == 256):
                flag_toggle_ipv6_fltr_reqd = True
                if log == True:
                    print "check_if_toggle_slices_required | Toggling IPv6 Filter"
                    

        if (flag_ipv4_write_req == True and flag_ipv6_write_req == True) and ((comb_req_entries['v4mac'+'filter'+'ingress'] <= 256) and (comb_req_entries['v6'+'filter'+'ingress'] <= 256)):
            v4mac_curr_alloc    = 0
            v6_curr_alloc       = 0
            curr_tbl_entry = self.get_current_table_entry(direction, "v4mac", "filter")
            if curr_tbl_entry:
                v4mac_curr_alloc    =   curr_tbl_entry[2]

            curr_tbl_entry = self.get_current_table_entry(direction, "v6", "filter")
            if curr_tbl_entry:
                v6_curr_alloc   =   curr_tbl_entry[2]

            #If current IPv4 MAC Filter Alloc is 512 and IPv6 Filter is 256, then IPv6 Filter Toggles
            if v4mac_curr_alloc == 512 and v6_curr_alloc == 256:
                flag_toggle_ipv6_fltr_reqd = True
            #If Current IPv4 MAC Filter Alloc is 512 and IPv6 Filter is 512, then IPv4 MAC Filter toggles
            if v4mac_curr_alloc == 512 and v6_curr_alloc == 512:
                flag_toggle_ipv4_fltr_reqd = True
            #If current IPv4 MAC Filter Alloc is 256 and IPv6 Filter is 512, then both Toggle
            if v4mac_curr_alloc == 256 and v6_curr_alloc == 512:
                flag_toggle_ipv6_fltr_reqd = True
                flag_toggle_ipv4_fltr_reqd = True
    

        if log == True:
            print "check_if_toggle_slices_required | IPv4 Toggle Reqd : ", flag_toggle_ipv4_fltr_reqd, " | IPv6 Toggle Reqd : ", flag_toggle_ipv6_fltr_reqd

        return True, flag_toggle_ipv4_fltr_reqd, flag_toggle_ipv6_fltr_reqd
    
class BCM_TRIDENT3(BCM):
    
    def __init__(self, non_atomic_mode = False):
        BCM.__init__(self, "trident3", non_atomic_mode)
        if self.non_atomic_mode == False:
            #slice info
            num_in_256 = 0
            num_in_512 = 0
            num_in_768 = 6
            num_eg_256 = 0
            num_eg_512 = 2
            num_eg_768 = 0
            #entry table info
            self.default_in_v4mac_filter    = [42,768,768]
            self.default_in_v6_filter       = [36,768,768]
            self.default_in_mirror_filter   = [0,0,0]
            self.default_in_8021x_filter    = [0,0,0]
            self.default_in_v4mac_mangle    = [0,0,0]
            self.default_in_v6_mangle       = [0,0,0]
            self.default_eg_v4mac_filter    = [28,512,512]
            self.default_eg_v6_filter       = [0,0,0]
        else:
            #slice info
            num_in_256 = 0
            num_in_512 = 0
            num_in_768 = 12
            num_eg_256 = 0
            num_eg_512 = 4
            num_eg_768 = 0
            #entry table info
            self.default_in_v4mac_filter    = [36,512,256]
            self.default_in_v6_filter       = [29,768,512]
            self.default_in_mirror_filter   = [0,0,0]
            self.default_in_8021x_filter    = [0,0,0]
            self.default_in_v4mac_mangle    = [0,0,0]
            self.default_in_v6_mangle       = [0,0,0]
            self.default_eg_v4mac_filter    = [29,256,256]
            self.default_eg_v6_filter       = [0,0,0]

        BCM.update_slices(self,"ingress",num_in_256,num_in_512,num_in_768)
        BCM.update_slices(self,"egress",num_eg_256,num_eg_512,num_eg_768)
        BCM.update_default_tables(self)

    #not used for TD3, since there are no 256 entry slices in TD3
    def get_256_slice_factor(self, direction):
        return 2
    #this function returns
    #   total number of slices reqd for entries
    #   distribution of slices across various sizes
    #in addition is also returns number of entries that get allocated
    #   for those slices
    def get_num_slices_entries_reqd_for_entries (self, entries, direction = "ingress", tbl_type = "v4mac", tbl_id = "filter", log = False):
    
        num_256     =   0
        num_512     =   0
        num_768     =   0
        num_total   =   0
        num_entries =   0

        if log == True:
            print direction, " ", tbl_type, " ", tbl_id, " | Entries : ",entries

        if direction == "ingress":
            if (entries%768) != 0:
                num_768 = ((entries / 768)+1) * 3
            else:
                num_768 = ((entries / 768) * 3)

            num_total = num_768
            num_entries = (num_768/3)*768
        else:
            if (entries%512) != 0:
                num_512 = ((entries / 512)+1) * 2
            else:
                num_512 = (entries / 512) * 2

            num_total = num_512
            num_entries = ((num_512/2)*512)

        if log == True:
            print "Total Slices : ",num_total," 256 : ", num_256, " | 512 : ", num_512, " | 768 : ", num_768," | Total Entries : ",num_entries

        return num_total, num_256, num_512, num_768, num_entries
    
    def check_if_toggle_slices_required(self, req_entries, comb_req_entries, log = False):
        ret_val = False

        if log == True:
            print "Checking if toggle slices required. Not required for this platform(TD3)"

        return ret_val, False, False
    

class BCM_TOMAHAWK(BCM):
    
    def __init__(self, non_atomic_mode = False):
        BCM.__init__(self, "tomahawk", non_atomic_mode)
        if self.non_atomic_mode == False:
            num_in_256 = 6
            num_in_512 = 0
            num_in_768 = 0
            num_eg_256 = 2
            num_eg_512 = 0
            num_eg_768 = 0
            #entry table info
            self.default_in_v4mac_filter    = [36,256,256]
            self.default_in_v6_filter       = [29,256,256]
            self.default_in_mirror_filter   = [0,0,0]
            self.default_in_8021x_filter    = [0,0,0]
            self.default_in_v4mac_mangle    = [0,0,0]
            self.default_in_v6_mangle       = [0,0,0]
            self.default_eg_v4mac_filter    = [29,256,256]
            self.default_eg_v6_filter       = [0,0,0]
        else:
            num_in_256 = 12
            num_in_512 = 0
            num_in_768 = 0
            num_eg_256 = 4
            num_eg_512 = 0
            num_eg_768 = 0
            #entry table info
            self.default_in_v4mac_filter    = [36,768,256]
            self.default_in_v6_filter       = [29,768,256]
            self.default_in_mirror_filter   = [0,0,0]
            self.default_in_8021x_filter    = [0,0,0]
            self.default_in_v4mac_mangle    = [0,0,0]
            self.default_in_v6_mangle       = [0,0,0]
            self.default_eg_v4mac_filter    = [29,512,256]
            self.default_eg_v6_filter       = [0,0,0]

        BCM.update_slices(self,"ingress",num_in_256,num_in_512,num_in_768)
        BCM.update_slices(self,"egress",num_eg_256,num_eg_512,num_eg_768)
        BCM.update_default_tables(self)

    def get_256_slice_factor(self, direction):
        if direction == "ingress":
            return 3
        else:
            return 2

    #this function returns
    #   total number of slices reqd for entries
    #   distribution of slices across various sizes
    #in addition is also returns number of entries that get allocated
    #   for those slices
    def get_num_slices_entries_reqd_for_entries (self, entries, direction = "ingress", tbl_type = "v4mac", tbl_id = "filter", log = False):
    
        num_256     =   0
        num_512     =   0
        num_768     =   0
        num_total   =   0
        num_entries =   0

        if log == True:
            print direction, " ", tbl_type, " ", tbl_id, " | Entries : ",entries

        if direction == "ingress":
            slice_factor = 3
        else:
            slice_factor = 2

        if (entries%256) != 0:
            num_256 = ((entries / 256)+1) * slice_factor
        else:
            num_256 = ((entries / 256) * slice_factor)

        num_total = num_256
        num_entries = (num_256/slice_factor)*256

        if log == True:
            print "Total Slices : ",num_total," 256 : ", num_256, " | 512 : ", num_512, " | 768 : ", num_768," | Total Entries : ",num_entries
        return num_total, num_256, num_512, num_768, num_entries

    def check_if_toggle_slices_required(self, req_entries, comb_req_entries, log = False):
        ret_val = False

        if log == True:
            print "Checking if toggle slices required. Not required for this platform(TH/TH+)"

        return ret_val, False, False
    

def read_non_update_atomic_update(log = False):

    ret_val = True
    non_atomic_update = False

    cmd = "cat /cumulus/switchd/config/acl/non_atomic_update_mode"
    f = os.popen(cmd)
    output = f.read()
    if "FALSE" in output:
        non_atomic_update = False
    elif "TRUE" in output:
        non_atomic_update = True
    else:
        if log == True:
            print "Data for non atomic update is wrong : ", output
            print "Exiting"
        ret_val = False

    return ret_val, non_atomic_update

def get_table_name(tbl_type,tbl_id,direction):

    global acl_table_name

    acl_table_name = {}

    acl_table_name['v4mac'+'filter'+'ingress']  = "in_acl_v4mac_filter"
    acl_table_name['v6'+'filter'+'ingress']     = "in_acl_v6_filter"
    acl_table_name['mirror'+'filter'+'ingress'] = "in_acl_mirror_filter"
    acl_table_name['8021x'+'filter'+'ingress']  = "in_acl_8021x_filter"
    acl_table_name['v4mac'+'mangle'+'ingress']  = "in_acl_v4mac_mangle"
    acl_table_name['v6'+'mangle'+'ingress']     = "in_acl_v6_mangle"
    acl_table_name['mirror'+'mangle'+'ingress'] = ""
    acl_table_name['8021x'+'mangle'+'ingress']  = ""
    acl_table_name['v4mac'+'filter'+'egress']   = "eg_acl_v4mac_filter"
    acl_table_name['v6'+'filter'+'egress']      = "eg_acl_v6_filter"
    acl_table_name['mirror'+'filter'+'egress']  = ""
    acl_table_name['8021x'+'filter'+'egress']   = ""
    acl_table_name['v4mac'+'mangle'+'egress']   = ""
    acl_table_name['v6'+'mangle'+'egress']      = ""
    acl_table_name['mirror'+'mangle'+'egress']  = ""
    acl_table_name['8021x'+'mangle'+'egress']   = ""
    
    return acl_table_name[tbl_type+tbl_id+direction]

def get_table_type(tbl_id,direction):

    global acl_table_type

    acl_table_type = {}

    acl_table_type_in_filter    =   ['v4mac','v6','mirror','8021x']
    acl_table_type_in_mangle    =   ['v4mac','v6']
    acl_table_type_eg_filter    =   ['v4mac','v6']
    acl_table_type_eg_mangle    =   []

    acl_table_type['filter'+'ingress']  =   acl_table_type_in_filter
    acl_table_type['mangle'+'ingress']  =   acl_table_type_in_mangle
    acl_table_type['filter'+'egress']   =   acl_table_type_eg_filter
    acl_table_type['mangle'+'egress']   =   acl_table_type_in_mangle

    return acl_table_type[tbl_id+direction]

def get_table_directions():

    acl_directions  = ['ingress','egress']

    return acl_directions

def get_table_ids():

    acl_table_ids   = ['filter','mangle']

    return acl_table_ids

def display_current_entry_info(chip_info, direction = "ingress", tbl_type = "v4mac", tbl_id = "filter"):
    tbl = chip_info.get_current_table_entry(direction, tbl_type, tbl_id)
    if tbl:
        print direction, " ", tbl_type, " ", tbl_id, " : ", "Used : ", tbl[0], " Max : ", tbl[1], " Alloc'd : ",tbl[2]

def display_current_table(chip_info, use_json=False):

    stats_dict = {}
    for direction in get_table_directions():
        for tbl_id in get_table_ids():
            for tbl_type in get_table_type(tbl_id, direction):
                if use_json == False:
                    display_current_entry_info(chip_info, direction, tbl_type, tbl_id)

                if use_json == True:
                    key = get_table_name(tbl_type, tbl_id, direction)
                    tbl = chip_info.get_current_table_entry(direction, tbl_type, tbl_id)
                    if key and tbl:
                        stats_dict[key] = {
                            'count': int(tbl[0]),
                            'max': int(tbl[1]),
                            'allocated': int(tbl[2])
                        }

    if use_json == True:
        print json.dumps(stats_dict, sort_keys=True, indent=4)

def display_default_entry_info(chip_info, direction = "ingress", tbl_type = "v4mac", tbl_id = "filter"):
    tbl = chip_info.get_default_table_entry(direction, tbl_type, tbl_id)
    if tbl:
        print direction, " ", tbl_type, " ", tbl_id, " : ", "Used : ", tbl[0], " Max : ", tbl[1], " Alloc'd : ",tbl[2]

def display_default_table(chip_info):

    for direction in get_table_directions():
        for tbl_id in get_table_ids():
            for tbl_type in get_table_type(tbl_id, direction):
                display_default_entry_info(chip_info, direction, tbl_type, tbl_id)

def display_slice_info(chip_info):
    num_in_256, num_in_512, num_in_768 = chip_info.get_slices("ingress")
    num_eg_256, num_eg_512, num_eg_768 = chip_info.get_slices("egress")
    total_in_slices = chip_info.get_total_slices("ingress")
    total_eg_slices = chip_info.get_total_slices("egress")


    print "Ingress 256 Entry Slices : ", num_in_256
    print "Ingress 512 Entry Slices : ", num_in_512
    print "Ingress 768 Entry Slices : ", num_in_768
    print "Egress 256 Entry Slices : ", num_eg_256
    print "Egress 512 Entry Slices : ", num_eg_512
    print "Egress 768 Entry Slices : ", num_eg_768
    print "Total Ingress Slices : ", total_in_slices
    print "Total Egress Slices : ", total_eg_slices

def get_cl_res_json_data():
    cmd = "cl-resource-query -j"
    f = os.popen(cmd)
    output = f.read()
    jout = json.loads(output)
    return jout

def check_if_res_avlbl(chip_info, req_entries, log = False, analysis_log = True):
    comb_req_entries    =   {}
    comb_req_entries['v4mac'+'filter'+'ingress']    =   0
    comb_req_entries['v6'+'filter'+'ingress']       =   0
    comb_req_entries['mirror'+'filter'+'ingress']   =   0
    comb_req_entries['8021x'+'filter'+'ingress']    =   0
    comb_req_entries['v4mac'+'mangle'+'ingress']    =   0
    comb_req_entries['v6'+'mangle'+'ingress']       =   0
    comb_req_entries['mirror'+'mangle'+'ingress']   =   0
    comb_req_entries['8021x'+'mangle'+'ingress']    =   0
    comb_req_entries['v4mac'+'filter'+'egress']     =   0
    comb_req_entries['v6'+'filter'+'egress']        =   0
    comb_req_entries['mirror'+'filter'+'egress']    =   0
    comb_req_entries['8021x'+'filter'+'egress']     =   0
    comb_req_entries['v4mac'+'mangle'+'egress']     =   0
    comb_req_entries['v6'+'mangle'+'egress']        =   0
    comb_req_entries['mirror'+'mangle'+'egress']    =   0
    comb_req_entries['8021x'+'mangle'+'egress']     =   0

    for direction in get_table_directions():
        for tbl_id in get_table_ids():
            for tbl_type in get_table_type(tbl_id, direction):
                #ignore below combos
                #ingress mirror+mangle, 8021x+mangle
                #egress mirror+filter/mangle, 8021x+filter/mangle
                #egress ipv6
            
                #add ciurrent to request and generate the composite req
                if log == True:
                    print "Processing request for ", direction, " ", tbl_type, " ", tbl_id," | Num of entries requested : ",req_entries[tbl_type+tbl_id+direction]

                if direction == "egress" and tbl_type  == "v6" and tbl_id == "filter" and (req_entries[tbl_type+tbl_id+direction] > 0):
                    #cannot process this request for now, HW does not support it for egress
                    if log == True:
                        print "Egress IPv6 Filter rules are not supported currently."
                    return False

                curr_tbl_entry = chip_info.get_current_table_entry(direction, tbl_type, tbl_id)
                if curr_tbl_entry:
                    curr_tbl_entry_used     =   curr_tbl_entry[0]
                    curr_tbl_entry_max      =   curr_tbl_entry[1]
                    curr_tbl_entry_alloc    =   curr_tbl_entry[2]
                    comb_req_entries[tbl_type+tbl_id+direction] =   curr_tbl_entry_used + req_entries[tbl_type+tbl_id+direction]
                    if log == True:
                        print "Current Used ",curr_tbl_entry_used," Entries for ",direction," ",tbl_type," ",tbl_id

    ret_val, flag_toggle_ipv4_reqd, flag_toggle_ipv6_reqd = chip_info.check_if_toggle_slices_required(req_entries, comb_req_entries, log)
    if log == True:
        print "Toggle required : ",ret_val, " | IPv4 : ",flag_toggle_ipv4_reqd, " | IPv6 : ",flag_toggle_ipv6_reqd
    if ret_val == True:
        if log == True:
            print "Toggle is required"

        toggle_alloc_and_recalc_max_all(chip_info,"ingress",flag_toggle_ipv4_reqd,flag_toggle_ipv6_reqd,log)
        
        

    for direction in get_table_directions():
        for tbl_id in get_table_ids():
            for tbl_type in get_table_type(tbl_id, direction):
                #ignore below combos
                #ingress mirror+mangle, 8021x+mangle
                #egress mirror+filter/mangle, 8021x+filter/mangle
            
                #add ciurrent to request and generate the composite req
                curr_tbl_entry = chip_info.get_current_table_entry(direction, tbl_type, tbl_id)
                if curr_tbl_entry:
                    curr_tbl_entry_used     =   curr_tbl_entry[0]
                    curr_tbl_entry_max      =   curr_tbl_entry[1]
                    curr_tbl_entry_alloc    =   curr_tbl_entry[2]
                    total_curr_req          =   comb_req_entries[tbl_type+tbl_id+direction]
                    if log == True:
                        print "Total Requested ",comb_req_entries[tbl_type+tbl_id+direction]," Entries for ",direction," ",tbl_type," ",tbl_id

                    #for testing only
                    #chip_info.get_num_slices_entries_reqd_for_entries(curr_tbl_entry_alloc,direction,tbl_type,tbl_id,log)

                    #check if total request fits within MAX
                    if req_entries[tbl_type+tbl_id+direction] > 0 and total_curr_req <= curr_tbl_entry_max:
                        #possible to allocate
                        #check, if post granting this request, allocation would change or not

                        #check if total request can fit in within current allocation or not
                        if total_curr_req <= curr_tbl_entry_alloc:
                            #can allocate
                            chip_info.update_current_table_entry(direction, tbl_type, tbl_id, total_curr_req, curr_tbl_entry_max, curr_tbl_entry_alloc)
                            if log == True:
                                print "Requested ",req_entries[tbl_type+tbl_id+direction]," Entries for ",direction," ",tbl_type," ",tbl_id," **CAN BE ALLOCATED**"
                        else:
                            #can allocate, but after allocation requires reallocation of all tables
                            #during this reallocation, max and allocated for each table may/may not change
                            if log == True:
                                print "Reallocing tables for ",direction, " ",tbl_type," ", tbl_id
                            realloc_all_tables(chip_info, req_entries[tbl_type+tbl_id+direction], direction, tbl_type, tbl_id, log)

                    else:
                    #total request cannot fit in within max
                    #this request cannot be honoured
                        if req_entries[tbl_type+tbl_id+direction] != 0:
                            if curr_tbl_entry_max != 0 and curr_tbl_entry_alloc != 0 and total_curr_req > curr_tbl_entry_max:
                                if log == True:
                                    print "Requested ",req_entries[tbl_type+tbl_id+direction]," Entries for ",direction,\
                                        " ",tbl_type," ",tbl_id," **CANNOT BE ALLOCATED** | Request beyond MAX"
                                if analysis_log == True:
                                    print "Requested ",req_entries[tbl_type+tbl_id+direction]," Entries for ",direction,\
                                        " ",tbl_type," ",tbl_id," **CANNOT BE ALLOCATED** as it is greater than max possible entries"
                                return False
                            elif curr_tbl_entry_max == 0 and curr_tbl_entry_alloc == 0:
                                if log == True:
                                    print "0 allocation for ", direction, " ", tbl_type, " ", tbl_id
                                    print "Requested ",req_entries[tbl_type+tbl_id+direction]," for ", direction, " ", tbl_type, " ", tbl_id
                                #check if free slices are available
                                total_free_slices, free_256_slices, free_512_slices, free_768_slices, total_free_entries = \
                                    chip_info.get_curr_num_free_slices_entries_available(direction,log)
                                    #chip_info.get_num_free_slices_entries_available(direction)
                                if log == True:
                                    print total_free_slices, " Free slices and ", total_free_entries, " Free entries are available."
                                if total_free_slices > 0 and total_free_entries >= req_entries[tbl_type+tbl_id+direction]:
                                    if log == True:
                                        print "Trying to allocate"
                                        print "Reallocing tables for ",direction, " ",tbl_type," ", tbl_id
                                    realloc_all_tables(chip_info, req_entries[tbl_type+tbl_id+direction], direction, tbl_type, tbl_id, log)
                                elif total_free_slices > 0 and total_free_entries > 0 and total_free_entries < req_entries[tbl_type+tbl_id+direction]:
                                    if log == True:
                                        print "Not enough Free entries available. ****Cannot Allocate****"
                                    if analysis_log == True:
                                        print "Not enough Free entries available to allocate request of ",\
                                            req_entries[tbl_type+tbl_id+direction]," entries for ", direction, " ", tbl_type, " ", tbl_id
                                    return False
                                elif total_free_slices <= 0:
                                    if log == True:
                                        print "No Free slices available. ****Cannot Allocate****"
                                    if analysis_log == True:
                                        print "No Free slices available to allocate request of ",\
                                            req_entries[tbl_type+tbl_id+direction]," entries for ", direction, " ", tbl_type, " ", tbl_id
                                    return False
                                elif total_free_entries <= 0:
                                    if log == True:
                                        print "No Free entries available. ****Cannot Allocate****"
                                    if analysis_log == True:
                                        print "No Free entries available to allocate request of ",\
                                            req_entries[tbl_type+tbl_id+direction]," entries for ", direction, " ", tbl_type, " ", tbl_id
                                    return False

    return True
                                

def toggle_alloc_and_recalc_max_all(chip_info, direction, flag_toggle_ipv4, flag_toggle_ipv6, log = False):
    if log == True:
        print "toggle_alloc_and_recalc_max_all | direction : ",direction," toggle flag : IPv4 : ",flag_toggle_ipv4, " | IPv6 : ",flag_toggle_ipv6

    #toggle is for ingress only, so discard call for egress
    if direction != "ingress":
        return

    #check what is the current allocation for the requested table
    if flag_toggle_ipv4 == True or flag_toggle_ipv6 == True:
        if flag_toggle_ipv4 == True:
            tbl_type = "v4mac"
            tbl_id  = "filter"
            curr_tbl_entry = chip_info.get_current_table_entry(direction, "v4mac", "filter")
            if curr_tbl_entry:
                curr_tbl_entry_used     =   curr_tbl_entry[0]
                curr_tbl_entry_max      =   curr_tbl_entry[1]
                curr_tbl_entry_alloc    =   curr_tbl_entry[2]
                if log == True:
                    print "toggle_alloc_and_recalc_max_all } Alloc :  ",curr_tbl_entry_alloc," Entries for ",direction," v4mac Filter"
        
                if curr_tbl_entry_alloc == 256:
                    curr_tbl_entry_alloc = 512
                elif curr_tbl_entry_alloc == 512:
                    curr_tbl_entry_alloc = 256

                chip_info.update_current_table_entry(direction, tbl_type, tbl_id, curr_tbl_entry_used, curr_tbl_entry_max, curr_tbl_entry_alloc)

        if flag_toggle_ipv6 == True:
            tbl_type = "v6"
            tbl_id  = "filter"
            curr_tbl_entry = chip_info.get_current_table_entry(direction, "v6", "filter")
            if curr_tbl_entry:
                curr_tbl_entry_used     =   curr_tbl_entry[0]
                curr_tbl_entry_max      =   curr_tbl_entry[1]
                curr_tbl_entry_alloc    =   curr_tbl_entry[2]
                if log == True:
                    print "toggle_alloc_and_recalc_max_all | Alloc :  ",curr_tbl_entry_alloc," Entries for ",direction," v6 Filter"
                
                if curr_tbl_entry_alloc == 256:
                    curr_tbl_entry_alloc = 512
                elif curr_tbl_entry_alloc == 512:
                    curr_tbl_entry_alloc = 256

                chip_info.update_current_table_entry(direction, tbl_type, tbl_id, curr_tbl_entry_used, curr_tbl_entry_max, curr_tbl_entry_alloc)


        #Now that updation is done, recalculate MAX
        #get free slices and entries
        total_free_slices, free_256_slices, free_512_slices, free_768_slices, total_free_entries = chip_info.get_curr_num_free_slices_entries_available(direction,log)
        if log == True:
            print "toggle_alloc_and_recalc_max_all | Free Info for ",direction," Total Free slices : ",total_free_slices," Free Slices : 256 : ",free_256_slices," 512 : ",free_512_slices," 768 : ",free_768_slices," Total Free Entries : ",total_free_entries
        

        #claculate max by adding the free remaining entries to current alloc to each and update
        for tbl_id in get_table_ids():
            for tbl_type in get_table_type(tbl_id, direction):

                if log == True:
                    print "toggle_alloc_and_recalc_max_all | Direction : ",direction," tbl_type : ",tbl_type," tbl_id : ",tbl_id
                tbl_entry = chip_info.get_current_table_entry(direction, tbl_type, tbl_id)
                if tbl_entry:
                    num_used    =   tbl_entry[0]
                    num_max     =   tbl_entry[1]
                    num_alloc   =   tbl_entry[2]

                    if log == True:
                        print "toggle_alloc_and_recalc_max_all | Used : ", num_used, " Max : ", num_max, " Alloc : ", num_alloc
                    
                    if num_alloc != 0:
                        num_max = total_free_entries + num_alloc
                        if log == True:
                            print "toggle_alloc_and_recalc_max_all | Updating --> num_used : ",num_used," | max : ",num_max," | alloc : ",num_alloc
                        chip_info.update_current_table_entry(direction, tbl_type, tbl_id, num_used, num_max, num_alloc)

            

def realloc_all_tables(chip_info, req_entries, req_direction, req_tbl_type, req_tbl_id, log = False):
    #identify which table has requested the change
    #check how may slices will the new request occupy
    #check if requested slices are available
    #go over each table and update max and alloc for each in this direction 
    curr_tbl_entry = chip_info.get_current_table_entry(req_direction, req_tbl_type, req_tbl_id)
    if curr_tbl_entry:
        curr_tbl_entry_used     =   curr_tbl_entry[0]
        curr_tbl_entry_max      =   curr_tbl_entry[1]
        curr_tbl_entry_alloc    =   curr_tbl_entry[2]
        total_req_entries       =   curr_tbl_entry_used + req_entries
        if log == True:
            print "Req entries : ", req_entries, " curr_tbl_entry_used : ", curr_tbl_entry_used," total_req_entries : ",total_req_entries

        #get number of slices it already occupies
        old_num_total_slices, old_num_256, old_num_512, old_num_768, old_num_entries = chip_info.get_num_slices_entries_reqd_for_entries(curr_tbl_entry_used, req_direction, req_tbl_type, req_tbl_id)
        #get new number of slices it WILL occupy
        req_num_total_slices, req_num_256, req_num_512, req_num_768, req_num_entries = chip_info.get_num_slices_entries_reqd_for_entries(total_req_entries, req_direction, req_tbl_type, req_tbl_id)
        #check if the delta number of slices of this type is available
        total_free_slices, free_256_slices, free_512_slices, free_768_slices, total_free_entries = chip_info.get_curr_num_free_slices_entries_available(req_direction,log)

        delta_req_slices = req_num_total_slices - old_num_total_slices
        delta_req_256_slices = req_num_256 - old_num_256
        delta_req_512_slices = req_num_512 - old_num_512
        delta_req_768_slices = req_num_768 - old_num_768
        delta_req_num_entries   =   req_num_entries - old_num_entries
        #compute remaining free entries after this allocation is done
        #diff of total free and the delta new that is getting allocated
        rem_free_entries        =   total_free_entries - delta_req_num_entries

        if log == True:
            print "old entries : ",old_num_entries," req_num_entries : ",req_num_entries," delta : ",delta_req_num_entries, " total free entries : ", total_free_entries
            print "Rem Free entries : ", rem_free_entries, " delta req slices : ", delta_req_slices, " total free slices : ", total_free_slices

        if delta_req_slices <= total_free_slices:
            if delta_req_256_slices <= free_256_slices and delta_req_512_slices <= free_512_slices and delta_req_768_slices <= free_768_slices:
                #allocation is possible sanity done
                #now reallocate for all
                if log == True:
                    print "Allocation sanity done"
            else:
                if log == True:
                    print "This should not happen"
        else:
            if log == True:
                print "Why this failure? Something wrong"


        direction = req_direction
        for tbl_id in get_table_ids():
            for tbl_type in get_table_type(tbl_id, direction):

                if log == True:
                    print "Req : direction : ",req_direction," tbl_type : ",req_tbl_type," tbl_id : ",req_tbl_id
                    print "Direction : ",direction," tbl_type : ",tbl_type," tbl_id : ",tbl_id
                tbl_entry = chip_info.get_current_table_entry(direction, tbl_type, tbl_id)
                if tbl_entry:
                    num_used    =   tbl_entry[0]
                    num_max     =   tbl_entry[1]
                    num_alloc   =   tbl_entry[2]

                    if log == True:
                        print "Used : ", num_used, " Max : ", num_max, " Alloc : ", num_alloc
                    if direction == req_direction and tbl_id == req_tbl_id and tbl_type == req_tbl_type:
                        num_used    =   total_req_entries 
                        num_alloc   =   num_alloc + delta_req_num_entries
                        #num_max        =   num_alloc + (total_free_entries - req_num_entries)
                        num_max     =   num_alloc + rem_free_entries
                        if num_max == 0:
                            num_max = delta_req_num_entries

                    else:
                        tbl_entry = chip_info.get_current_table_entry(direction, tbl_type, tbl_id)
                        if tbl_entry:
                            num_used    =   tbl_entry[0]
                            num_max     =   tbl_entry[1]
                            num_alloc   =   tbl_entry[2]
                            if log == True:
                                print "num_used : ",num_used," | max : ",num_max," | alloc : ",num_alloc
                            if num_alloc != 0:
                                num_max =   num_alloc + rem_free_entries
                                #num_max = num_alloc + (total_free_entries - req_num_entries)
                
                    if log == True:
                        print "Updating --> num_used : ",num_used," | max : ",num_max," | alloc : ",num_alloc
                    chip_info.update_current_table_entry(direction, tbl_type, tbl_id, num_used, num_max, num_alloc)

def parse_file_and_get_num_rules(inputfile, is_control_plane_rules = False, log = False):

    fp = open(inputfile,"r")

    data = fp.read()

    num_rules = {}
    num_rules["iptables"] = 0
    num_rules["ip6tables"] = 0
    num_rules["ebtables"] = 0

    num_rules["ingress"+"v4mac"+"filter"]=0
    num_rules["ingress"+"v4mac"+"mangle"]=0
    num_rules["ingress"+"v6"+"filter"]=0
    num_rules["ingress"+"v6"+"mangle"]=0
    num_rules["ingress"+"mirror"+"filter"]=0
    num_rules["ingress"+"8021x"+"filter"]=0
    num_rules["egress"+"v4mac"+"filter"]=0
    num_rules["egress"+"v6"+"filter"]=0

    found_iptables  = False
    found_ip6tables = False
    found_ebtables  = False
    
    data_iptables = ""
    data_ip6tables = ""
    data_ebtables = ""
    start_iptables  = False
    start_ip6tables = False
    start_ebtables  = False

    for split_line in data.splitlines():
        if split_line and split_line.startswith("#") == False:
            if log == True:
                print "LINE --> ", split_line, "\n"
            if found_iptables == False and found_ip6tables == False and found_ebtables == False:
                line = split_line.rsplit()
                if len(line) == 3 and line[1] == "=":
                    if log == True:
                        print "New LINE -- > ", line, "\n"              
                    file_var_name   = line[0]
                    file_var_val    = line[2]
                    if log == True:
                        print "Variable Name : ", file_var_name, " Value : ", file_var_val

            if split_line == "[iptables]":
                found_iptables = True
                start_iptables = True
                start_ip6tables = False
                start_ebtables  = False

            if split_line == "[ip6tables]":
                found_ip6tables = True
                start_ip6tables = True
                start_iptables  = False
                start_ebtables  = False

            if split_line == "[ebtables]":
                found_ebtables = True
                start_ebtables = True
                start_iptables  = False
                start_ip6tables = False

            if start_iptables == True and found_iptables == True:
                if split_line != "[iptables]":
                    data_iptables += split_line
                    data_iptables += "\n"

            if start_ip6tables == True and found_ip6tables == True:
                if split_line != "[ip6tables]":
                    data_ip6tables += split_line
                    data_ip6tables += "\n"

            if start_ebtables == True and found_ebtables == True:
                if split_line != "[ebtables]":
                    data_ebtables += split_line
                    data_ebtables += "\n"


    if log == True:
        print "IPTables --> ",data_iptables, "\n"
        print "IP6Tables --> ",data_ip6tables, "\n"
        print "EbTables --> ",data_ebtables, "\n"

    found_iptables = False
    found_ip6tables = False
    found_ebtables = False
    for split_line in data.splitlines():
        if split_line and split_line.startswith("#") == False:
            if found_iptables == False and found_ip6tables == False and found_ebtables == False:
                line = split_line.rsplit()
                if len(line) == 3 and line[1] == "=":
                    if log == True:
                        print "New LINE -- > ", line, "\n"              
                    file_var_name   = line[0]
                    file_var_val    = line[2]
                    new_var_name = '$'+file_var_name
                    if log == True:
                        print "Variable Name : ", file_var_name, " New Var Name : ", new_var_name, " Value : ", file_var_val
                    data_iptables = data_iptables.replace(new_var_name,file_var_val)
                    data_ip6tables = data_ip6tables.replace(new_var_name,file_var_val)
                    data_ebtables = data_ebtables.replace(new_var_name,file_var_val)


            if split_line == "[iptables]":
                found_iptables = True
    
            if split_line == "[ip6tables]":
                found_ip6tables = True

            if split_line == "[ebtables]":
                found_ebtables = True

    table_names = ["iptables", "ip6tables", "ebtables"]
    if log == True:
        print "Len of IPTables : ", len(data_iptables.splitlines())
        print "Modified IPTables --> ",data_iptables.splitlines(), "\n"
        print "Len of IP6Tables : ", len(data_ip6tables.splitlines())
        print "Modified IP6Tables --> ",data_ip6tables.splitlines(), "\n"
        print "Len of EbTables : ", len(data_ebtables.splitlines())
        print "Modified EbTables --> ",data_ebtables.splitlines(), "\n"

    split_line_dict = {}
    split_line_dict["iptables"] = data_iptables.splitlines()
    split_line_dict["ip6tables"] = data_ip6tables.splitlines()
    split_line_dict["ebtables"] = data_ebtables.splitlines()

    for tblName in table_names:
        acl_ipv4mac = False
        acl_ipv6    = False

        if tblName == "iptables" or tblName == "ebtables":
            acl_ipv4mac = True
        elif tblName == "ip6tables":
            acl_ipv6 = True

        split_line = split_line_dict[tblName]
        acl_num_child_police_rules = 0
        for split_line_itr in range (0,len(split_line)):

            #Inititalize all flags here
            # True indicates filter, False indicates Mangle
            acl_filter = False

            acl_mirror_present          =   False
            acl_8021x_present           =   False

            #True indicates Input, False indictes Forward
            acl_input_present           =   False
            acl_forward_present         =   False
            acl_output_present          =   False
    
            acl_num_chains_present      =   False

            acl_setclass_action_present =   False
            acl_class_present           =   False
            acl_class_value             =   0

            acl_police_action_present   =   False
        
            acl_in_intf_present         =   False
            acl_out_intf_present        =   False

            acl_num_in_intfs            =   0
            acl_num_out_intfs           =   0
    
            acl_src_add_present         =   False
            acl_dst_add_present         =   False

            acl_num_src_add             =   0
            acl_num_dst_add             =   0

            acl_src_port_present        =   False
            acl_dst_port_present        =   False

            acl_src_port_range_exists   =   False
            acl_dst_port_range_exists   =   False

            acl_num_src_ports           =   0
            acl_num_dst_ports           =   0

            acl_match_present           =   False
            #

            if acl_num_child_police_rules != 0:
                acl_num_child_police_rules -= 1
                continue

            if log == True:
                print ">>>>>>>>>>>> RULE LINE in ",tblName, " @ ", split_line_itr, " --> ", split_line[split_line_itr]
            acl_num_child_police_rules = 0
            #each line is 1 Rule
            split_data = split_line[split_line_itr].split()
            #print "Split Data : ", split_data
            split_data_len = len(split_data)
            my_data_iptable_dict = {}
            for split_data_itr in range (0,split_data_len/2):
                split_data_idx = 2*split_data_itr
                #print "Split Data Idx : ", split_data_idx
                my_data_iptable_dict[split_data[split_data_idx]] = split_data[split_data_idx+1]

            #print "My dictionary --> ", my_data_iptable_dict

            #look for Table Filter/Mangle
            #By default in -t is missing its a filter table
            val = 'INVALID'
            try:
                val = my_data_iptable_dict['-t']
        
            except KeyError:
                val = 'INVALID'

            if  val != 'INVALID' and val != 'filter':
                #print "Mangle Rule"
                acl_filter = False
            else:
                #print "Filter Rule"
                acl_filter = True
    

            #-A for Checking chains , Input or Forward  
            val = 'INVALID'
            try:
                val = my_data_iptable_dict['-A']
        
            except KeyError:
                val = 'INVALID'

            if  val != 'INVALID':
                if my_data_iptable_dict["-A"]:
                    #print " Chain Type : ", my_data_iptable_dict["-A"]
                    acl_num_chains_present = val.count(',') + 1

                    if "INPUT" in val:
                        acl_input_present = True                        
                    
                    if "FORWARD" in val:
                        acl_forward_present = True                      
                    
                    if "OUTPUT" in val:
                        acl_output_present = True                       
                    

            #Look up for Ingress interface with -i option
            val = 'INVALID'
            try:
                val = my_data_iptable_dict['-i']
        
            except KeyError:
                val = 'INVALID'

            if  val != 'INVALID':
                if my_data_iptable_dict['-i']:
                    #print "Ingress interface : ", my_data_iptable_dict['-i']
                    acl_in_intf_present = True

            #Look up for Ingress interface with --in-interface option
            val = 'INVALID'
            try:
                val = my_data_iptable_dict['--in-interface']
        
            except KeyError:
                val = 'INVALID'

            if  val != 'INVALID':
                if my_data_iptable_dict['--in-interface']:
                    #print "Ingress interface : ", my_data_iptable_dict['--in-interface']
                    acl_in_intf_present = True
                
            #Look up for Egress interface with -o option
            val = 'INVALID'
            try:
                val = my_data_iptable_dict['-o']
        
            except KeyError:
                val = 'INVALID'

            if  val != 'INVALID':
                if my_data_iptable_dict['-o']:
                    #print "Egress interface : ", my_data_iptable_dict['-o']
                    acl_out_intf_present = True

            #Look up for Egress interface with --out-interface option
            val = 'INVALID'
            try:
                val = my_data_iptable_dict['--out-interface']
        
            except KeyError:
                val = 'INVALID'

            if  val != 'INVALID':
                if my_data_iptable_dict['--out-interface']:
                    #print "Egress interface : ", my_data_iptable_dict['--out-interface']
                    acl_out_intf_present = True
                
            #Look up for action with -j option
            val = 'INVALID'
            try:
                val = my_data_iptable_dict['-j']
        
            except KeyError:
                val = 'INVALID'

            if  val != 'INVALID':
                #if my_data_iptable_dict['-j']:
                #   print "Action : ", my_data_iptable_dict['-j']

                if my_data_iptable_dict['-j'] == 'setclass' or my_data_iptable_dict['-j'] == 'SETCLASS':
                    #print "*************** Got SETCLASS"
                    acl_setclass_action_present = True
                    curr_split_line_itr = split_line_itr + 1
                    #check if next line has police at -j
                    #if has each "-j" police is one rule
                    ret_val = True
                    while ret_val == True:
                        ret_val = check_if_line_has_police_action(split_line, curr_split_line_itr)
                        if ret_val == True:
                            #print "Found Police, checking again ..."
                            curr_split_line_itr += 1
                            acl_num_child_police_rules += 1
            
                if my_data_iptable_dict['-j'] == 'police' or my_data_iptable_dict['-j'] == 'POLICE':
                    #print "Got POLICE"
                    #print "Found POLICE RULE --> ", split_line
                    acl_police_action_present = True
                    #else:
                    #   break

                    #print "Done finding Police for SETCLASS | Num of Child Police Rules : ", num_child_police_rules

            #Look up for class value with --class option
            val = 'INVALID'
            try:
                val = my_data_iptable_dict['--class']
        
            except KeyError:
                val = 'INVALID'

            if  val != 'INVALID':
                if my_data_iptable_dict['--class']:
                    #print "Ingress interface : ", my_data_iptable_dict['--in-interface']
                    acl_class_present   = True
                    acl_class_value     = int(val)  
                
            #Look up for Source IP with -s option
            val = 'INVALID'
            try:
                val = my_data_iptable_dict['-s']
        
            except KeyError:
                val = 'INVALID'

            if  val != 'INVALID':
                if my_data_iptable_dict['-s']:
                    #if tblName == "iptables":
                    #   print "IPv4 Source Address"
                    #elif tblName == "ip6tables":
                    #   print "IPv6 Source Address"
                    #elif tblName == "ebtables":
                    #   print "Source MAC Address"

                    acl_src_add_present = True
                    acl_num_src_add = val.count(',')+1

                    #print "Address : ", my_data_iptable_dict['-s']
                    #if val.count(','):
                    #   print "%d Addresses", val.count(',')+1
                    #else:
                    #   print "Only 1 Address"

            #Look up for Destination IP with -d option
            val = 'INVALID'
            try:
                val = my_data_iptable_dict['-d']
        
            except KeyError:
                val = 'INVALID'

            if  val != 'INVALID':
                if my_data_iptable_dict['-d']:
                    #if tblName == "iptables":
                    #   print "IPv4 Destination Address"
                    #elif tblName == "ip6tables":
                    #   print "IPv6 Destination Address"
                    #elif tblName == "ebtables":
                    #   print "Destination MAC Address"

                    acl_dst_add_present = True
                    acl_num_dst_add = val.count(',')+1

                    #print "Address  : ", my_data_iptable_dict['-d']
                    #if val.count(','):
                    #   print "%d Addresses", val.count(',')+1
                    #else:
                    #   print "Only Address"

            if log == True:
                if acl_ipv4mac == True:
                    print "IPv4MAC"
    
                if acl_ipv6 == True:
                    print "IPv6"

                if acl_mirror_present == True:
                    print "MIRROR"

                if acl_8021x_present == True:
                    print "8021X"

                if acl_filter == True:
                    print "Filter"
                else:
                    print "Mangle"

                if acl_num_chains_present:
                    print "Number of Chains : ", acl_num_chains_present

                if acl_input_present == True:
                    print "INPUT"
    
                if acl_forward_present == True:
                    print "FORWARD"
    
                if acl_output_present == True:
                    print "OUTPUT"

                if acl_in_intf_present == True:
                    print "Ingress Interface/s Present" 

                if acl_num_in_intfs:
                    print "Number of Ingress interfaces : ", acl_num_in_intfs

                if acl_out_intf_present == True:
                    print "Egress Interface/s Present"  

                if acl_num_out_intfs:
                    print "Number of Egress interfaces : ", acl_num_out_intfs

                if acl_src_add_present == True:
                    print "Source Address Present"

                if acl_num_src_add:
                    print "Number of Source Addresses : ", acl_num_src_add

                if acl_dst_add_present == True:
                    print "Destination Address Present"

                if acl_num_dst_add:
                    print "Number of Destination Addresses : ", acl_num_dst_add

                if acl_src_port_present == True:
                    print "Source Port Present"

                if acl_num_src_ports:
                    print "Number of source ports : ", acl_num_src_ports

                if acl_src_port_range_exists:
                    print "Source Port Range is present"

                if acl_dst_port_present == True:
                    print "Destination Port Present"

                if acl_num_dst_ports:
                    print "Number of destination ports : ", acl_num_dst_ports

                if acl_dst_port_range_exists:
                    print "Destination Port Range is present"

                if acl_setclass_action_present == True:
                    print "SETCLASS Action present"
                    if acl_num_child_police_rules:
                        print "Number of Child Police Action rules : ", acl_num_child_police_rules 

                if acl_police_action_present == True:
                    print "Police Action Rule Present"

        
            #if chains > 0 identify if it has output as well,
            #   *   OUTPUT will add to egress rules
            #   *   INPUT and FORWARD will account for ingress rules

            curr_total_num_rules = 0
            if acl_num_chains_present:
                if acl_output_present == True:
                    #ignore egress for now
                    if log == True:
                        print "Not checking Egress/Output chain currently"
                elif acl_input_present == True or acl_forward_present == True:
                    curr_num_rule = 1
                    curr_total_num_rules    =   curr_num_rule

                    if acl_num_chains_present:
                        curr_total_num_rules *= acl_num_chains_present
                
                    if acl_src_add_present:
                        if acl_num_src_add:
                            curr_total_num_rules *= acl_num_src_add

                    if acl_dst_add_present:
                        if acl_num_dst_add:
                            curr_total_num_rules *= acl_num_dst_add

                    if acl_in_intf_present and acl_out_intf_present:
                        if acl_num_in_intfs and acl_num_out_intfs:
                            curr_total_num_rules *= (acl_num_in_intfs * acl_num_out_intfs)
                    elif acl_in_intf_present and acl_num_in_intfs:
                        curr_total_num_rules *= acl_num_in_intfs
                    elif acl_out_intf_present and acl_num_out_intfs:
                        curr_total_num_rules *= acl_num_out_intfs

                    if acl_setclass_action_present == True:
                        if acl_num_child_police_rules:
                            curr_total_num_rules *= acl_num_child_police_rules
                        else:
                            curr_total_num_rules *= 1

                    if log == True:
                        print "Total Number of Rules : ", curr_total_num_rules
            
                num_rules[tblName] += curr_total_num_rules  

            curr_total_num_rules = 0
            if acl_num_chains_present:
                if acl_output_present == True:
                    #ignore output chain for now
                    if log == True:
                        print "Not checking Output chain currently"
                elif acl_input_present == True or acl_forward_present == True:
                    curr_num_rule = 1
                    curr_total_num_rules    =   curr_num_rule

                    if acl_num_chains_present:
                        curr_total_num_rules *= acl_num_chains_present
                
                    if acl_src_add_present:
                        if acl_num_src_add:
                            curr_total_num_rules *= acl_num_src_add

                    if acl_dst_add_present:
                        if acl_num_dst_add:
                            curr_total_num_rules *= acl_num_dst_add

                    if acl_in_intf_present and acl_out_intf_present:
                        if acl_num_in_intfs and acl_num_out_intfs:
                            curr_total_num_rules *= (acl_num_in_intfs * acl_num_out_intfs)
                    elif acl_in_intf_present and acl_num_in_intfs:
                        curr_total_num_rules *= acl_num_in_intfs
                    elif acl_out_intf_present and acl_num_out_intfs:
                        curr_total_num_rules *= acl_num_out_intfs

                    if acl_setclass_action_present == True:
                        if acl_num_child_police_rules:
                            #this is an exception
                            if is_control_plane_rules == True and acl_num_child_police_rules == 2 and acl_in_intf_present == True and acl_out_intf_present == False and acl_class_present == True and acl_class_value == 0:

                                curr_total_num_rules *= 1
                            else:
                                curr_total_num_rules *= acl_num_child_police_rules
                        else:
                            curr_total_num_rules *= 1

                    if log == True:
                        print "Total Number of Rules : ", curr_total_num_rules
            
                if acl_ipv4mac == True and acl_output_present == False and acl_filter == True and \
                    ((acl_out_intf_present == False) or (acl_out_intf_present == True and acl_in_intf_present == True)) :
                    num_rules["ingress"+"v4mac"+"filter"] += curr_total_num_rules   

                if acl_ipv4mac == True and acl_output_present == False and acl_filter == False and \
                    ((acl_out_intf_present == False) or (acl_out_intf_present == True and acl_in_intf_present == True)) :
                    num_rules["ingress"+"v4mac"+"mangle"] += curr_total_num_rules   

                if acl_ipv6 == True and acl_output_present == False and acl_filter == True and \
                    ((acl_out_intf_present == False) or (acl_out_intf_present == True and acl_in_intf_present == True)) :
                    num_rules["ingress"+"v6"+"filter"] += curr_total_num_rules  

                if acl_ipv6 == True and acl_output_present == False and acl_filter == False and \
                    ((acl_out_intf_present == False) or (acl_out_intf_present == True and acl_in_intf_present == True)) :
                    num_rules["ingress"+"v6"+"mangle"] += curr_total_num_rules  

                if acl_ipv4mac == True and acl_output_present == False and acl_filter == True and \
                    (((acl_out_intf_present == True and acl_in_intf_present == False) and (acl_input_present == True or acl_forward_present == True)) \
                    or (acl_setclass_action_present == True)):
                    num_rules["egress"+"v4mac"+"filter"] += curr_total_num_rules    

                if acl_ipv6 == True and acl_output_present == False and acl_filter == True and \
                    (acl_out_intf_present == True and acl_in_intf_present == False) :
                    #num_rules["egress"+"v6"+"filter"] += curr_total_num_rules   
                    num_rules["egress"+"v4mac"+"filter"] += curr_total_num_rules   
                    num_rules["ingress"+"v6"+"filter"] += curr_total_num_rules   

                if acl_mirror_present == True and acl_output_present == False and acl_filter == True and \
                    ((acl_out_intf_present == False) or (acl_out_intf_present == True and acl_in_intf_present == True)) :
                    num_rules["ingress"+"mirror"+"filter"] += curr_total_num_rules  

                if acl_8021x_present == True and acl_output_present == False and acl_filter == True and \
                    ((acl_out_intf_present == False) or (acl_out_intf_present == True and acl_in_intf_present == True)) :
                    num_rules["ingress"+"8021x"+"filter"] += curr_total_num_rules   


        if log == True:
            print "Num of Rules for ", tblName, " : ", num_rules[tblName]

    return num_rules
    fp.close()


def check_if_line_has_police_action (split_lines, split_line_itr, log = False):

    ret_val = False

    if split_line_itr >= len(split_lines):
        return ret_val
    
    split_line = split_lines[split_line_itr]

    if split_line:
        split_data = split_line.split()
        #print "Split Data : ", split_data
        split_data_len = len(split_data)
        my_data_iptable_dict = {}
        for split_data_itr in range (0,split_data_len/2):
            split_data_idx = 2*split_data_itr
            #print "Split Data Idx : ", split_data_idx
            my_data_iptable_dict[split_data[split_data_idx]] = split_data[split_data_idx+1]

        #print "My dictionary --> ", my_data_iptable_dict

        #Look up for action with -j option
        val = 'INVALID'
        try:
            val = my_data_iptable_dict['-j']
    
        except KeyError:
            val = 'INVALID'

        if  val != 'INVALID':
            if my_data_iptable_dict['-j'] == 'police' or my_data_iptable_dict['-j'] == 'POLICE':
                #print "Got POLICE"
                if log == True:
                    print "Found POLICE RULE --> ", split_line
                return True

    return False

def get_chip_info(log = False):
    cmd = "cat /cumulus/switchd/config/system_info"
    f   = os.popen(cmd)
    output = f.read()
    chip_info = json.loads(output)
    chip =  chip_info[0]['chip']

    if log == True:
        print "Found Chip ", chip 
        print "JSON --> ", chip_map_json[chip]
    chip_name_info = chip_map_json[chip]
    chip_name = chip_name_info['chip_name']

    if log == True:
        print "Chip Name : ",chip_name

    return chip_name

#Function to provide info if the platforms and their modes are supported or not
def is_check_supported():

    my_chip_name = get_chip_info()

    #trident2 with non atomic mode is not supported
    #if my_chip_name == "trident2":
    #    ret_val, non_atomic_update = read_non_update_atomic_update()
    #    if ret_val != True:
    #        print "cl-resource-est-tool | Could not read Non Atomic Update, Exiting"
    #        return False

    if my_chip_name == "trident3" or my_chip_name == "tomahawk" or my_chip_name == "tomahawkplus":
        return True;

    return False

def main(argv):

    inputfile       = ''
    path            = ''
    debug_log       = False
    parse_only      = False
    ver             =   "1.0"
    analysis_log    =   True
    

    try:
        opts, args = getopt.getopt(argv,"hvdoap:i:",["help","version","debug_log","parse_only","analysis_log","path","ifile"])
    except getopt.GetoptError:
        print 'Usage : cl-resource-est-tool.py -p <path_to_rules_files>'
        sys.exit(2)

    #print opts
    #print args
    for opt, arg in opts:
        if opt in ("-h", "--help"):
            print 'cl-resource-est-tool.py'
            print '==================='
            print 'Tool for verifying if enough HW TCAM resources are available to write the ACL Rules.'
            print 'Usage : cl-resource-est-tool.py -p <path_to_rules_files>'
            print 'path to rules files is mandatory. The tool will consider all files with extension .rules.'
            print 'The tool assumes that there is no entry written in the HW and all rules present in the directory would be the only ones to be written.'
            print 'This follows the same mechanism as used by cl-acltool.'
            print 'Options : '
            print '-p : Directory path to the *rules files'
            print '-o : To be used if only file parsing needs to be done. No analysis will be done.'
            print '-d : To be used for debug logs for the tool.'
            print '-i : Only 1 file parsing and analysis. With this option only 1 file can be analysed for verification.'
            print '-a : Analysis logs can be turned off by using this option'
            sys.exit()
        elif opt in ("-i", "--ifile"):
            inputfile = arg
            #print "input file -->",inputfile
        elif opt in ("-p", "--path"):
            path = arg
            #print "Path --> ",path
        elif opt in ("-d", "--debug_log"):
            debug_log = True
            #print "Got Debug LOG"
        elif opt in ("-o", "--parse_only"):
            parse_only = True
            print "Only Parsing"
        elif opt in ("-a", "--analysis_log"):
            analysis_log = False
        elif opt in ("-v", "--version"):
            print "cl-resource-est-tool.py"
            print "Version : ",ver
            return
 
    #Only one option of path and file is required 
    if path and inputfile:
        print "Both options cannot be used together. Use only one"
        return

    #Check if the chip is supported for this check/estimation 
    my_chip_name = get_chip_info()
    ret = is_check_supported()
    if ret == False:
        if debug_log == True:
            print "Check not supported on this platform."
        return

    #Below logic goes over *.rules files and estimates number of rules
    num_ip_rules = 0
    num_ip6_rules = 0
    num_eb_rules = 0
    total_num_ip_rules = 0
    total_num_ip6_rules = 0
    total_num_eb_rules = 0

    num_rules = {}
    
    total_num_rules = {}
    for direction in get_table_directions():
        for tbl_id in get_table_ids():
            for tbl_type in get_table_type(tbl_id, direction):
                total_num_rules[tbl_type+tbl_id+direction] = 0

    if path or inputfile:
        if path:
            if debug_log == True:
                print 'Path is ', path
            cmd = "ls "+path+"/*.rules"
            f = os.popen(cmd)
            output = f.read()
            if debug_log == True:
                print "Output --> ", output

        if inputfile:
            output = inputfile
            if debug_log == True:
                print "File Output --> ", output

        num_control_plane_files = 0
        ctrl_plane_file_name_1  =   "00control_plane.rules"
        ctrl_plane_file_name_2  =   "99control_plane_catch_all.rules"
        for split_line in output.splitlines():

            is_control_plane_rules = False

            if split_line:
                if ctrl_plane_file_name_1 in split_line or ctrl_plane_file_name_2 in split_line:
                    num_control_plane_files += 1
                    is_control_plane_rules = True
                else:
                    is_control_plane_rules = False

                num_rules = parse_file_and_get_num_rules(split_line, is_control_plane_rules,debug_log)
                if debug_log == True:
                    print "For File ", split_line, " : ", num_rules["iptables"], " for IPv4 | ", num_rules["ip6tables"], " for IPv6 | ", num_rules["ebtables"], " for Eb Found\n"
                    print "Ingress IPv4MAC Filter : ",num_rules["ingress"+"v4mac"+"filter"]
                    print "Ingress IPv6 Filter : ",num_rules["ingress"+"v6"+"filter"]
                    print "Ingress mirror Filter : ",num_rules["ingress"+"mirror"+"filter"]
                    print "Ingress 8021x Filter : ",num_rules["ingress"+"8021x"+"filter"]
                    print "Ingress IPv4MAC Mangle : ",num_rules["ingress"+"v4mac"+"mangle"]
                    print "Ingress IPv6 Mangle : ",num_rules["ingress"+"v6"+"mangle"]
                    print "Egress IPv4MAC Filter : ",num_rules["egress"+"v4mac"+"filter"]
                    print "Egress IPv6 Filter : ",num_rules["egress"+"v6"+"filter"]

                total_num_ip_rules += num_ip_rules
                total_num_ip6_rules += num_ip6_rules
                total_num_eb_rules += num_eb_rules

                total_num_rules["v4mac"+"filter"+"ingress"] += num_rules["ingress"+"v4mac"+"filter"]
                total_num_rules["v6"+"filter"+"ingress"] += num_rules["ingress"+"v6"+"filter"]
                total_num_rules["mirror"+"filter"+"ingress"] += num_rules["ingress"+"mirror"+"filter"]
                total_num_rules["8021x"+"filter"+"ingress"] += num_rules["ingress"+"8021x"+"filter"]
                total_num_rules["v4mac"+"mangle"+"ingress"] += num_rules["ingress"+"v4mac"+"mangle"]
                total_num_rules["v6"+"mangle"+"ingress"] += num_rules["ingress"+"v6"+"mangle"]
                total_num_rules["v4mac"+"filter"+"egress"] += num_rules["egress"+"v4mac"+"filter"]
                total_num_rules["v6"+"filter"+"egress"] += num_rules["egress"+"v6"+"filter"]


        #This is explicit handling to match with switchd. These rules are not present in rules files
        if num_control_plane_files:
            chip = get_chip_info()
            if chip:
                if chip == "trident2" or chip == "tomahawk" or chip == "tomahawkplus":
                    #reduce 2 from IPv4 MAC Filter
                    total_num_rules["v4mac"+"filter"+"ingress"] -= 2
                    #add 3 default rules to egress ipv4mac filter
                    total_num_rules["v4mac"+"filter"+"egress"] += 3
                elif chip == "trident3":
                    # add 5 to IPv4 MAC Filter
                    total_num_rules["v4mac"+"filter"+"ingress"] += 5
                    # add 11 to IPv6 Filter
                    total_num_rules["v6"+"filter"+"ingress"] += 11
                    #add 3 default rules to egress ipv4mac filter
                    total_num_rules["v4mac"+"filter"+"egress"] += 3
                    #reduce 1 ICMP rule from egress IPv4 mac filter
                    total_num_rules["v4mac"+"filter"+"egress"] -= 1

        if debug_log == True:
            print "Total IPv4MAC Rules  : ", total_num_ip_rules+total_num_eb_rules
            print "Total IPv6 Rules     : ",total_num_ip6_rules
            print "Total Ingress IPv4MAC Filter : ",total_num_rules["v4mac"+"filter"+"ingress"]
            print "Total Ingress IPv6 Filter : ",total_num_rules["v6"+"filter"+"ingress"]
            print "Total Ingress mirror Filter : ",total_num_rules["mirror"+"filter"+"ingress"]
            print "Total Ingress 8021x Filter : ",total_num_rules["8021x"+"filter"+"ingress"]
            print "Total Ingress IPv4MAC Mangle : ",total_num_rules["v4mac"+"mangle"+"ingress"]
            print "Total Ingress IPv6 Mangle : ",total_num_rules["v6"+"mangle"+"ingress"]
            print "Total Egress IPv4MAC Filter : ",total_num_rules["v4mac"+"filter"+"egress"]
            print "Total Egress IPv6 Filter : ",total_num_rules["v6"+"filter"+"egress"]



    if parse_only == True:
        print "Parsing of File Only"
        if debug_log == False:
            print "To check result of Parsing, rerun the cmd with -d option"
        return

    ret_val, non_atomic_update = read_non_update_atomic_update()
    if ret_val != True:
        print "Could not read Non Atomic Update, Exiting"
        return

    chip = get_chip_info()
    if chip:
        if chip == "trident2":
            my_chip = BCM_TRIDENT2(non_atomic_update)
        elif chip == "trident3":
            my_chip = BCM_TRIDENT3(non_atomic_update)
        elif chip == "tomahawk" or chip == "tomahawkplus":
            my_chip = BCM_TOMAHAWK(non_atomic_update)
        else:
            print "Error | Invalid Chip"
            return

        my_chip.get_chip_type()
        my_chip.get_atomic_mode()
        #display_slice_info(my_chip)
        #display_default_entry_info(my_chip)
        #display_default_table(my_chip)
        total_free_slices, free_256_slices, free_512_slices, free_768_slices, total_free_entries = my_chip.get_num_free_slices_entries_available("ingress")
        if debug_log == True:
            print "total Free slices    : ", total_free_slices
            print "Free 256 slices      : ", free_256_slices
            print "Free 512 slices      : ", free_512_slices
            print "Free 768 slices      : ", free_768_slices
            print "total Free entries   : ", total_free_entries
            num_entries = 2
            print "Checking how many slices reqd for ", num_entries
            num_total, num_256, num_512, num_768, num_entries = my_chip.get_num_slices_entries_reqd_for_entries(num_entries, "ingress", "v4mac", "filter")
            print "total slices     : ", num_total
            print "256 slices       : ", num_256
            print "512 slices       : ", num_512
            print "768 slices       : ", num_768
            print "total entries    : ", num_entries

            print "Fetching current tables"

        need_0_default_rules = False
        if path:
            need_0_default_rules = True
        else:
            need_0_default_rules = False
        my_chip.update_current_entries_table(need_0_default_rules)

        if debug_log == True:
            print "Current Table"
            display_current_table(my_chip)
        

        #check/compute if resources are available to handle available rules
        ret = check_if_res_avlbl(my_chip, total_num_rules, debug_log, analysis_log)
        if ret != True:
            print "****CANNOT APPLY THE RULES**** Not enough resources available"
            return False
        else:
            if debug_log == True:
                print "Rules can be applied successfully"
                print "Current Table"
            display_current_table(my_chip, True)
            return True
    
    return False

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

