-
Notifications
You must be signed in to change notification settings - Fork 81
/
confman.go
155 lines (146 loc) · 5.25 KB
/
confman.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package confman
import (
"context"
"errors"
"log"
"strings"
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/datastructs"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
)
const (
TenantConfigKind = "TenantConfig"
)
func GetTenantConfig(danmClient danmclientset.Interface) (*danmtypes.TenantConfig, error) {
reply, err := danmClient.DanmV1().TenantConfigs().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}
if reply == nil || len(reply.Items) == 0 {
return nil, errors.New("no TenantConfigs exist int the cluster")
}
//TODO: do a namespace based selection later if one generic config does not suffice
return &reply.Items[0], nil
}
func Reserve(danmClient danmclientset.Interface, tconf *danmtypes.TenantConfig, iface danmtypes.IfaceProfile) (int,error) {
for {
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")
}
chosenVni, newAlloc, err := reserveVni(tconf.HostDevices[index])
if err != nil {
return 0, err
}
tconf.HostDevices[index].Alloc = newAlloc
newConf, wasRefreshed, err := updateTenantConf(danmClient, tconf)
if err != nil {
return chosenVni, err
}
if wasRefreshed {
tconf = newConf
continue
}
return chosenVni, nil
}
}
func reserveVni(iface danmtypes.IfaceProfile) (int,string,error) {
allocs := bitarray.NewBitArrayFromBase64(iface.Alloc)
if allocs.Len() == 0 {
return 0, "", errors.New("VNI allocations for interface:" + iface.Name + " is corrupt! Are you running without webhook?")
}
vnis, err := cpuset.Parse(iface.VniRange)
if err != nil {
return 0, "", errors.New("vniRange for interface:" + iface.Name + " cannot be parsed because:" + err.Error())
}
chosenVni := -1
vniSet := vnis.ToSlice()
for _, vni := range vniSet {
if allocs.Get(uint32(vni)) {
continue
}
allocs.Set(uint32(vni))
chosenVni = vni
break
}
if chosenVni == -1 {
return 0, "", errors.New("VNI cannot be allocated from interface profile:" + iface.Name + " because the whole range is already reserved")
}
return chosenVni, allocs.Encode(), nil
}
func getIfaceIndex(tconf *danmtypes.TenantConfig, name, vniType string) int {
for index, iface := range tconf.HostDevices {
//As HostDevices is a list, the same interface might be added multiple types but with different VNI types
//We don't want to accidentally overwrite the wrong profile
if iface.Name == name && iface.VniType == vniType {
return index
}
}
return -1
}
func Free(danmClient danmclientset.Interface, tconf *danmtypes.TenantConfig, dnet *danmtypes.DanmNet) error {
if dnet.Spec.Options.Vlan == 0 && dnet.Spec.Options.Vxlan == 0 {
return nil
}
vniType := "vlan"
if dnet.Spec.Options.Vxlan != 0 {
vniType = "vxlan"
}
ifaceName := dnet.Spec.Options.Device
if dnet.Spec.Options.DevicePool != "" {
ifaceName = dnet.Spec.Options.DevicePool
}
for {
index := getIfaceIndex(tconf,ifaceName,vniType)
if index < 0 {
log.Println("WARNING: There is a data incosistency between TenantNetwork:" + dnet.ObjectMeta.Name + " in namespace:" +
dnet.ObjectMeta.Namespace + " , and TenantConfig:" + tconf.ObjectMeta.Name +
" as the used network details (interface name, VNI type) doe not match any entries in TenantConfig. This means your APIs were possibly tampered with!")
return nil
}
newAlloc, err := freeVni(dnet, tconf.HostDevices[index])
if err != nil {
return err
}
tconf.HostDevices[index].Alloc = newAlloc
newConf, wasRefreshed, err := updateTenantConf(danmClient, tconf)
if err != nil {
return err
}
if wasRefreshed {
tconf = newConf
continue
}
return nil
}
}
func freeVni(dnet *danmtypes.DanmNet, iface danmtypes.IfaceProfile) (string,error) {
vni := dnet.Spec.Options.Vlan
if dnet.Spec.Options.Vxlan != 0 {
vni = dnet.Spec.Options.Vxlan
}
allocs := bitarray.NewBitArrayFromBase64(iface.Alloc)
if allocs.Len() == 0 {
return "", errors.New("VNI allocations for interface:" + iface.Name + " is corrupt! Are you running without webhook?")
}
allocs.Reset(uint32(vni))
return allocs.Encode(), nil
}
func updateTenantConf(danmClient danmclientset.Interface, tconf *danmtypes.TenantConfig) (*danmtypes.TenantConfig,bool,error) {
var wasRefreshed bool
var newConf *danmtypes.TenantConfig
_, err := danmClient.DanmV1().TenantConfigs().Update(context.TODO(), tconf, metav1.UpdateOptions{})
if err != nil && strings.Contains(err.Error(), datastructs.OptimisticLockErrorMsg) {
newConf, err = danmClient.DanmV1().TenantConfigs().Get(context.TODO(), tconf.ObjectMeta.Name, metav1.GetOptions{})
if err != nil {
return nil, wasRefreshed, err
}
//https://github.com/kubernetes/client-go/issues/308 rears its ugly head here as well
newConf.TypeMeta.Kind = TenantConfigKind
wasRefreshed = true
}
return newConf, wasRefreshed, err
}