-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathdhcpetcd.go
128 lines (110 loc) · 3.53 KB
/
dhcpetcd.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
package main
import (
"errors"
"net"
"strings"
"time"
"github.com/coreos/go-etcd/etcd"
)
func (db EtcdDB) InitDHCP() {
db.client.CreateDir("dhcp", 0)
}
func (db EtcdDB) GetIP(ip net.IP) (IPEntry, error) {
key := etcdKeyFromIP(ip)
response, err := db.client.Get(key, false, false)
if response == nil || response.Node == nil {
return IPEntry{}, errors.New("Not Found")
}
mac, err := net.ParseMAC(response.Node.Value)
if err != nil {
return IPEntry{}, err
}
return IPEntry{MAC: mac}, nil
}
func (db EtcdDB) HasIP(ip net.IP) bool {
key := etcdKeyFromIP(ip)
response, _ := db.client.Get(key, false, false)
if response != nil && response.Node != nil {
return true
}
return false
}
func (db EtcdDB) GetMAC(mac net.HardwareAddr, cascade bool) (*MACEntry, bool, error) {
// TODO: First attempt to retrieve the entry from a cache of some kind (that can be dirtied)
// NOTE: The cache should always return a deep copy of the cached value
entry := MACEntry{MAC: mac}
// Copy cascaded attributes by making recursive calls to this function
if cascade && len(mac) > 1 {
parent, _, _ := db.GetMAC(mac[0:len(mac)-1], cascade) // Chop off the last byte for each recursive call
if parent != nil {
entry.Attr = parent.Attr // Only safe if we receive a deep copy of the cached value
}
}
// Fetch attributes and lease data for this MAC
key := etcdKeyFromMAC(mac)
response, err := db.client.Get(key, true, true) // do the lookup
if err != nil {
// FIXME: Return the etcd error for everything except missing keys
//return nil, false, err
return &entry, false, nil
}
if response.Node == nil || !response.Node.Dir {
// Not found
// NOTE: Retuning the entry is necessary for recursive calls
return &entry, false, nil
}
etcdNodeToMACEntry(response.Node, &entry)
return &entry, true, nil
}
func (db EtcdDB) RenewLease(lease *MACEntry) error {
// FIXME: Validate lease
duration := uint64(lease.Duration.Seconds() + 0.5) // Half second jitter to hide network delay
_, err := db.client.CompareAndSwap("dhcp/"+lease.IP.String(), lease.MAC.String(), duration, lease.MAC.String(), 0)
if err == nil {
return db.WriteLease(lease)
}
return err
}
func (db EtcdDB) CreateLease(lease *MACEntry) error {
// FIXME: Validate lease
duration := uint64(lease.Duration.Seconds() + 0.5)
_, err := db.client.Create("dhcp/"+lease.IP.String(), lease.MAC.String(), duration)
if err == nil {
return db.WriteLease(lease)
}
return err
}
func (db EtcdDB) WriteLease(lease *MACEntry) error {
// FIXME: Validate lease
// NOTE: This does not save attributes. That should probably happen in a different function.
duration := uint64(lease.Duration.Seconds() + 0.5) // Half second jitter to hide network delay
// FIXME: Decide what to do if either of these calls returns an error
db.client.CreateDir("dhcp/"+lease.MAC.String(), 0)
db.client.Set("dhcp/"+lease.MAC.String()+"/ip", lease.IP.String(), duration)
return nil
}
// TODO: Write function for saving attributes to etcd?
func etcdNodeToMACEntry(root *etcd.Node, entry *MACEntry) {
for _, node := range root.Nodes {
if node.Dir {
continue // Ignore subdirectories
}
key := strings.Replace(node.Key, root.Key+"/", "", 1)
switch key {
case "ip":
entry.IP = net.ParseIP(node.Value)
entry.Duration = time.Duration(node.TTL)
default:
if entry.Attr == nil {
entry.Attr = make(map[string]string)
}
entry.Attr[key] = node.Value
}
}
}
func etcdKeyFromIP(ip net.IP) string {
return "/dhcp/" + ip.String()
}
func etcdKeyFromMAC(mac net.HardwareAddr) string {
return "/dhcp/" + mac.String()
}