Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically skip interface configuration if already set #17

Merged
merged 3 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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