Skip to content

Commit

Permalink
Merge pull request #17 from buty4649/fix-error-rerun-netplan-apply
Browse files Browse the repository at this point in the history
Automatically skip interface configuration if already set
  • Loading branch information
buty4649 authored Mar 24, 2024
2 parents 11501c2 + 12c4af1 commit 2539fc0
Show file tree
Hide file tree
Showing 6 changed files with 624 additions and 73 deletions.
179 changes: 139 additions & 40 deletions cmd/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,27 @@ SOFTWARE.
package cmd

import (
"fmt"
"log/slog"
"netnsplan/config"
"netnsplan/iproute2"
"slices"

"github.com/spf13/cobra"
)

// applyCmd represents the apply command
var alwaysRunPostScript bool

var applyCmd = &cobra.Command{
Use: "apply",
Short: "Apply netns networks configuration to running system",
Long: "Apply netns networks configuration to running system",
RunE: func(cmd *cobra.Command, args []string) error {
for netns, values := range cfg.Netns {
needPostScript := true
if ip.ExistsNetns(netns) {
slog.Warn("netns is already exists", "name", netns)
needPostScript = false
} else {
slog.Info("create netns", "name", netns)
err := ip.AddNetns(netns)
Expand Down Expand Up @@ -65,18 +71,28 @@ var applyCmd = &cobra.Command{
return err
}

err = RunPostScript(netns, values.PostScript)
if err != nil {
return err
if needPostScript || alwaysRunPostScript {
err = RunPostScript(netns, values.PostScript)
if err != nil {
return err
}
}
}
return nil
},
}

func init() {
rootCmd.AddCommand(applyCmd)
applyCmd.Flags().BoolVarP(&alwaysRunPostScript, "always-run-post-script", "R", false, "always run post-script. by default, runs only when a netns is created.")
}

type IpCommand interface {
SetLinkUp(name string) error
ShowLink(name string) (*iproute2.Link, error)
ShowInterface(name string) (*iproute2.InterfaceInfo, error)
AddAddress(name, address string) error
ShowRoutes(name string) (iproute2.Routes, error)
AddRoute(name, to, via string) error
InNetns() bool
Netns() string
Expand All @@ -88,19 +104,48 @@ func SetupDevice(ip IpCommand, name string, addresses []string, routes []config.
return err
}

if ip.InNetns() {
slog.Info("add addresses", "name", name, "addresses", addresses, "netns", ip.Netns())
} else {
slog.Info("add addresses", "name", name, "addresses", addresses)
iface, err := ip.ShowInterface(name)
if err != nil {
return err
}

var intAddrs []string
for _, i := range iface.AddrInfo {
intAddrs = append(intAddrs, fmt.Sprintf("%s/%d", i.Local, i.Prefixlen))
}

for _, address := range addresses {
err := ip.AddAddress(name, address)
if slices.Contains(intAddrs, address) {
slog.Debug("address is already exists", "name", name, "address", address)
continue
}

if ip.InNetns() {
slog.Info("add addresses", "name", name, "address", address, "netns", ip.Netns())
} else {
slog.Info("add addresses", "name", name, "address", address)
}

err = ip.AddAddress(name, address)
if err != nil {
return err
}
}

rt, err := ip.ShowRoutes(name)
if err != nil {
return err
}

for _, route := range routes {
slog.Debug("route", "name", name, "to", route.To, "via", route.Via, "rt", rt)
if slices.ContainsFunc(rt, func(r iproute2.Route) bool {
return r.Dst == route.To && r.Gateway == route.Via
}) {
slog.Debug("route is already exists", "name", name, "to", route.To, "via", route.Via)
continue
}

if ip.InNetns() {
slog.Info("add route", "name", name, "to", route.To, "via", route.Via, "netns", ip.Netns())
} else {
Expand All @@ -115,6 +160,16 @@ func SetupDevice(ip IpCommand, name string, addresses []string, routes []config.
}

func SetLinkUp(ip IpCommand, name string) error {
link, err := ip.ShowLink(name)
if err != nil {
return err
}

if slices.Contains(link.Flags, "UP") {
slog.Debug("link is already up", "name", name)
return nil
}

if ip.InNetns() {
slog.Info("link up", "name", name, "netns", ip.Netns())
} else {
Expand All @@ -124,23 +179,33 @@ func SetLinkUp(ip IpCommand, name string) error {
return ip.SetLinkUp(name)
}

func SetupLoopback(netns string) error {
return SetLinkUp(ip.IntoNetns(netns), "lo")
}

func SetNetns(name string, netns string) error {
slog.Info("set netns", "name", name, "netns", netns)
return ip.SetNetns(name, netns)
}

func SetupLoopback(netns string) error {
return SetLinkUp(ip.IntoNetns(netns), "lo")
}

func SetupEthernets(netns string, ethernets map[string]config.Ethernet) error {
n := ip.IntoNetns(netns)
for name, values := range ethernets {
err := SetNetns(name, netns)
if err != nil {
return err
_, err := n.ShowLink(name)
if err == nil {
slog.Debug("device is already exists in netns", "name", name, "netns", netns)
} else {
if _, ok := err.(*iproute2.NotExistError); ok {
err := SetNetns(name, netns)
if err != nil {
return err
}
} else {
return err
}
}

err = SetupDevice(ip.IntoNetns(netns), name, values.Addresses, values.Routes)
err = SetupDevice(n, name, values.Addresses, values.Routes)
if err != nil {
return err
}
Expand All @@ -149,13 +214,21 @@ func SetupEthernets(netns string, ethernets map[string]config.Ethernet) error {
}

func SetupDummyDevices(netns string, devices map[string]config.Ethernet) error {
n := ip.IntoNetns(netns)
for name, values := range devices {
n := ip.IntoNetns(netns)

slog.Info("add dummy device", "name", name, "netns", netns)
err := n.AddDummyDevice(name)
if err != nil {
return err
_, err := n.ShowLink(name)
if err == nil {
slog.Debug("device is already exists in netns", "name", name, "netns", netns)
} else {
if _, ok := err.(*iproute2.NotExistError); !ok {
return err
} else {
slog.Info("add dummy device", "name", name, "netns", netns)
err := n.AddDummyDevice(name)
if err != nil {
return err
}
}
}

err = SetupDevice(n, name, values.Addresses, values.Routes)
Expand All @@ -167,33 +240,63 @@ func SetupDummyDevices(netns string, devices map[string]config.Ethernet) error {
}

func SetupVethDevices(netns string, devices map[string]config.VethDevice) error {
n := ip.IntoNetns(netns)
for name, values := range devices {
peerName := values.Peer.Name
peerNetns := values.Peer.Netns

slog.Info("add veth device", "name", name, "peer name", peerName)
err := ip.AddVethDevice(name, peerName)
if err != nil {
return err
}
// check if device is already exists in netns
_, err := n.ShowLink(name)
if err == nil {
slog.Debug("device is already exists in netns", "name", name, "netns", netns)
} else {
if _, ok := err.(*iproute2.NotExistError); !ok {
return err
} else {
// check if device is already exists in "default" netns
_, e := ip.ShowLink(name)
if e == nil {
slog.Debug("device is already exists", "name", name)
} else {
if _, ok := err.(*iproute2.NotExistError); !ok {
return err
} else {
slog.Info("add veth device", "name", name, "peer name", peerName)
err := ip.AddVethDevice(name, peerName)
if err != nil {
return err
}
}
}

err = SetNetns(name, netns)
if err != nil {
return err
err = SetNetns(name, netns)
if err != nil {
return err
}
}
}

n := ip.IntoNetns(netns)
err = SetupDevice(n, name, values.Addresses, values.Routes)
if err != nil {
return err
}

if peerNetns != "" {
err = SetNetns(peerName, peerNetns)
if err != nil {
return err
}
n := ip.IntoNetns(peerNetns)

_, err := n.ShowLink(peerName)
if err == nil {
slog.Debug("device is already exists in netns", "name", peerName, "netns", peerNetns)
} else {
if _, ok := err.(*iproute2.NotExistError); !ok {
return err
} else {
err = SetNetns(peerName, peerNetns)
if err != nil {
return err
}
}
}
err = SetupDevice(n, peerName, values.Peer.Addresses, values.Peer.Routes)
if err != nil {
return err
Expand All @@ -208,10 +311,6 @@ func SetupVethDevices(netns string, devices map[string]config.VethDevice) error
return nil
}

func init() {
rootCmd.AddCommand(applyCmd)
}

func RunPostScript(netns string, script string) error {
if script == "" {
return nil
Expand Down
Loading

0 comments on commit 2539fc0

Please sign in to comment.