Skip to content

Commit

Permalink
Adding TCs for confman.Reserve
Browse files Browse the repository at this point in the history
  • Loading branch information
Levovar committed Jul 9, 2019
1 parent 4ffd1dd commit 12cec53
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 25 deletions.
14 changes: 7 additions & 7 deletions pkg/admit/netadmit.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func addTenantSpecificDetails(danmClient danmclientset.Interface, tnet *danmtype
return err
}
if IsTypeDynamic(tnet.Spec.NetworkType) {
err = allocateDetailsForDynamicBackends(tnet,tconf)
err = allocateDetailsForDynamicBackends(danmClient, tnet,tconf)
if err != nil {
return err
}
Expand All @@ -180,15 +180,15 @@ func addTenantSpecificDetails(danmClient danmclientset.Interface, tnet *danmtype
return nil
}

func allocateDetailsForDynamicBackends(tnet *danmtypes.DanmNet,tconf *danmtypes.TenantConfig) error {
func allocateDetailsForDynamicBackends(danmClient danmclientset.Interface, tnet *danmtypes.DanmNet,tconf *danmtypes.TenantConfig) error {
var pfProfiles []danmtypes.IfaceProfile
for _, iface := range tconf.HostDevices {
if tnet.Spec.Options.DevicePool != "" && tnet.Spec.Options.DevicePool == iface.Name {
//This is the interface profile belonging to the network's DevicePool
return attachNetworkToIfaceProfile(tnet,tconf,iface)
return attachNetworkToIfaceProfile(danmClient, tnet,tconf,iface)
} else if tnet.Spec.Options.Device == iface.Name && !strings.Contains(iface.Name,"/") {
//This is the interface profile matching the requested host_device
return attachNetworkToIfaceProfile(tnet,tconf,iface)
return attachNetworkToIfaceProfile(danmClient, tnet,tconf,iface)
}
//DevicePools generally look like this: "xyz.abc.io/resource_name".
//Here we separate "real" NICs from abstract K8s Devices
Expand All @@ -206,16 +206,16 @@ func allocateDetailsForDynamicBackends(tnet *danmtypes.DanmNet,tconf *danmtypes.
rand.Seed(time.Now().UnixNano())
chosenProfile := pfProfiles[rand.Intn(len(pfProfiles))]
//Otherwise we randomly choose an interface profile and attach the TenantNetwork to it
return attachNetworkToIfaceProfile(tnet,tconf,chosenProfile)
return attachNetworkToIfaceProfile(danmClient, tnet,tconf,chosenProfile)
}

func attachNetworkToIfaceProfile(tnet *danmtypes.DanmNet, tconf *danmtypes.TenantConfig, iface danmtypes.IfaceProfile) error {
func attachNetworkToIfaceProfile(danmClient danmclientset.Interface, tnet *danmtypes.DanmNet, tconf *danmtypes.TenantConfig, iface danmtypes.IfaceProfile) error {
if tnet.Spec.Options.Device == "" && tnet.Spec.Options.DevicePool == "" {
tnet.Spec.Options.Device = iface.Name
}
if (iface.VniType == "vlan" && tnet.Spec.Options.Vlan == 0) ||
(iface.VniType == "vxlan" && tnet.Spec.Options.Vxlan == 0) {
vni,err := confman.Reserve(tconf, iface)
vni,err := confman.Reserve(danmClient, tconf, iface)
if err != nil {
return errors.New("cannot reserve VNI for interface:" + iface.Name + " , because:" + err.Error())
}
Expand Down
27 changes: 10 additions & 17 deletions pkg/confman/confman.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
danmtypes "github.com/nokia/danm/crd/apis/danm/v1"
danmclientset "github.com/nokia/danm/crd/client/clientset/versioned"
"github.com/nokia/danm/pkg/bitarray"
"github.com/nokia/danm/pkg/metacni"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
)
Expand All @@ -23,7 +22,7 @@ func GetTenantConfig(danmClient danmclientset.Interface) (*danmtypes.TenantConfi
return &reply.Items[0], nil
}

func Reserve(tconf *danmtypes.TenantConfig, iface danmtypes.IfaceProfile) (int,error) {
func Reserve(danmClient danmclientset.Interface, tconf *danmtypes.TenantConfig, iface danmtypes.IfaceProfile) (int,error) {
allocs := bitarray.NewBitArrayFromBase64(iface.Alloc)
vnis, err := cpuset.Parse(iface.VniRange)
if err != nil {
Expand All @@ -44,8 +43,14 @@ func Reserve(tconf *danmtypes.TenantConfig, iface danmtypes.IfaceProfile) (int,e
return 0, errors.New("VNI cannot be allocated from interface profile:" + iface.Name + " because the whole range is already reserved")
}
index := getIfaceIndex(tconf, iface.Name, iface.VniType)
if index == -1 {
return 0, errors.New("VNI cannot be reserved because selected interface does not exist. You should call for a tech priest, and start praying to the Omnissiah immediately.")
}
tconf.HostDevices[index] = iface
err = updateConfigInApi(tconf)
//TODO: now, do we actually need to manually check for the OptimisticLockErrorMessage when we use a generated client,
//or that is actually dead code in ipam as well?
//Let's find out!
_, err = danmClient.DanmV1().TenantConfigs().Update(tconf)
if err != nil {
return 0, errors.New("VNI allocation of TenantConfig cannot be updated in the Kubernetes API because:" + err.Error())
}
Expand All @@ -63,19 +68,6 @@ func getIfaceIndex(tconf *danmtypes.TenantConfig, name, vniType string) int {
return -1
}

func updateConfigInApi(tconf *danmtypes.TenantConfig) error {
danmClient, err := metacni.CreateDanmClient("")
if err != nil {
return err
}
confClient := danmClient.DanmV1().TenantConfigs()
//TODO: now, do we actually need to manually check for the OptimisticLockErrorMessage when we use a generated client,
//or that is actually dead code in ipam as well?
//Let's find out!
_, err = confClient.Update(tconf)
return err
}

func Free(tconf *danmtypes.TenantConfig, dnet *danmtypes.DanmNet) error {
if dnet.Spec.Options.Vlan == 0 && dnet.Spec.Options.Vxlan == 0 {
return nil
Expand All @@ -102,5 +94,6 @@ func Free(tconf *danmtypes.TenantConfig, dnet *danmtypes.DanmNet) error {
}
allocs.Reset(uint32(vni))
tconf.HostDevices[index].Alloc = allocs.Encode()
return updateConfigInApi(tconf)
return nil
// return updateConfigInApi(tconf)
}
5 changes: 4 additions & 1 deletion test/stubs/tconfclient_stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ func (tconfClient TconfClientStub) Create(obj *danmtypes.TenantConfig) (*danmtyp
}

func (tconfClient TconfClientStub) Update(obj *danmtypes.TenantConfig) (*danmtypes.TenantConfig, error) {
return nil, nil
if strings.HasPrefix(obj.ObjectMeta.Name,"error") {
return nil, errors.New("here you go")
}
return &danmtypes.TenantConfig{}, nil
}

func (tconfClient TconfClientStub) Delete(name string, options *meta_v1.DeleteOptions) error {
Expand Down
115 changes: 115 additions & 0 deletions test/uts/confman_test/confman_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package confman_test
import (
"testing"
danmtypes "github.com/nokia/danm/crd/apis/danm/v1"
"github.com/nokia/danm/pkg/bitarray"
"github.com/nokia/danm/pkg/confman"
"github.com/nokia/danm/test/stubs"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -17,6 +18,17 @@ type TconfSet struct {
tconfs []danmtypes.TenantConfig
}

const (
AllocFor5k = "gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
)

var (
emptyTconfs []danmtypes.TenantConfig
errorTconfs = []danmtypes.TenantConfig {
Expand All @@ -26,6 +38,27 @@ var (
danmtypes.TenantConfig{ObjectMeta: meta_v1.ObjectMeta {Name: "firstConf"}},
danmtypes.TenantConfig{ObjectMeta: meta_v1.ObjectMeta {Name: "secondConf"}},
}
reserveConfs = []danmtypes.TenantConfig {
danmtypes.TenantConfig{
ObjectMeta: meta_v1.ObjectMeta {Name: "tconf"},
HostDevices: []danmtypes.IfaceProfile {
danmtypes.IfaceProfile{Name: "ens4", VniType: "vxlan", VniRange: "700-710", Alloc:AllocFor5k},
danmtypes.IfaceProfile{Name: "ens4", VniType: "vlan", VniRange: "200,500-510", Alloc:AllocFor5k},
},
},
danmtypes.TenantConfig{
ObjectMeta: meta_v1.ObjectMeta {Name: "error"},
HostDevices: []danmtypes.IfaceProfile {
danmtypes.IfaceProfile{Name: "ens4", VniType: "vxlan", VniRange: "800-810", Alloc:AllocFor5k},
},
},
}
reserveIfaces = []danmtypes.IfaceProfile {
danmtypes.IfaceProfile{Name:"invalidVni", VniRange: "invalid"},
danmtypes.IfaceProfile{Name: "ens4", VniType: "vxlan", VniRange: "700-710", Alloc:AllocFor5k},
danmtypes.IfaceProfile{Name: "ens4", VniType: "vlan", VniRange: "200,500-510", Alloc:AllocFor5k},
danmtypes.IfaceProfile{Name: "hupak", VniType: "vlan", VniRange: "1000,1001", Alloc:AllocFor5k},
}
tconfSets = []TconfSet {
TconfSet{name: "emptyTcs", tconfs: emptyTconfs},
TconfSet{name: "errorTconfs", tconfs: errorTconfs},
Expand All @@ -44,6 +77,23 @@ var getTconfTcs = []struct {
{"multipleConfigs", testConfigs, false},
}

var reserveTcs = []struct {
tcName string
tconfName string
ifaceName string
vniType string
reserveVnis []int
isErrorExpected bool
expectedVni int
}{
{"invalidVni", "tconf", "invalidVni", "", nil, true, 0},
{"reserveFirstFreeInEmptyIface", "tconf", "ens4", "vlan", nil, false, 200},
{"reserveLastFreeInIface", "tconf", "ens4", "vlan", []int{200,509}, false, 510},
{"noFreeVniInIface", "tconf", "ens4", "vlan", []int{200,510}, true, 0},
{"errorUpdating", "error", "ens4", "vxlan", nil, true, 0},
{"nonExistentProfile", "tconf", "hupak", "vlan", nil, true, 0},
}

func TestGetTenantConfig(t *testing.T) {
for _, tc := range getTconfTcs {
t.Run(tc.tcName, func(t *testing.T) {
Expand All @@ -62,11 +112,76 @@ func TestGetTenantConfig(t *testing.T) {
}
}

func TestReserve(t *testing.T) {
for _, tc := range reserveTcs {
t.Run(tc.tcName, func(t *testing.T) {
tconf := getTconf(tc.tconfName, reserveConfs)
iface := getIface(tc.ifaceName, tc.vniType, reserveIfaces)
if tc.reserveVnis != nil {
reserveVnis(&iface,tc.reserveVnis)
}
testArtifacts := stubs.TestArtifacts{TestTconfs: reserveConfs}
tconfClientStub := stubs.NewClientSetStub(testArtifacts)
vni, err := confman.Reserve(tconfClientStub, tconf, iface)
if (err != nil && !tc.isErrorExpected) || (err == nil && tc.isErrorExpected) {
t.Errorf("Received error:%v does not match with expectation", err)
return
}
if tc.expectedVni != 0 {
if tc.expectedVni != vni {
t.Errorf("Received reserved VNI:%d does not match with expected:%d",vni,tc.expectedVni)
return
}
_, updatedIface := getIfaceFromTconf(tc.ifaceName, tc.vniType, tconf)
if updatedIface.Alloc == iface.Alloc {
t.Errorf("Alloc field in the selected inteface profile did not change even though a VNI was reserved!")
return
}
}
})
}
}

func getTconfSet(tconfName string, tconfSets []TconfSet) []danmtypes.TenantConfig {
for _, tconfSet := range tconfSets {
if tconfSet.name == tconfName {
return tconfSet.tconfs
}
}
return nil
}

func getTconf(tconfName string, tconfSet []danmtypes.TenantConfig) *danmtypes.TenantConfig {
for _, tconf := range tconfSet {
if tconf.ObjectMeta.Name == tconfName {
return &tconf
}
}
return nil
}

func getIface(ifaceName string, vniType string, ifaceSet []danmtypes.IfaceProfile) danmtypes.IfaceProfile {
for _, iface := range ifaceSet {
if iface.Name == ifaceName && iface.VniType == vniType{
return iface
}
}
return danmtypes.IfaceProfile{}
}

func getIfaceFromTconf(ifaceName string, vniType string, tconf *danmtypes.TenantConfig) (int,danmtypes.IfaceProfile) {
for index, iface := range tconf.HostDevices {
if iface.Name == ifaceName && iface.VniType == vniType {
return index, iface
}
}
return -1, danmtypes.IfaceProfile{}
}

func reserveVnis(iface *danmtypes.IfaceProfile, vniRange []int) {
allocs := bitarray.NewBitArrayFromBase64(iface.Alloc)
for i := vniRange[0]; i <= vniRange[1]; i++ {
allocs.Set(uint32(i))
}
iface.Alloc = allocs.Encode()
}

0 comments on commit 12cec53

Please sign in to comment.