[VOL-4627] Add COMBO pon support with configuration file

Change-Id: Ib7eec5640dfd5e5eb39e722ec7d2c8ee2b59f060
diff --git a/internal/bbsim/api/grpc_api_server.go b/internal/bbsim/api/grpc_api_server.go
index c7d7e38..2500cdd 100644
--- a/internal/bbsim/api/grpc_api_server.go
+++ b/internal/bbsim/api/grpc_api_server.go
@@ -100,6 +100,7 @@
 
 		p := bbsim.PONPort{
 			ID:                int32(pon.ID),
+			Technology:        pon.Technology.String(),
 			OperState:         pon.OperState.Current(),
 			InternalState:     pon.InternalState.Current(),
 			PacketCount:       pon.PacketCount,
diff --git a/internal/bbsim/devices/flows_test.go b/internal/bbsim/devices/flows_test.go
index 391b39e..512782a 100644
--- a/internal/bbsim/devices/flows_test.go
+++ b/internal/bbsim/devices/flows_test.go
@@ -42,6 +42,20 @@
 		},
 	}
 
+	allocIdPerOnu := uint32(common.Config.Olt.UniPorts * uint32(len(common.Services)))
+	common.PonsConfig = &common.PonPortsConfig{
+		Number: common.Config.Olt.PonPorts,
+		Ranges: []common.PonRangeConfig{
+			{
+				PonRange:     common.IdRange{StartId: 0, EndId: common.Config.Olt.PonPorts - 1},
+				Technology:   common.XGSPON.String(),
+				OnuRange:     common.IdRange{StartId: 1, EndId: 1 + (common.Config.Olt.OnusPonPort - 1)},
+				AllocIdRange: common.IdRange{StartId: 1024, EndId: 1024 + (common.Config.Olt.OnusPonPort * allocIdPerOnu)},
+				GemportRange: common.IdRange{StartId: 1024, EndId: 1024 + common.Config.Olt.OnusPonPort*allocIdPerOnu*8},
+			},
+		},
+	}
+
 	olt = CreateOLT(*common.Config, common.Services, true)
 
 	stream = &mockStream{
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index 0a50d59..c9068bd 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -51,14 +51,6 @@
 })
 
 const (
-	onuIdStart          = 1
-	allocIdStart        = 1024
-	gemPortIdPerAllocId = 8
-	gemportIdStart      = 1024
-	// The flow ids are no more necessary by the adapter, but still need to pass something dummy. Pass a very small valid range.
-	flowIdStart = 1
-	flowIdEnd   = flowIdStart + 1
-
 	//InternalState FSM states and transitions
 	OltInternalStateCreated     = "created"
 	OltInternalStateInitialized = "initialized"
@@ -108,12 +100,6 @@
 	OpenoltStream openolt.Openolt_EnableIndicationServer
 	enablePerf    bool
 
-	// resource ranges (only the ones that depends on the topology size)
-	onuIdEnd      uint32
-	allocIdPerOnu uint32
-	allocIdEnd    uint32
-	gemportIdEnd  uint32
-
 	// Allocated Resources
 	// this data are to verify that the openolt adapter does not duplicate resources
 	AllocIDsLock     sync.RWMutex
@@ -165,12 +151,6 @@
 		OmciResponseRate:    options.Olt.OmciResponseRate,
 	}
 
-	// create the resource ranges based on the configuration
-	olt.onuIdEnd = onuIdStart + (options.Olt.OnusPonPort - 1)                                               // we need one ONU ID available per ONU, but the smaller the range the smaller the pool created in the openolt adapter
-	olt.allocIdPerOnu = uint32(olt.NumUni * len(common.Services))                                           // 1 allocId per Service * UNI
-	olt.allocIdEnd = allocIdStart + (options.Olt.OnusPonPort * olt.allocIdPerOnu)                           // 1 allocId per Service * UNI * ONU
-	olt.gemportIdEnd = gemportIdStart + (options.Olt.OnusPonPort * olt.allocIdPerOnu * gemPortIdPerAllocId) // up to 8 gemport-id per tcont/alloc-id
-
 	if val, ok := ControlledActivationModes[options.BBSim.ControlledActivation]; ok {
 		olt.ControlledActivation = val
 	} else {
@@ -218,14 +198,38 @@
 
 	// create PON ports
 	for i := 0; i < olt.NumPon; i++ {
+		ponConf, err := common.GetPonConfigById(uint32(i))
+		if err != nil {
+			oltLogger.WithFields(log.Fields{
+				"Err":    err,
+				"IntfId": i,
+			}).Fatal("cannot-get-pon-configuration")
+		}
+
+		tech, err := common.PonTechnologyFromString(ponConf.Technology)
+		if err != nil {
+			oltLogger.WithFields(log.Fields{
+				"Err":    err,
+				"IntfId": i,
+			}).Fatal("unkown-pon-port-technology")
+		}
 
 		// initialize the resource maps for every PON Ports
 		olt.AllocIDs[uint32(i)] = make(map[uint32]map[uint32]map[int32]map[uint64]bool)
 		olt.GemPortIDs[uint32(i)] = make(map[uint32]map[uint32]map[int32]map[uint64]bool)
 
-		p := CreatePonPort(&olt, uint32(i))
+		p := CreatePonPort(&olt, uint32(i), tech)
 
 		// create ONU devices
+		if (ponConf.OnuRange.EndId - ponConf.OnuRange.StartId + 1) < uint32(olt.NumOnuPerPon) {
+			oltLogger.WithFields(log.Fields{
+				"OnuRange":     ponConf.OnuRange,
+				"RangeSize":    ponConf.OnuRange.EndId - ponConf.OnuRange.StartId + 1,
+				"NumOnuPerPon": olt.NumOnuPerPon,
+				"IntfId":       i,
+			}).Fatal("onus-per-pon-bigger-than-resource-range-size")
+		}
+
 		for j := 0; j < olt.NumOnuPerPon; j++ {
 			delay := time.Duration(olt.Delay*j) * time.Millisecond
 			o := CreateONU(&olt, p, uint32(j+1), delay, nextCtag, nextStag, isMock)
@@ -1278,62 +1282,48 @@
 }
 
 func (o *OltDevice) GetDeviceInfo(context.Context, *openolt.Empty) (*openolt.DeviceInfo, error) {
-
-	intfIDs := []uint32{}
-	for i := 0; i < o.NumPon; i++ {
-		intfIDs = append(intfIDs, uint32(i))
-	}
-
 	devinfo := &openolt.DeviceInfo{
 		Vendor:              common.Config.Olt.Vendor,
 		Model:               common.Config.Olt.Model,
 		HardwareVersion:     common.Config.Olt.HardwareVersion,
 		FirmwareVersion:     common.Config.Olt.FirmwareVersion,
-		Technology:          common.Config.Olt.Technology,
 		PonPorts:            uint32(o.NumPon),
-		OnuIdStart:          onuIdStart,
-		OnuIdEnd:            o.onuIdEnd,
-		AllocIdStart:        allocIdStart,
-		AllocIdEnd:          o.allocIdEnd,
-		GemportIdStart:      gemportIdStart,
-		GemportIdEnd:        o.gemportIdEnd,
-		FlowIdStart:         flowIdStart,
-		FlowIdEnd:           flowIdEnd,
 		DeviceSerialNumber:  o.SerialNumber,
 		DeviceId:            common.Config.Olt.DeviceId,
 		PreviouslyConnected: o.PreviouslyConnected,
-		Ranges: []*openolt.DeviceInfo_DeviceResourceRanges{
-			{
-				IntfIds:    intfIDs,
-				Technology: common.Config.Olt.Technology,
-				Pools: []*openolt.DeviceInfo_DeviceResourceRanges_Pool{
-					{
-						Type:    openolt.DeviceInfo_DeviceResourceRanges_Pool_ONU_ID,
-						Sharing: openolt.DeviceInfo_DeviceResourceRanges_Pool_DEDICATED_PER_INTF,
-						Start:   onuIdStart,
-						End:     o.onuIdEnd,
-					},
-					{
-						Type:    openolt.DeviceInfo_DeviceResourceRanges_Pool_ALLOC_ID,
-						Sharing: openolt.DeviceInfo_DeviceResourceRanges_Pool_DEDICATED_PER_INTF,
-						Start:   allocIdStart,
-						End:     o.allocIdEnd,
-					},
-					{
-						Type:    openolt.DeviceInfo_DeviceResourceRanges_Pool_GEMPORT_ID,
-						Sharing: openolt.DeviceInfo_DeviceResourceRanges_Pool_DEDICATED_PER_INTF,
-						Start:   gemportIdStart,
-						End:     o.gemportIdEnd,
-					},
-					{
-						Type:    openolt.DeviceInfo_DeviceResourceRanges_Pool_FLOW_ID,
-						Sharing: openolt.DeviceInfo_DeviceResourceRanges_Pool_SHARED_BY_ALL_INTF_ALL_TECH,
-						Start:   flowIdStart,
-						End:     flowIdEnd,
-					},
+		Ranges:              []*openolt.DeviceInfo_DeviceResourceRanges{},
+	}
+
+	for _, resRange := range common.PonsConfig.Ranges {
+		intfIDs := []uint32{}
+		for i := resRange.PonRange.StartId; i <= resRange.PonRange.EndId; i++ {
+			intfIDs = append(intfIDs, uint32(i))
+		}
+
+		devinfo.Ranges = append(devinfo.Ranges, &openolt.DeviceInfo_DeviceResourceRanges{
+			IntfIds:    intfIDs,
+			Technology: resRange.Technology,
+			Pools: []*openolt.DeviceInfo_DeviceResourceRanges_Pool{
+				{
+					Type:    openolt.DeviceInfo_DeviceResourceRanges_Pool_ONU_ID,
+					Sharing: openolt.DeviceInfo_DeviceResourceRanges_Pool_DEDICATED_PER_INTF,
+					Start:   resRange.OnuRange.StartId,
+					End:     resRange.OnuRange.EndId,
+				},
+				{
+					Type:    openolt.DeviceInfo_DeviceResourceRanges_Pool_ALLOC_ID,
+					Sharing: openolt.DeviceInfo_DeviceResourceRanges_Pool_DEDICATED_PER_INTF,
+					Start:   resRange.AllocIdRange.StartId,
+					End:     resRange.AllocIdRange.EndId,
+				},
+				{
+					Type:    openolt.DeviceInfo_DeviceResourceRanges_Pool_GEMPORT_ID,
+					Sharing: openolt.DeviceInfo_DeviceResourceRanges_Pool_DEDICATED_PER_INTF,
+					Start:   resRange.GemportRange.StartId,
+					End:     resRange.GemportRange.EndId,
 				},
 			},
-		},
+		})
 	}
 
 	oltLogger.WithFields(log.Fields{
@@ -1341,16 +1331,7 @@
 		"Model":               devinfo.Model,
 		"HardwareVersion":     devinfo.HardwareVersion,
 		"FirmwareVersion":     devinfo.FirmwareVersion,
-		"Technology":          devinfo.Technology,
 		"PonPorts":            devinfo.PonPorts,
-		"OnuIdStart":          devinfo.OnuIdStart,
-		"OnuIdEnd":            devinfo.OnuIdEnd,
-		"AllocIdStart":        devinfo.AllocIdStart,
-		"AllocIdEnd":          devinfo.AllocIdEnd,
-		"GemportIdStart":      devinfo.GemportIdStart,
-		"GemportIdEnd":        devinfo.GemportIdEnd,
-		"FlowIdStart":         devinfo.FlowIdStart,
-		"FlowIdEnd":           devinfo.FlowIdEnd,
 		"DeviceSerialNumber":  devinfo.DeviceSerialNumber,
 		"DeviceId":            devinfo.DeviceId,
 		"PreviouslyConnected": devinfo.PreviouslyConnected,
diff --git a/internal/bbsim/devices/olt_test.go b/internal/bbsim/devices/olt_test.go
index e442d54..ee80e05 100644
--- a/internal/bbsim/devices/olt_test.go
+++ b/internal/bbsim/devices/olt_test.go
@@ -19,15 +19,16 @@
 import (
 	"context"
 	"fmt"
+	"net"
+	"sync"
+	"testing"
+
 	"github.com/looplab/fsm"
 	"github.com/opencord/bbsim/internal/bbsim/types"
 	bbsim "github.com/opencord/bbsim/internal/bbsim/types"
 	"github.com/opencord/bbsim/internal/common"
 	"github.com/opencord/voltha-protos/v5/go/openolt"
 	"github.com/stretchr/testify/assert"
-	"net"
-	"sync"
-	"testing"
 )
 
 func createMockOlt(numPon int, numOnu int, numUni int, services []ServiceIf) *OltDevice {
@@ -111,6 +112,20 @@
 		},
 	}
 
+	allocIdPerOnu := uint32(common.Config.Olt.UniPorts * uint32(len(common.Services)))
+	common.PonsConfig = &common.PonPortsConfig{
+		Number: common.Config.Olt.PonPorts,
+		Ranges: []common.PonRangeConfig{
+			{
+				PonRange:     common.IdRange{StartId: 0, EndId: common.Config.Olt.PonPorts - 1},
+				Technology:   common.XGSPON.String(),
+				OnuRange:     common.IdRange{StartId: 1, EndId: 1 + (common.Config.Olt.OnusPonPort - 1)},
+				AllocIdRange: common.IdRange{StartId: 1024, EndId: 1024 + (common.Config.Olt.OnusPonPort * allocIdPerOnu)},
+				GemportRange: common.IdRange{StartId: 1024, EndId: 1024 + common.Config.Olt.OnusPonPort*allocIdPerOnu*8},
+			},
+		},
+	}
+
 	olt := CreateOLT(*common.Config, common.Services, true)
 
 	assert.Equal(t, len(olt.Pons), int(common.Config.Olt.PonPorts))
@@ -191,21 +206,96 @@
 		},
 	}
 
+	common.PonsConfig = &common.PonPortsConfig{
+		Number: 16,
+		Ranges: []common.PonRangeConfig{
+			{
+				PonRange:     common.IdRange{StartId: 0, EndId: 1},
+				Technology:   common.XGSPON.String(),
+				OnuRange:     common.IdRange{StartId: 1, EndId: 4},
+				AllocIdRange: common.IdRange{StartId: 1024, EndId: 1024 + 4},
+				GemportRange: common.IdRange{StartId: 1024, EndId: 1024 + 32},
+			},
+			{
+				PonRange:     common.IdRange{StartId: 2, EndId: 2},
+				Technology:   common.GPON.String(),
+				OnuRange:     common.IdRange{StartId: 1, EndId: 4},
+				AllocIdRange: common.IdRange{StartId: 1024, EndId: 1028 + 4},
+				GemportRange: common.IdRange{StartId: 1024, EndId: 1024 + 32},
+			},
+			{
+				PonRange:     common.IdRange{StartId: 3, EndId: 3},
+				Technology:   common.XGSPON.String(),
+				OnuRange:     common.IdRange{StartId: 1, EndId: 4},
+				AllocIdRange: common.IdRange{StartId: 1024, EndId: 1028 + 4},
+				GemportRange: common.IdRange{StartId: 1024, EndId: 1024 + 32},
+			},
+			{
+				PonRange:     common.IdRange{StartId: 4, EndId: 6},
+				Technology:   common.XGSPON.String(),
+				OnuRange:     common.IdRange{StartId: 1, EndId: 4},
+				AllocIdRange: common.IdRange{StartId: 1024, EndId: 1028 + 4},
+				GemportRange: common.IdRange{StartId: 1024, EndId: 1024 + 32},
+			},
+			{
+				PonRange:     common.IdRange{StartId: 7, EndId: 8},
+				Technology:   common.XGSPON.String(),
+				OnuRange:     common.IdRange{StartId: 1, EndId: 4},
+				AllocIdRange: common.IdRange{StartId: 1024, EndId: 1028 + 4},
+				GemportRange: common.IdRange{StartId: 1024, EndId: 1024 + 32},
+			},
+			{
+				PonRange:     common.IdRange{StartId: 9, EndId: 9},
+				Technology:   common.GPON.String(),
+				OnuRange:     common.IdRange{StartId: 1, EndId: 4},
+				AllocIdRange: common.IdRange{StartId: 1024, EndId: 1028 + 4},
+				GemportRange: common.IdRange{StartId: 1024, EndId: 1024 + 32},
+			},
+			{
+				PonRange:     common.IdRange{StartId: 10, EndId: 10},
+				Technology:   common.GPON.String(),
+				OnuRange:     common.IdRange{StartId: 1, EndId: 4},
+				AllocIdRange: common.IdRange{StartId: 1024, EndId: 1028 + 4},
+				GemportRange: common.IdRange{StartId: 1024, EndId: 1024 + 32},
+			},
+			{
+				PonRange:     common.IdRange{StartId: 11, EndId: 15},
+				Technology:   common.XGSPON.String(),
+				OnuRange:     common.IdRange{StartId: 1, EndId: 4},
+				AllocIdRange: common.IdRange{StartId: 1024, EndId: 1028 + 4},
+				GemportRange: common.IdRange{StartId: 1024, EndId: 1024 + 32},
+			},
+		},
+	}
+
 	olt := CreateOLT(*common.Config, common.Services, true)
 
 	res, err := olt.GetDeviceInfo(context.Background(), &openolt.Empty{})
 
 	assert.NoError(t, err, "GetDeviceInfo returned error")
 
-	fmt.Println(res)
-	fmt.Println(res.OnuIdEnd - res.OnuIdStart + 1)
+	assert.Equal(t, len(res.Ranges), len(common.PonsConfig.Ranges))
 
-	assert.Equal(t, onuIdStart+(onusPerPon-1), res.OnuIdEnd)
-	assert.Equal(t, onuIdStart+(onusPerPon-1), res.Ranges[0].Pools[0].End)
-	assert.Equal(t, onusPerPon, res.OnuIdEnd-res.OnuIdStart+1, "The ONU ID range size is different that the number of ONUs per PON")
+	for _, resRange := range res.Ranges {
+		for _, ponId := range resRange.IntfIds {
+			conf, err := common.GetPonConfigById(ponId)
+			assert.NoError(t, err, fmt.Sprintf("Cannot get pon configuration by id %d", ponId))
 
-	assert.Equal(t, uint32(allocIdStart+(olt.NumOnuPerPon*olt.NumUni*len(common.Services))), res.AllocIdEnd)
-	assert.Equal(t, uint32(gemportIdStart+(olt.NumOnuPerPon*olt.NumUni*len(common.Services))*gemPortIdPerAllocId), res.GemportIdEnd)
+			for _, pool := range resRange.Pools {
+				switch pool.Type {
+				case openolt.DeviceInfo_DeviceResourceRanges_Pool_ONU_ID:
+					assert.Equal(t, pool.Start, conf.OnuRange.StartId)
+					assert.Equal(t, pool.End, conf.OnuRange.EndId)
+				case openolt.DeviceInfo_DeviceResourceRanges_Pool_ALLOC_ID:
+					assert.Equal(t, pool.Start, conf.AllocIdRange.StartId)
+					assert.Equal(t, pool.End, conf.AllocIdRange.EndId)
+				case openolt.DeviceInfo_DeviceResourceRanges_Pool_GEMPORT_ID:
+					assert.Equal(t, pool.Start, conf.GemportRange.StartId)
+					assert.Equal(t, pool.End, conf.GemportRange.EndId)
+				}
+			}
+		}
+	}
 }
 
 func Test_Olt_FindOnuBySn_Success(t *testing.T) {
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index e6e4e58..b99d918 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -356,7 +356,7 @@
 		o.PotsPorts = append(o.PotsPorts, pots)
 	}
 
-	mibDb, err := omcilib.GenerateMibDatabase(len(o.UniPorts), len(o.PotsPorts))
+	mibDb, err := omcilib.GenerateMibDatabase(len(o.UniPorts), len(o.PotsPorts), o.PonPort.Technology)
 	if err != nil {
 		onuLogger.WithFields(log.Fields{
 			"OnuId":  o.ID,
diff --git a/internal/bbsim/devices/onu_test_helpers.go b/internal/bbsim/devices/onu_test_helpers.go
index ba04b57..287b3a9 100644
--- a/internal/bbsim/devices/onu_test_helpers.go
+++ b/internal/bbsim/devices/onu_test_helpers.go
@@ -19,11 +19,12 @@
 import (
 	"context"
 	"errors"
+	"time"
+
 	bbsim_common "github.com/opencord/bbsim/internal/common"
 	omcilib "github.com/opencord/bbsim/internal/common/omci"
 	"github.com/opencord/voltha-protos/v5/go/extension"
 	log "github.com/sirupsen/logrus"
-	"time"
 
 	"github.com/opencord/bbsim/internal/bbsim/types"
 	"github.com/opencord/voltha-protos/v5/go/ext/config"
@@ -183,7 +184,7 @@
 		OmciResponseRate: 10,
 	}
 
-	pon := CreatePonPort(&olt, 1)
+	pon := CreatePonPort(&olt, 1, bbsim_common.XGSPON)
 
 	onu := CreateONU(&olt, pon, 1, time.Duration(1*time.Millisecond), nextCtag, nextStag, true)
 	// NOTE we need this in order to create the OnuChannel
diff --git a/internal/bbsim/devices/pon.go b/internal/bbsim/devices/pon.go
index c1b3257..068a250 100644
--- a/internal/bbsim/devices/pon.go
+++ b/internal/bbsim/devices/pon.go
@@ -22,6 +22,7 @@
 	"sync"
 
 	"github.com/looplab/fsm"
+	"github.com/opencord/bbsim/internal/common"
 	"github.com/opencord/voltha-protos/v5/go/openolt"
 	log "github.com/sirupsen/logrus"
 )
@@ -44,6 +45,7 @@
 type PonPort struct {
 	// BBSIM Internals
 	ID            uint32
+	Technology    common.PonTechnology
 	NumOnu        int
 	Onus          []*Onu
 	Olt           *OltDevice
@@ -66,11 +68,11 @@
 }
 
 // CreatePonPort creates pon port object
-func CreatePonPort(olt *OltDevice, id uint32) *PonPort {
-
+func CreatePonPort(olt *OltDevice, id uint32, tech common.PonTechnology) *PonPort {
 	ponPort := PonPort{
 		NumOnu:            olt.NumOnuPerPon,
 		ID:                id,
+		Technology:        tech,
 		Type:              "pon",
 		Olt:               olt,
 		Onus:              []*Onu{},
diff --git a/internal/bbsim/dmiserver/dmi_hw_mgmt.go b/internal/bbsim/dmiserver/dmi_hw_mgmt.go
index 848ac21..9f4a6ee 100755
--- a/internal/bbsim/dmiserver/dmi_hw_mgmt.go
+++ b/internal/bbsim/dmiserver/dmi_hw_mgmt.go
@@ -87,7 +87,7 @@
 		trans := dmi.Component{
 			Name:        transName,
 			Class:       dmi.ComponentType_COMPONENT_TYPE_TRANSCEIVER,
-			Description: "XGS-PON",
+			Description: olt.Pons[i].Technology.String(),
 			Uuid: &dmi.Uuid{
 				Uuid: dms.ponTransceiverUuids[i],
 			},
diff --git a/internal/bbsimctl/commands/olt.go b/internal/bbsimctl/commands/olt.go
index 1789c86..d2e72a5 100644
--- a/internal/bbsimctl/commands/olt.go
+++ b/internal/bbsimctl/commands/olt.go
@@ -36,7 +36,7 @@
 	DEFAULT_OLT_DEVICE_HEADER_FORMAT    = "table{{ .ID }}\t{{ .SerialNumber }}\t{{ .OperState }}\t{{ .InternalState }}\t{{ .IP }}"
 	DEFAULT_OLT_RESOURCES_HEADER_FORMAT = "table{{ .Type }}\t{{ .PonPortId }}\t{{ .OnuId }}\t{{ .PortNo }}\t{{ .ResourceId }}\t{{ .FlowId }}"
 	DEFAULT_NNI_PORT_HEADER_FORMAT      = "table{{ .ID }}\t{{ .OperState }}\t{{ .InternalState }}\t{{ .PacketCount }}"
-	DEFAULT_PON_PORT_HEADER_FORMAT      = "table{{ .ID }}\t{{ .OperState }}\t{{ .InternalState }}\t{{ .PacketCount }}\t{{ .AllocatedOnuIds }}\t{{ .AllocatedGemPorts }}\t{{ .AllocatedAllocIds }}"
+	DEFAULT_PON_PORT_HEADER_FORMAT      = "table{{ .ID }}\t{{ .Technology }}\t{{ .OperState }}\t{{ .InternalState }}\t{{ .PacketCount }}\t{{ .AllocatedOnuIds }}\t{{ .AllocatedGemPorts }}\t{{ .AllocatedAllocIds }}"
 )
 
 type OltGet struct{}
diff --git a/internal/common/omci/onu_mib_db.go b/internal/common/omci/onu_mib_db.go
index e9fabed..2902fd2 100644
--- a/internal/common/omci/onu_mib_db.go
+++ b/internal/common/omci/onu_mib_db.go
@@ -21,6 +21,7 @@
 	"encoding/binary"
 	"encoding/hex"
 
+	"github.com/opencord/bbsim/internal/common"
 	me "github.com/opencord/omci-lib-go/v2/generated"
 )
 
@@ -70,6 +71,7 @@
 	cardHolderOnuType byte = 0x01 // ONU is a single piece of integrated equipment
 	ethernetUnitType  byte = 0x2f // Ethernet BASE-T
 	xgsPonUnitType    byte = 0xee // XG-PON10G10
+	gPonUnitType      byte = 0xf5 // GPON12441244
 	potsUnitType      byte = 0x20 // POTS
 	cardHolderSlotID  byte = 0x01
 	tcontSlotId       byte = 0x80 // why is this not the same as the cardHolderSlotID, it does not point to anything
@@ -92,7 +94,7 @@
 
 // creates a MIB database for a ONU
 // CircuitPack and CardHolder are static, everything else can be configured
-func GenerateMibDatabase(ethUniPortCount int, potsUniPortCount int) (*MibDb, error) {
+func GenerateMibDatabase(ethUniPortCount int, potsUniPortCount int, technology common.PonTechnology) (*MibDb, error) {
 
 	mibDb := MibDb{
 		items: []MibDbEntry{},
@@ -116,12 +118,21 @@
 	//	},
 	//})
 
-	// circuitPack XG-PON10G10
+	// ANI circuitPack
+	var aniCPType byte
+
+	switch technology {
+	case common.XGSPON:
+		aniCPType = xgsPonUnitType
+	case common.GPON:
+		aniCPType = gPonUnitType
+	}
+
 	mibDb.items = append(mibDb.items, MibDbEntry{
 		me.CircuitPackClassID,
 		circuitPackEntityID,
 		me.AttributeValueMap{
-			me.CircuitPack_Type:          xgsPonUnitType,
+			me.CircuitPack_Type:          aniCPType,
 			me.CircuitPack_NumberOfPorts: 1, // NOTE is this the ANI port? must be
 			me.CircuitPack_SerialNumber:  ToOctets("BBSM-Circuit-Pack-ani", 20),
 			me.CircuitPack_Version:       ToOctets("v0.0.1", 20),
diff --git a/internal/common/omci/onu_mib_db_test.go b/internal/common/omci/onu_mib_db_test.go
index 378ac6e..2794aa4 100644
--- a/internal/common/omci/onu_mib_db_test.go
+++ b/internal/common/omci/onu_mib_db_test.go
@@ -19,6 +19,7 @@
 import (
 	"testing"
 
+	"github.com/opencord/bbsim/internal/common"
 	"github.com/opencord/omci-lib-go/v2"
 	me "github.com/opencord/omci-lib-go/v2/generated"
 	"github.com/stretchr/testify/assert"
@@ -56,7 +57,7 @@
 
 func Test_GenerateMibDatabase(t *testing.T) {
 	const uniPortCount = 4
-	mibDb, err := GenerateMibDatabase(uniPortCount, 0)
+	mibDb, err := GenerateMibDatabase(uniPortCount, 0, common.XGSPON)
 
 	expectedItems := 9                     //ONU-G + 2 Circuit Packs (4 messages each)
 	expectedItems += 2 * uniPortCount      // 1 PPTP and 1 UniG per UNI
@@ -94,7 +95,7 @@
 func Test_GenerateMibDatabase_withPots(t *testing.T) {
 	const uniPortCount = 4
 	const potsPortCount = 1
-	mibDb, err := GenerateMibDatabase(uniPortCount, potsPortCount)
+	mibDb, err := GenerateMibDatabase(uniPortCount, potsPortCount, common.XGSPON)
 
 	expectedItems := 13                                      //ONU-G + 3 Circuit Packs (4 messages each)
 	expectedItems += 2 * (uniPortCount + potsPortCount)      // 1 PPTP and 1 UniG per UNI
diff --git a/internal/common/option_test.go b/internal/common/option_test.go
index 2a57624..bf8d139 100644
--- a/internal/common/option_test.go
+++ b/internal/common/option_test.go
@@ -17,8 +17,11 @@
 package common
 
 import (
-	"gotest.tools/assert"
+	"fmt"
 	"testing"
+
+	"github.com/imdario/mergo"
+	"gotest.tools/assert"
 )
 
 func TestLoadBBSimServices(t *testing.T) {
@@ -36,3 +39,186 @@
 	assert.Equal(t, services[0].NeedsIgmp, false)
 	assert.Equal(t, services[0].TechnologyProfileID, 64)
 }
+
+func TestLoadPonsConfigDefaults(t *testing.T) {
+	Config = getDefaultOps()
+	// The default options define 1 PON per OLT
+	// and 1 ONU per PON
+
+	Services = []ServiceYaml{
+		{
+			Name: "test",
+		},
+	}
+
+	ponsConf, err := getDefaultPonsConfig()
+
+	assert.NilError(t, err, "Can't get defaults")
+
+	assert.Equal(t, ponsConf.Number, uint32(1))
+	assert.Equal(t, len(ponsConf.Ranges), 1)
+
+	ranges := ponsConf.Ranges
+
+	//The default should replicate the old way bbsim used to compute ranges
+	assert.Equal(t, ranges[0].PonRange, IdRange{0, 0})
+	assert.Equal(t, ranges[0].Technology, XGSPON.String())
+	assert.Equal(t, ranges[0].OnuRange, IdRange{defaultOnuIdStart, defaultOnuIdStart})
+	assert.Equal(t, ranges[0].AllocIdRange, IdRange{defaultAllocIdStart, defaultAllocIdStart + 4})
+	assert.Equal(t, ranges[0].GemportRange, IdRange{defaultGemportIdStart, defaultGemportIdStart + 32})
+
+	assert.NilError(t, validatePonsConfig(ponsConf), "Configuration is not valid")
+}
+
+func TestLoadPonsConfigFile(t *testing.T) {
+
+	Config = getDefaultOps()
+
+	Services = []ServiceYaml{
+		{
+			Name: "test",
+		},
+	}
+
+	ponsConf, err := getDefaultPonsConfig()
+
+	assert.NilError(t, err, "Can't get defaults")
+
+	yamlConf, err := loadBBSimPons("../../configs/pon-interfaces.yaml")
+
+	assert.NilError(t, err, "Can't read config file")
+
+	// merging Yaml and Default Values
+	err = mergo.Merge(ponsConf, yamlConf, mergo.WithOverride)
+	assert.NilError(t, err, "Can't merge YAML and Config")
+
+	assert.Equal(t, ponsConf.Number, uint32(16))
+	assert.Equal(t, len(ponsConf.Ranges), 1)
+
+	ranges := ponsConf.Ranges
+
+	assert.Equal(t, ranges[0].PonRange, IdRange{0, 15})
+	assert.Equal(t, ranges[0].Technology, XGSPON.String())
+	assert.Equal(t, ranges[0].OnuRange, IdRange{1, 255})
+	assert.Equal(t, ranges[0].AllocIdRange, IdRange{256, 1024})
+	assert.Equal(t, ranges[0].GemportRange, IdRange{256, 1024})
+
+	assert.NilError(t, validatePonsConfig(ponsConf), "Configuration is not valid")
+}
+
+func getTestPonsConfiguration() *PonPortsConfig {
+	return &PonPortsConfig{
+		Number: 16,
+		Ranges: []PonRangeConfig{
+			{
+				PonRange:     IdRange{0, 7},
+				Technology:   GPON.String(),
+				OnuRange:     IdRange{defaultOnuIdStart, defaultOnuIdStart},
+				AllocIdRange: IdRange{defaultAllocIdStart, defaultAllocIdStart + 4},
+				GemportRange: IdRange{defaultGemportIdStart, defaultGemportIdStart + 32},
+			},
+			{
+				PonRange:     IdRange{8, 15},
+				Technology:   XGSPON.String(),
+				OnuRange:     IdRange{defaultOnuIdStart, defaultOnuIdStart},
+				AllocIdRange: IdRange{defaultAllocIdStart, defaultAllocIdStart + 4},
+				GemportRange: IdRange{defaultGemportIdStart, defaultGemportIdStart + 32},
+			},
+		},
+	}
+}
+
+func TestPonsValidationTechnology(t *testing.T) {
+	ponsConf := getTestPonsConfiguration()
+	assert.NilError(t, validatePonsConfig(ponsConf), "Test configuration is not valid")
+
+	ponsConf.Ranges[0].Technology = XGSPON.String()
+	assert.NilError(t, validatePonsConfig(ponsConf), "Correct technology considered invalid")
+
+	ponsConf.Ranges[0].Technology = GPON.String()
+	assert.NilError(t, validatePonsConfig(ponsConf), "Correct technology considered invalid")
+
+	ponsConf.Ranges[0].Technology = "TEST"
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "technology", "Incorrect technology considered valid")
+}
+
+func TestPonsValidationPortsInRanges(t *testing.T) {
+	ponsConf := getTestPonsConfiguration()
+	assert.NilError(t, validatePonsConfig(ponsConf), "Test configuration is not valid")
+
+	//The second range now misses pon 8
+	ponsConf.Ranges[1].PonRange.StartId = 9
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "not-defined", "Missing pon definition considered valid")
+
+	//The second range defines pon 7 a second time
+	ponsConf.Ranges[1].PonRange.StartId = 7
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "duplicate", "Duplicate pon definition considered valid")
+
+	//Get back to a known good configuration
+	ponsConf = getTestPonsConfiguration()
+	//The second range uses an Id that is out of range
+	ponsConf.Ranges[1].PonRange.EndId = 16
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "max", "Out of range pon definition considered valid")
+
+	//Fix the start of the second range
+	ponsConf.Ranges[1].PonRange.EndId = 15
+
+	ponsConf.Number = 0
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "no-pon-ports", "Zero pons considered valid")
+}
+
+func TestPonsValidationRangeLimits(t *testing.T) {
+	ponsConf := getTestPonsConfiguration()
+	assert.NilError(t, validatePonsConfig(ponsConf), "Test configuration is not valid")
+
+	ponsConf.Ranges[0].PonRange = IdRange{0, 0}
+	ponsConf.Ranges[1].PonRange = IdRange{1, 15}
+
+	assert.NilError(t, validatePonsConfig(ponsConf), "Single pon range considered invalid")
+
+	//Get back to a known good configuration
+	ponsConf = getTestPonsConfiguration()
+	ponsConf.Ranges[0].PonRange = IdRange{5, 4}
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "limits", "Invalid pons range limits considered valid")
+
+	ponsConf = getTestPonsConfiguration()
+	ponsConf.Ranges[0].OnuRange = IdRange{5, 4}
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "limits", "Invalid onus range limits considered valid")
+
+	ponsConf = getTestPonsConfiguration()
+	ponsConf.Ranges[0].AllocIdRange = IdRange{5, 4}
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "limits", "Invalid alloc-ids range limits considered valid")
+
+	ponsConf = getTestPonsConfiguration()
+	ponsConf.Ranges[0].GemportRange = IdRange{5, 4}
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "limits", "Invalid gemports range limits considered valid")
+}
+
+func TestGetPonConfigById(t *testing.T) {
+	PonsConfig = getTestPonsConfiguration()
+
+	for id := uint32(0); id < PonsConfig.Number-1; id++ {
+		conf, err := GetPonConfigById(id)
+		assert.NilError(t, err, fmt.Sprintf("Cannot get configuration for pon %d", id))
+
+		if id > conf.PonRange.EndId || id < conf.PonRange.StartId {
+			assert.NilError(t, err, fmt.Sprintf("Got wrong configuration for pon %d", id))
+		}
+	}
+
+	_, err := GetPonConfigById(16)
+
+	assert.Assert(t, err != nil, "Invalid pon id returned configuration")
+
+	_, err = GetPonConfigById(100)
+
+	assert.Assert(t, err != nil, "Invalid pon id returned configuration")
+}
diff --git a/internal/common/options.go b/internal/common/options.go
index 000a8dc..58cec20 100644
--- a/internal/common/options.go
+++ b/internal/common/options.go
@@ -88,7 +88,6 @@
 	NniPorts           uint32 `yaml:"nni_ports"`
 	NniSpeed           uint32 `yaml:"nni_speed"`
 	OnusPonPort        uint32 `yaml:"onus_per_port"`
-	Technology         string `yaml:"technology"`
 	ID                 int    `yaml:"id"`
 	OltRebootDelay     int    `yaml:"reboot_delay"`
 	PortStatsInterval  int    `yaml:"port_stats_interval"`
@@ -97,9 +96,77 @@
 	PotsPorts          uint32 `yaml:"pots_ports"`
 }
 
+type PonPortsConfig struct {
+	Number uint32           `yaml:"num_pon_ports"`
+	Ranges []PonRangeConfig `yaml:"ranges"`
+}
+
+type IdRange struct {
+	StartId uint32 `yaml:"start"`
+	EndId   uint32 `yaml:"end"`
+}
+
+type PonTechnology int
+
+var ponTechnologyValues = []string{
+	"GPON", "XGS-PON",
+}
+
+func (t PonTechnology) String() string {
+	return ponTechnologyValues[t]
+}
+
+const (
+	GPON PonTechnology = iota
+	XGSPON
+)
+
+func PonTechnologyFromString(s string) (PonTechnology, error) {
+	for i, val := range ponTechnologyValues {
+		if val == s {
+			return PonTechnology(i), nil
+		}
+	}
+	log.WithFields(log.Fields{
+		"ValidValues": strings.Join(ponTechnologyValues[:], ", "),
+	}).Errorf("%s-is-not-a-valid-pon-technology", s)
+	return -1, fmt.Errorf("%s-is-not-a-valid-pon-technology", s)
+}
+
+//Constants for default allocation ranges
+const (
+	defaultOnuIdStart          = 1
+	defaultAllocIdStart        = 1024
+	defaultGemPortIdPerAllocId = 8
+	defaultGemportIdStart      = 1024
+)
+
+type PonRangeConfig struct {
+	PonRange     IdRange `yaml:"pon_id_range"`
+	Technology   string  `yaml:"tech"`
+	OnuRange     IdRange `yaml:"onu_id_range"`
+	AllocIdRange IdRange `yaml:"alloc_id_range"`
+	GemportRange IdRange `yaml:"gemport_id_range"`
+}
+
+func GetPonConfigById(id uint32) (*PonRangeConfig, error) {
+	if PonsConfig == nil {
+		return nil, fmt.Errorf("pons-config-nil")
+	}
+
+	for _, r := range PonsConfig.Ranges {
+		if id >= r.PonRange.StartId && id <= r.PonRange.EndId {
+			return &r, nil
+		}
+	}
+
+	return nil, fmt.Errorf("pon-config-for-id-%d-not-found", id)
+}
+
 type BBSimConfig struct {
 	ConfigFile             string
 	ServiceConfigFile      string
+	PonsConfigFile         string
 	DhcpRetry              bool    `yaml:"dhcp_retry"`
 	AuthRetry              bool    `yaml:"auth_retry"`
 	LogLevel               string  `yaml:"log_level"`
@@ -172,8 +239,9 @@
 }
 
 var (
-	Config   *GlobalConfig
-	Services []ServiceYaml
+	Config     *GlobalConfig
+	Services   []ServiceYaml
+	PonsConfig *PonPortsConfig
 )
 
 // Load the BBSim configuration. This is a combination of CLI parameters and YAML files
@@ -221,6 +289,31 @@
 
 	Services = services
 
+	//A blank filename means we should fall back to bbsim defaults
+	if Config.BBSim.PonsConfigFile == "" {
+		PonsConfig, err = getDefaultPonsConfig()
+		if err != nil {
+			log.WithFields(log.Fields{
+				"err": err,
+			}).Fatal("Can't load Pon interfaces defaults.")
+		}
+	} else {
+		PonsConfig, err = loadBBSimPons(Config.BBSim.PonsConfigFile)
+
+		if err != nil {
+			log.WithFields(log.Fields{
+				"file": Config.BBSim.PonsConfigFile,
+				"err":  err,
+			}).Fatal("Can't read services file")
+		}
+	}
+
+	if err := validatePonsConfig(PonsConfig); err != nil {
+		log.WithFields(log.Fields{
+			"file": Config.BBSim.PonsConfigFile,
+			"err":  err,
+		}).Fatal("Invalid Pon interfaces configuration")
+	}
 }
 
 func readCliParams() *GlobalConfig {
@@ -229,6 +322,7 @@
 
 	configFile := flag.String("config", conf.BBSim.ConfigFile, "Configuration file path")
 	servicesFile := flag.String("services", conf.BBSim.ServiceConfigFile, "Service Configuration file path")
+	ponsFile := flag.String("pon_port_config_file", conf.BBSim.PonsConfigFile, "Pon Interfaces Configuration file path")
 	sadisBpFormat := flag.String("bp_format", conf.BBSim.BandwidthProfileFormat, "Bandwidth profile format, 'mef' or 'ietf'")
 
 	olt_id := flag.Int("olt_id", conf.Olt.ID, "OLT device ID")
@@ -275,6 +369,7 @@
 	conf.Olt.OmciResponseRate = uint8(*omci_response_rate)
 	conf.BBSim.ConfigFile = *configFile
 	conf.BBSim.ServiceConfigFile = *servicesFile
+	conf.BBSim.PonsConfigFile = *ponsFile
 	conf.BBSim.CpuProfile = profileCpu
 	conf.BBSim.LogLevel = *logLevel
 	conf.BBSim.LogCaller = *logCaller
@@ -309,8 +404,12 @@
 
 	c := &GlobalConfig{
 		BBSimConfig{
-			ConfigFile:             "configs/bbsim.yaml",
-			ServiceConfigFile:      "configs/att-services.yaml",
+			ConfigFile:        "configs/bbsim.yaml",
+			ServiceConfigFile: "configs/att-services.yaml",
+			// PonsConfigFile is left intentionally blank here
+			// to use the default values computed at runtime depending
+			// on the loaded Services
+			PonsConfigFile:         "",
 			LogLevel:               "debug",
 			LogCaller:              false,
 			Delay:                  200,
@@ -341,7 +440,6 @@
 			NniPorts:           1,
 			NniSpeed:           10000, //Mbps
 			OnusPonPort:        1,
-			Technology:         "XGS-PON",
 			ID:                 0,
 			OltRebootDelay:     60,
 			PortStatsInterval:  20,
@@ -357,6 +455,35 @@
 	return c
 }
 
+func getDefaultPonsConfig() (*PonPortsConfig, error) {
+
+	if Config == nil {
+		return nil, fmt.Errorf("Config is nil")
+	}
+	if Services == nil {
+		return nil, fmt.Errorf("Services is nil")
+	}
+
+	//The default should replicate the old way bbsim used to compute resource ranges based on the configuration
+	// 1 allocId per Service * UNI
+	allocIdPerOnu := uint32(Config.Olt.UniPorts * uint32(len(Services)))
+	return &PonPortsConfig{
+		Number: Config.Olt.PonPorts,
+		Ranges: []PonRangeConfig{
+			{
+				PonRange:   IdRange{0, Config.Olt.PonPorts - 1},
+				Technology: XGSPON.String(),
+				// we need one ONU ID available per ONU, but the smaller the range the smaller the pool created in the openolt adapter
+				OnuRange: IdRange{defaultOnuIdStart, defaultOnuIdStart + (Config.Olt.OnusPonPort - 1)},
+				// 1 allocId per Service * UNI * ONU
+				AllocIdRange: IdRange{defaultAllocIdStart, defaultAllocIdStart + (Config.Olt.OnusPonPort * allocIdPerOnu)},
+				// up to 8 gemport-id per tcont/alloc-id
+				GemportRange: IdRange{defaultGemportIdStart, defaultGemportIdStart + Config.Olt.OnusPonPort*allocIdPerOnu*defaultGemPortIdPerAllocId},
+			},
+		},
+	}, nil
+}
+
 // LoadBBSimConf loads the BBSim configuration from a YAML file
 func loadBBSimConf(filename string) (*GlobalConfig, error) {
 	yamlConfig := getDefaultOps()
@@ -378,6 +505,84 @@
 	return yamlConfig, nil
 }
 
+// loadBBSimPons loads the configuration of PON interfaces from a YAML file
+func loadBBSimPons(filename string) (*PonPortsConfig, error) {
+	yamlPonsConfig, err := getDefaultPonsConfig()
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("Can't load Pon interfaces defaults.")
+		return nil, err
+	}
+
+	yamlFile, err := ioutil.ReadFile(filename)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err":      err,
+			"filename": filename,
+		}).Error("Cannot load Pon interfaces configuration file. Using defaults.")
+		return yamlPonsConfig, nil
+	}
+
+	err = yaml.Unmarshal(yamlFile, yamlPonsConfig)
+	if err != nil {
+		return nil, err
+	}
+
+	return yamlPonsConfig, nil
+}
+
+// validatePonsConfig checks if the configuration to use for the definition of Pon interfaces is valid
+func validatePonsConfig(pons *PonPortsConfig) error {
+
+	if pons.Number == 0 {
+		return fmt.Errorf("no-pon-ports")
+	}
+
+	definedPorts := make([]int, pons.Number)
+
+	for rIndex, resRange := range pons.Ranges {
+		if _, err := PonTechnologyFromString(resRange.Technology); err != nil {
+			return err
+		}
+
+		if resRange.PonRange.EndId < resRange.PonRange.StartId {
+			return fmt.Errorf("invalid-pon-ports-limits-in-range-%d", rIndex)
+		}
+
+		//Keep track of the defined pons
+		for p := resRange.PonRange.StartId; p <= resRange.PonRange.EndId; p++ {
+			if p > uint32(len(definedPorts)-1) {
+				return fmt.Errorf("pon-port-%d-in-range-%d-but-max-is-%d", p, rIndex, pons.Number-1)
+			}
+			definedPorts[p]++
+
+			if definedPorts[p] > 1 {
+				return fmt.Errorf("pon-port-%d-has-duplicate-definition-in-range-%d", p, rIndex)
+			}
+		}
+
+		if resRange.OnuRange.EndId < resRange.OnuRange.StartId {
+			return fmt.Errorf("invalid-onus-limits-in-range-%d", rIndex)
+		}
+		if resRange.AllocIdRange.EndId < resRange.AllocIdRange.StartId {
+			return fmt.Errorf("invalid-allocid-limits-in-range-%d", rIndex)
+		}
+		if resRange.GemportRange.EndId < resRange.GemportRange.StartId {
+			return fmt.Errorf("invalid-gemport-limits-in-range-%d", rIndex)
+		}
+	}
+
+	//Check if the ranges define all the pons
+	for i, num := range definedPorts {
+		if num < 1 {
+			return fmt.Errorf("pon-port-%d-is-not-defined-in-ranges", i)
+		}
+	}
+
+	return nil
+}
+
 // LoadBBSimServices parses a file describing the services that need to be created for each UNI
 func loadBBSimServices(filename string) ([]ServiceYaml, error) {