-
Notifications
You must be signed in to change notification settings - Fork 20
/
ip.go
99 lines (92 loc) · 2.35 KB
/
ip.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
package model
import (
"context"
"encoding/json"
"net"
"strconv"
"github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/clientv3/clientv3util"
"github.com/cybozu-go/coil"
"github.com/cybozu-go/netutil"
)
func (m etcdModel) GetAllocatedIPs(ctx context.Context, block *net.IPNet) (map[string]net.IP, error) {
prefix := ipKeyPrefix(block)
resp, err := m.etcd.Get(ctx, prefix, clientv3.WithPrefix())
if err != nil {
return nil, err
}
ips := make(map[string]net.IP)
for _, kv := range resp.Kvs {
offsetStr := string(kv.Key[len(prefix):])
offset, err := strconv.Atoi(offsetStr)
if err != nil {
return nil, err
}
ip := netutil.IntToIP4(netutil.IP4ToInt(block.IP) + uint32(offset))
var key string
var assignment coil.IPAssignment
err = json.Unmarshal(kv.Value, &assignment)
if err != nil {
// In older than version 1.0.2, the value is container-id but not json.
key = string(kv.Value)
} else {
key = assignment.ContainerID
}
ips[key] = ip
}
return ips, nil
}
func (m etcdModel) AllocateIP(ctx context.Context, block *net.IPNet, assignment coil.IPAssignment) (net.IP, error) {
resp, err := m.etcd.Get(ctx, ipKeyPrefix(block), clientv3.WithPrefix(), clientv3.WithKeysOnly())
if err != nil {
return nil, err
}
allocated := make(map[string]bool)
for _, kv := range resp.Kvs {
allocated[string(kv.Key)] = true
}
ones, bits := block.Mask.Size()
blockSize := int(1 << uint(bits-ones))
val, err := json.Marshal(assignment)
if err != nil {
return nil, err
}
offset := -1
for i := 0; i < blockSize; i++ {
k := ipKey(block, i)
if allocated[k] {
continue
}
resp, err := m.etcd.Txn(ctx).
If(clientv3util.KeyMissing(k)).
Then(clientv3.OpPut(k, string(val))).
Commit()
if err != nil {
return nil, err
}
if !resp.Succeeded {
continue
}
offset = i
break
}
if offset == -1 {
return nil, ErrBlockIsFull
}
return netutil.IntToIP4(netutil.IP4ToInt(block.IP) + uint32(offset)), nil
}
func (m etcdModel) FreeIP(ctx context.Context, block *net.IPNet, ip net.IP, modRev int64) error {
offset := netutil.IP4ToInt(ip) - netutil.IP4ToInt(block.IP)
key := ipKey(block, int(offset))
resp, err := m.etcd.Txn(ctx).
If(clientv3.Compare(clientv3.ModRevision(key), "=", modRev)).
Then(clientv3.OpDelete(key)).
Commit()
if err != nil {
return err
}
if !resp.Succeeded {
return ErrModRevDiffers
}
return nil
}