Skip to content

Commit 5afec52

Browse files
Merge pull request #98 from qiangzii/master
add ipam-check tool; update ip relese; update ipamblock restore; update ipamblock attributes info;
2 parents 9e00608 + 477f1fc commit 5afec52

File tree

16 files changed

+595
-197
lines changed

16 files changed

+595
-197
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ tools: vet fmt
6767
$(BUILD_ENV) go build -ldflags "-w" -o $(TOOLS_BIN_DIR)/vxnet-client cmd/tools/vxnet-client/client.go
6868
$(BUILD_ENV) go build -ldflags "-w" -o $(TOOLS_BIN_DIR)/patch-node cmd/tools/node-patch/patch.go
6969
$(BUILD_ENV) go build -ldflags "-w" -o $(TOOLS_BIN_DIR)/dhcp-client cmd/tools/dhcp-client/client.go
70+
$(BUILD_ENV) go build -ldflags "-w" -o $(TOOLS_BIN_DIR)/ipam-check cmd/tools/ipam-check/check.go
7071

7172
deploy:
7273
sed -i'' -e 's@image: .*@image: '"${IMG}"'@' config/${TARGET}/manager_image_patch.yaml

build/hostnic/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
FROM alpine
22
RUN apk --no-cache add ca-certificates \
3+
&& apk --no-cache add ipvsadm \
34
&& update-ca-certificates 2>/dev/null || true
45
WORKDIR /app
56
ADD bin/hostnic .

build/hostnic/DockerfileWithBuilder

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ RUN CGO_ENABLED=0 GOOS=linux go build -ldflags "-w" -o ${CORE_BIN_DIR}/hostnic-c
1010
&& CGO_ENABLED=0 GOOS=linux go build -ldflags "-w" -o ${TOOLS_BIN_DIR}/hostnic-client ./cmd/tools/hostnic-client/client.go \
1111
&& CGO_ENABLED=0 GOOS=linux go build -ldflags "-w" -o ${TOOLS_BIN_DIR}/vxnet-client ./cmd/tools/vxnet-client/client.go \
1212
&& CGO_ENABLED=0 GOOS=linux go build -ldflags "-w" -o ${TOOLS_BIN_DIR}/patch-node ./cmd/tools/node-patch/patch.go \
13-
&& CGO_ENABLED=0 GOOS=linux go build -ldflags "-w" -o $(TOOLS_BIN_DIR)/dhcp-client ./cmd/tools/dhcp-client/client.go
13+
&& CGO_ENABLED=0 GOOS=linux go build -ldflags "-w" -o $(TOOLS_BIN_DIR)/dhcp-client ./cmd/tools/dhcp-client/client.go \
14+
&& CGO_ENABLED=0 GOOS=linux go build -ldflags "-w" -o ${TOOLS_BIN_DIR}/ipam-check ./cmd/tools/ipam-check/check.go
1415

1516

1617
FROM alpine
1718
ARG CORE_BIN_DIR
1819
RUN apk --no-cache add ca-certificates \
20+
&& apk --no-cache add ipvsadm \
1921
&& update-ca-certificates 2>/dev/null || true
2022
WORKDIR /app
2123
COPY --from=builder /hostnic-cni/${CORE_BIN_DIR} .

cmd/hostnic/hostnic.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,14 @@ func cmdAdd(args *skel.CmdArgs) error {
443443
}
444444
klog.Infof("cmdAdd for %s load and check netconf success, conf=%+v", args.ContainerID, conf)
445445

446+
//get nodename
447+
nodeName, err := os.Hostname()
448+
if err != nil {
449+
klog.Errorf("failed to get hostname: %v, ignore!", err)
450+
}
451+
446452
// run the IPAM plugin and get back the config to apply
447-
ipamMsg, result, err := ipam2.AddrAlloc(args)
453+
ipamMsg, result, err := ipam2.AddrAlloc(args, nodeName)
448454
if err != nil {
449455
return fmt.Errorf("failed to alloc addr: %v", err)
450456
}
@@ -582,12 +588,16 @@ func cmdDel(args *skel.CmdArgs) error {
582588
if err != nil {
583589
return fmt.Errorf("get nic and ip info for pod %s error: %v", podKey, err)
584590
}
591+
592+
if ipamMsg.IP != "" {
593+
klog.Infof("get ip info for pod %s success, ip: %v", podKey, ipamMsg.IP)
594+
}
595+
585596
if ipamMsg.Nic == nil {
586-
// not found db record, just return
587-
klog.Infof("not found db record for pod %s, skip cleanup!", podKey)
588-
return nil
597+
// not found db record and ip record, skip cleanup
598+
klog.Infof("not found nic record for pod %s, try to clean up rules and release ip", podKey)
599+
return cleanupRulesAndIP(args, ipamMsg)
589600
}
590-
klog.Infof(" get nic and ip info for pod %s success, ip: %v", podKey, ipamMsg.IP)
591601

592602
netns, err := ns.GetNS(args.Netns)
593603
if err != nil {

cmd/hostnic/ipam/ipam.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import (
3636
"github.com/yunify/hostnic-cni/pkg/rpc"
3737
)
3838

39-
func AddrAlloc(args *skel.CmdArgs) (*rpc.IPAMMessage, *current.Result, error) {
39+
func AddrAlloc(args *skel.CmdArgs, nodeName string) (*rpc.IPAMMessage, *current.Result, error) {
4040
// conf := NetConf{}
4141
// if err := json.Unmarshal(args.StdinData, &conf); err != nil {
4242
// return nil, nil, fmt.Errorf("failed to unmarshal netconf %s", spew.Sdump(args))
@@ -63,6 +63,7 @@ func AddrAlloc(args *skel.CmdArgs) (*rpc.IPAMMessage, *current.Result, error) {
6363
Containter: string(k8sArgs.K8S_POD_INFRA_CONTAINER_ID),
6464
Netns: args.Netns,
6565
IfName: args.IfName,
66+
NodeName: nodeName,
6667
},
6768
})
6869
if err != nil {

cmd/tools/ipam-check/check.go

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"os"
7+
"regexp"
8+
"strconv"
9+
"strings"
10+
"time"
11+
12+
k8sinformers "k8s.io/client-go/informers"
13+
"k8s.io/client-go/kubernetes"
14+
"k8s.io/client-go/tools/clientcmd"
15+
16+
networkv1alpha1 "github.com/yunify/hostnic-cni/pkg/apis/network/v1alpha1"
17+
clientset "github.com/yunify/hostnic-cni/pkg/client/clientset/versioned"
18+
informers "github.com/yunify/hostnic-cni/pkg/client/informers/externalversions"
19+
"github.com/yunify/hostnic-cni/pkg/constants"
20+
"github.com/yunify/hostnic-cni/pkg/networkutils"
21+
"github.com/yunify/hostnic-cni/pkg/rpc"
22+
"github.com/yunify/hostnic-cni/pkg/signals"
23+
"github.com/yunify/hostnic-cni/pkg/simple/client/network/ippool/ipam"
24+
)
25+
26+
var kubeconfig string
27+
var delete, debug bool
28+
29+
func usage() {
30+
fmt.Println("This tool is used to check leak ip arp rules and release ip, it will list all leak ip arp rules on this instance by default, if you want to delete them, please use '-delete' flag")
31+
flag.PrintDefaults()
32+
}
33+
34+
func main() {
35+
flag.StringVar(&kubeconfig, "kubeconfig", "/root/.kube/config", "Path to a kubeconfig. Only required if out-of-cluster.If not set, use the default path")
36+
flag.BoolVar(&delete, "delete", false, "delete leak ip arp rules and release ip")
37+
flag.BoolVar(&debug, "debug", false, "show debug info")
38+
flag.Usage = usage
39+
flag.Parse()
40+
41+
//get instance-id
42+
instanceIDByte, err := os.ReadFile(constants.InstanceIDFile)
43+
if err != nil {
44+
fmt.Printf("failed to load instance-id: %v", err)
45+
return
46+
}
47+
instanceID := strings.TrimSpace(string(instanceIDByte))
48+
fmt.Println("get instanceID: ", instanceID)
49+
50+
// set up signals so we handle the first shutdown signals gracefully
51+
stopCh := signals.SetupSignalHandler()
52+
53+
//client
54+
cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
55+
if err != nil {
56+
fmt.Printf("Error building kubeconfig: %v", err)
57+
return
58+
}
59+
60+
k8sClient, err := kubernetes.NewForConfig(cfg)
61+
if err != nil {
62+
fmt.Printf("Error building kubernetes clientset: %v", err)
63+
return
64+
}
65+
66+
client, err := clientset.NewForConfig(cfg)
67+
if err != nil {
68+
fmt.Printf("Error building example clientset: %v", err)
69+
return
70+
}
71+
72+
k8sInformerFactory := k8sinformers.NewSharedInformerFactory(k8sClient, time.Second*10)
73+
informerFactory := informers.NewSharedInformerFactory(client, time.Second*30)
74+
75+
ipamClient := ipam.NewIPAMClient(client, networkv1alpha1.IPPoolTypeLocal, informerFactory, k8sInformerFactory)
76+
77+
k8sInformerFactory.Start(stopCh)
78+
informerFactory.Start(stopCh)
79+
80+
if err = ipamClient.Sync(stopCh); err != nil {
81+
fmt.Printf("ipamclient sync error: %v", err)
82+
return
83+
}
84+
85+
//network tools for clean pod network
86+
networkutils.SetupNetworkHelper()
87+
88+
//get all arp rules on the instance
89+
rules, err := getArpRuleList()
90+
if err != nil {
91+
fmt.Printf("getArpRuleList failed: %v\n", err)
92+
return
93+
}
94+
if debug {
95+
fmt.Printf("all arp rules on instance %s: \n", instanceID)
96+
for _, rule := range rules {
97+
fmt.Printf("\t%s\n", rule.rule)
98+
}
99+
}
100+
fmt.Printf("\n\n")
101+
102+
//get pods on the instance
103+
podsMap, err := ipamClient.ListInstancePods(instanceID)
104+
if err != nil {
105+
fmt.Printf("ListInstancePods failed: %v\n", err)
106+
return
107+
}
108+
109+
//check and gather leak ip arp rules
110+
rulesToClear := []arpReplyInfo{}
111+
for _, rule := range rules {
112+
if _, ok := podsMap[rule.ip]; !ok {
113+
rulesToClear = append(rulesToClear, rule)
114+
}
115+
}
116+
117+
if len(rulesToClear) == 0 {
118+
fmt.Printf("no leak ip arp rules found on instance %s\n\n", instanceID)
119+
return
120+
}
121+
122+
fmt.Printf("leak ip arp rules on instance %s: \n", instanceID)
123+
for _, rule := range rulesToClear {
124+
fmt.Printf("\t%s\n", rule.rule)
125+
}
126+
fmt.Printf("\n\n")
127+
128+
if !delete {
129+
return
130+
}
131+
132+
// delete leak ip arp rules and release ip
133+
// should delete arp rules first, then release ip, or if release ip error,the rule will left but ip was released and will be allocated to other pod
134+
fmt.Printf("going to delete leak ip arp rules and release ip\n\n")
135+
for _, rule := range rulesToClear {
136+
if err := clearArpReplyRule(rule); err != nil {
137+
fmt.Printf("delete arp rule for leak ip %s error: %v, skip\n\n", rule.ip, err)
138+
continue
139+
}
140+
fmt.Printf("delete arp rule for leak ip %s success\n", rule.ip)
141+
if err := ipamClient.ReleaseByIP(rule.ip); err != nil {
142+
fmt.Printf("release leak ip %s error: %v, skip\n\n", rule.ip, err)
143+
}
144+
fmt.Printf("release leak ip %s success\n\n", rule.ip)
145+
}
146+
}
147+
148+
// list arp rules
149+
type arpReplyInfo struct {
150+
routeTableNum int32
151+
ip string
152+
macAddr string
153+
rule string
154+
}
155+
156+
func getArpRuleList() (ruleList []arpReplyInfo, err error) {
157+
//get all arp rules
158+
ruleStr, err := networkutils.ListArpReply()
159+
if err != nil {
160+
return nil, fmt.Errorf("list arp rules error: %v", err)
161+
}
162+
163+
//parse arp rules
164+
return parseARPRules(ruleStr)
165+
}
166+
167+
func parseARPRules(output string) (ruleList []arpReplyInfo, err error) {
168+
lines := strings.Split(output, "\n")
169+
170+
ruleRegex := regexp.MustCompile(`-p ARP --logical-in (br_\d+) --arp-op Request --arp-ip-dst (\d+\.\d+\.\d+\.\d+) -j arpreply --arpreply-mac ([\da-fA-F:]+)`)
171+
172+
for _, line := range lines {
173+
line = strings.TrimSpace(line)
174+
if line == "" || strings.HasPrefix(line, "Bridge chain:") || strings.HasPrefix(line, "policy:") {
175+
// skip empty line and header
176+
continue
177+
}
178+
179+
match := ruleRegex.FindStringSubmatch(line)
180+
if len(match) > 3 {
181+
logicalIn := match[1]
182+
ip := match[2]
183+
mac := match[3]
184+
185+
tableNumStr := strings.TrimPrefix(logicalIn, "br_")
186+
tableNum, err := strconv.Atoi(tableNumStr)
187+
if err != nil {
188+
return nil, fmt.Errorf("parse tableNumStr %s error: %v", tableNumStr, err)
189+
}
190+
191+
ruleList = append(ruleList, arpReplyInfo{
192+
routeTableNum: int32(tableNum),
193+
ip: ip,
194+
macAddr: mac,
195+
rule: line,
196+
})
197+
}
198+
}
199+
return
200+
}
201+
202+
// check and delete arp rules
203+
func clearArpReplyRule(item arpReplyInfo) error {
204+
//delete arp rule
205+
err := networkutils.NetworkHelper.CleanupPodNetwork(&rpc.HostNic{
206+
RouteTableNum: item.routeTableNum,
207+
HardwareAddr: item.macAddr,
208+
}, item.ip)
209+
if err != nil && !strings.Contains(err.Error(), "rule does not exist") {
210+
return fmt.Errorf("failed to delete ebtables rule %s: %s", item.rule, err)
211+
}
212+
213+
return nil
214+
}

cmd/tools/ipam-client/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ func main() {
142142
for _, block := range ippool.BrokenBlocks {
143143
for ipStr, podNames := range block.IpToPods {
144144
if len(podNames) > 1 {
145-
fmt.Printf("\t\tip %s was allocated more than noce to pods %v\n", ipStr, podNames)
145+
fmt.Printf("\t\tip %s was allocated more than once to pods %v\n", ipStr, podNames)
146146
}
147147
}
148148

deploy/hostnic.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ spec:
481481
fieldRef:
482482
apiVersion: v1
483483
fieldPath: metadata.namespace
484-
image: qingcloud/hostnic-plus:v1.0.14
484+
image: qingcloud/hostnic-plus:v1.0.15
485485
imagePullPolicy: IfNotPresent
486486
name: hostnic-node
487487
ports:
@@ -520,7 +520,7 @@ spec:
520520
- /app/install_hostnic.sh
521521
command:
522522
- /bin/sh
523-
image: qingcloud/hostnic-plus:v1.0.14
523+
image: qingcloud/hostnic-plus:v1.0.15
524524
imagePullPolicy: IfNotPresent
525525
name: hostnic-init
526526
resources: {}
@@ -595,7 +595,7 @@ spec:
595595
spec:
596596
containers:
597597
- name: hostnic-controller
598-
image: qingcloud/hostnic-plus:v1.0.14
598+
image: qingcloud/hostnic-plus:v1.0.15
599599
command:
600600
- /app/hostnic-controller
601601
- --v=5

pkg/allocator/allocator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func (a *Allocator) AllocHostNic(args *rpc.PodInfo) (*rpc.HostNic, error) {
218218
}
219219
nics, _, err := qcclient.QClient.CreateNicsAndAttach(vxnet, 1, nil, 1)
220220
if err != nil {
221-
return nil, err
221+
return nil, fmt.Errorf("create and attach nic failed: %v", err)
222222
}
223223
log.Infof("create and attach nic %s", getNicKey(nics[0]))
224224

0 commit comments

Comments
 (0)