Skip to content

Commit 1b375ad

Browse files
authored
Merge pull request #145 from mkumatag/dhcp
dhcp-sync: sync dhcp config with powervs network
2 parents c4a06cd + 820c753 commit 1b375ad

File tree

4 files changed

+305
-0
lines changed

4 files changed

+305
-0
lines changed

cmd/dhcp-sync/dhcp-sync.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
// Copyright 2021 IBM Corp
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dhcp
16+
17+
import (
18+
"fmt"
19+
"io/ioutil"
20+
"net"
21+
"strings"
22+
"sync"
23+
"time"
24+
25+
"github.com/fsnotify/fsnotify"
26+
"github.com/spf13/cobra"
27+
"k8s.io/klog/v2"
28+
29+
"github.com/ppc64le-cloud/pvsadm/pkg"
30+
"github.com/ppc64le-cloud/pvsadm/pkg/client"
31+
"github.com/ppc64le-cloud/pvsadm/pkg/utils"
32+
33+
. "github.com/sayotte/iscdhcp"
34+
)
35+
36+
var (
37+
gateway, networkID, file, nameservers string
38+
mutex = &sync.Mutex{}
39+
)
40+
41+
func ipv4MaskString(m []byte) string {
42+
if len(m) != 4 {
43+
panic("ipv4Mask: len must be 4 bytes")
44+
}
45+
46+
return fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3])
47+
}
48+
49+
const dhcpdTemplate = `
50+
default-lease-time 600;
51+
max-lease-time 7200;
52+
ddns-update-style none;
53+
authoritative;
54+
55+
%s
56+
`
57+
58+
func doEvery(d time.Duration, f func()) {
59+
ticker := time.NewTicker(d)
60+
defer ticker.Stop()
61+
for ; true; <-ticker.C {
62+
f()
63+
}
64+
}
65+
66+
type RoutersOption []net.IP
67+
68+
// IndentedString implements the method of the same name in the Statement interface
69+
func (ro RoutersOption) IndentedString(prefix string) string {
70+
s := prefix + "option routers "
71+
for i := 0; i < len(ro)-1; i++ {
72+
s += ro[i].String() + ", "
73+
}
74+
return s + ro[len(ro)-1].String() + ";\n"
75+
}
76+
77+
func syncDHCPD() {
78+
c, err := client.NewClientWithEnv(pkg.Options.APIKey, pkg.Options.Environment, false)
79+
if err != nil {
80+
klog.Fatalf("failed to create a session with IBM cloud: %v", err)
81+
}
82+
83+
pvmclient, err := client.NewPVMClientWithEnv(c, pkg.Options.InstanceID, "", "prod")
84+
if err != nil {
85+
klog.Fatalf("failed to create a PVM client: %v", err)
86+
}
87+
88+
n, err := pvmclient.NetworkClient.Get(networkID)
89+
90+
ipv4Addr, ipv4Net, err := net.ParseCIDR(*n.Cidr)
91+
if err != nil {
92+
klog.Fatalf("failed to ParseCIDR: %v", err)
93+
}
94+
subnetStmt := SubnetStatement{
95+
SubnetNumber: ipv4Addr,
96+
Netmask: net.ParseIP(ipv4MaskString(ipv4Net.Mask)),
97+
}
98+
99+
ports, err := pvmclient.NetworkClient.GetAllPort(networkID)
100+
if err != nil {
101+
klog.Fatalf("failed to get the ports: %v", err)
102+
}
103+
104+
var g = n.Gateway
105+
106+
if gateway != "" {
107+
g = gateway
108+
}
109+
110+
router := RoutersOption{net.ParseIP(g)}
111+
subnetStmt.Statements = append(subnetStmt.Statements, router)
112+
113+
nameserver := DomainNameServersOption{}
114+
var nsList = n.DNSServers
115+
if nameservers != "" {
116+
nsList = strings.Split(nameservers, ",")
117+
}
118+
119+
for _, ns := range nsList {
120+
nameserver = append(nameserver, net.ParseIP(ns))
121+
}
122+
subnetStmt.Statements = append(subnetStmt.Statements, nameserver)
123+
124+
for _, port := range ports.Ports {
125+
hs := HostStatement{
126+
Hostname: "host" + strings.Split(*port.IPAddress, ".")[3],
127+
}
128+
hs.Statements = append(hs.Statements, HardwareStatement{HardwareType: "ethernet", HardwareAddress: *port.MacAddress})
129+
hs.Statements = append(hs.Statements, FixedAddressStatement{net.ParseIP(*port.IPAddress)})
130+
subnetStmt.Statements = append(subnetStmt.Statements, hs)
131+
}
132+
133+
content := fmt.Sprintf(dhcpdTemplate, subnetStmt.IndentedString(""))
134+
mutex.Lock()
135+
ioutil.WriteFile(file, []byte(content), 0644)
136+
mutex.Unlock()
137+
}
138+
139+
var Cmd = &cobra.Command{
140+
Use: "dhcp-sync",
141+
Short: "dhcp-sync command",
142+
Long: `dhcp-sync tool is a tool populating the dhcpd.conf file from the PowerVS network and restart the dhcpd service.`,
143+
PreRunE: func(cmd *cobra.Command, args []string) error {
144+
if pkg.Options.InstanceID == "" {
145+
return fmt.Errorf("--instance-id is required")
146+
}
147+
if pkg.Options.APIKey == "" {
148+
return fmt.Errorf("api-key can't be empty, pass the token via --api-key or set IBMCLOUD_API_KEY environment variable")
149+
}
150+
return nil
151+
},
152+
RunE: func(cmd *cobra.Command, args []string) error {
153+
go doEvery(2*time.Minute, syncDHCPD)
154+
155+
watcher, err := fsnotify.NewWatcher()
156+
if err != nil {
157+
klog.Fatal(err)
158+
}
159+
defer watcher.Close()
160+
161+
done := make(chan bool)
162+
go func() {
163+
for {
164+
select {
165+
case event, ok := <-watcher.Events:
166+
if !ok {
167+
return
168+
}
169+
klog.Info("event:", event)
170+
if event.Op&fsnotify.Write == fsnotify.Write {
171+
klog.Info("modified file:", event.Name)
172+
klog.Info("restarting the dhcpd service")
173+
exitcode, out, err := utils.RunCMD("systemctl", "restart", "dhcpd")
174+
if exitcode != 0 {
175+
klog.Errorf("failed to restart the dhcpd service, exitcode: %d, stdout: %s, err: %s", exitcode, out, err)
176+
}
177+
}
178+
case err, ok := <-watcher.Errors:
179+
if !ok {
180+
return
181+
}
182+
klog.Error("error:", err)
183+
}
184+
}
185+
}()
186+
187+
err = watcher.Add(file)
188+
if err != nil {
189+
klog.Fatal(err)
190+
}
191+
<-done
192+
return nil
193+
},
194+
}
195+
196+
func init() {
197+
Cmd.Flags().StringVarP(&pkg.Options.InstanceID, "instance-id", "i", "", "Instance ID of the PowerVS instance")
198+
Cmd.Flags().StringVar(&networkID, "network-id", "", "Network ID to be monitored")
199+
Cmd.Flags().StringVar(&file, "file", "/etc/dhcp/dhcpd.conf", "DHCP conf file")
200+
Cmd.Flags().StringVar(&gateway, "gateway", "", "Override the gateway value with")
201+
Cmd.Flags().StringVar(&nameservers, "nameservers", "", "Override the DNS nameservers")
202+
_ = Cmd.MarkFlagRequired("network-id")
203+
}

cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"github.com/ppc64le-cloud/pvsadm/cmd/create"
2828
deletecmd "github.com/ppc64le-cloud/pvsadm/cmd/delete"
29+
"github.com/ppc64le-cloud/pvsadm/cmd/dhcp-sync"
2930
"github.com/ppc64le-cloud/pvsadm/cmd/get"
3031
"github.com/ppc64le-cloud/pvsadm/cmd/image"
3132
"github.com/ppc64le-cloud/pvsadm/cmd/purge"
@@ -67,6 +68,7 @@ func init() {
6768
rootCmd.AddCommand(image.Cmd)
6869
rootCmd.AddCommand(create.Cmd)
6970
rootCmd.AddCommand(deletecmd.Cmd)
71+
rootCmd.AddCommand(dhcp.Cmd)
7072
rootCmd.PersistentFlags().StringVarP(&pkg.Options.APIKey, "api-key", "k", "", "IBMCLOUD API Key(env name: IBMCLOUD_API_KEY)")
7173
rootCmd.PersistentFlags().StringVar(&pkg.Options.Environment, "env", client.DefaultEnv, "IBM Cloud Environments, supported are: ["+strings.Join(client.ListEnvironments(), ", ")+"]")
7274
rootCmd.PersistentFlags().BoolVar(&pkg.Options.Debug, "debug", false, "Enable PowerVS debug option(ATTENTION: dev only option, may print sensitive data from APIs)")

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/IBM/ibm-cos-sdk-go v1.5.0
1111
github.com/IBM/platform-services-go-sdk v0.14.4
1212
github.com/dgrijalva/jwt-go v3.2.0+incompatible
13+
github.com/fsnotify/fsnotify v1.4.9
1314
github.com/go-openapi/strfmt v0.19.10
1415
github.com/klauspost/compress v1.11.1 // indirect
1516
github.com/klauspost/pgzip v1.2.5
@@ -18,6 +19,7 @@ require (
1819
github.com/onsi/ginkgo v1.14.2
1920
github.com/onsi/gomega v1.10.3
2021
github.com/ppc64le-cloud/powervs-utils v0.0.0-20210415051532-4cdd6a79c8fa
22+
github.com/sayotte/iscdhcp v0.0.0-20190926162140-d6be84ba9969
2123
github.com/spf13/cobra v1.0.0
2224
github.com/spf13/pflag v1.0.5
2325
k8s.io/apimachinery v0.20.0

0 commit comments

Comments
 (0)