From 48df1be79314bed3bf0bbf69a9e43a45e6b0aba1 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Thu, 22 May 2014 15:00:41 -0700 Subject: [PATCH] feat(convertNetconf): Add support for network config conversion Adding the flag -convertNetconf which is used to specify the config format to convert from (right now, only 'debian' is supported). Once the network configs are generated, they are written to systemd's runtime network directory and the network is restarted. --- coreos-cloudinit.go | 93 +++++++++++++++++++++++++++++++++++++++------ network/network.go | 41 -------------------- system/networkd.go | 89 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 53 deletions(-) create mode 100644 system/networkd.go diff --git a/coreos-cloudinit.go b/coreos-cloudinit.go index 6b44342..93cebc6 100644 --- a/coreos-cloudinit.go +++ b/coreos-cloudinit.go @@ -1,23 +1,21 @@ package main import ( + "encoding/json" "flag" "fmt" - "log" + "io/ioutil" "os" + "path" "github.com/coreos/coreos-cloudinit/datasource" "github.com/coreos/coreos-cloudinit/initialize" + "github.com/coreos/coreos-cloudinit/network" "github.com/coreos/coreos-cloudinit/system" ) const version = "0.7.1+git" -func init() { - //Removes timestamp since it is displayed already during booting - log.SetFlags(0) -} - func main() { var printVersion bool flag.BoolVar(&printVersion, "version", false, "Print the version and exit") @@ -37,6 +35,9 @@ func main() { var useProcCmdline bool flag.BoolVar(&useProcCmdline, "from-proc-cmdline", false, fmt.Sprintf("Parse %s for '%s=', using the cloud-config served by an HTTP GET to ", datasource.ProcCmdlineLocation, datasource.ProcCmdlineCloudConfigFlag)) + var convertNetconf string + flag.StringVar(&convertNetconf, "convert-netconf", "", "Read the network config provided in cloud-drive and translate it from the specified format into networkd unit files (requires the -from-configdrive flag)") + var workspace string flag.StringVar(&workspace, "workspace", "/var/lib/coreos-cloudinit", "Base directory coreos-cloudinit should use to store data") @@ -64,10 +65,23 @@ func main() { os.Exit(1) } - log.Printf("Fetching user-data from datasource of type %q", ds.Type()) + if convertNetconf != "" && configdrive == "" { + fmt.Println("-convert-netconf flag requires -from-configdrive") + os.Exit(1) + } + + switch convertNetconf { + case "": + case "debian": + default: + fmt.Printf("Invalid option to -convert-netconf: '%s'. Supported options: 'debian'\n", convertNetconf) + os.Exit(1) + } + + fmt.Printf("Fetching user-data from datasource of type %q\n", ds.Type()) userdataBytes, err := ds.Fetch() if err != nil { - log.Printf("Failed fetching user-data from datasource: %v", err) + fmt.Printf("Failed fetching user-data from datasource: %v\n", err) if ignoreFailure { os.Exit(0) } else { @@ -78,13 +92,22 @@ func main() { env := initialize.NewEnvironment("/", workspace) if len(userdataBytes) > 0 { if err := processUserdata(string(userdataBytes), env); err != nil { - log.Fatalf("Failed resolving user-data: %v", err) + fmt.Printf("Failed resolving user-data: %v\n", err) if !ignoreFailure { os.Exit(1) } } } else { - log.Printf("No user data to handle.") + fmt.Println("No user data to handle.") + } + + if convertNetconf != "" { + if err := processNetconf(convertNetconf, configdrive); err != nil { + fmt.Printf("Failed to process network config: %v\n", err) + if !ignoreFailure { + os.Exit(1) + } + } } } @@ -93,13 +116,14 @@ func processUserdata(userdata string, env *initialize.Environment) error { parsed, err := initialize.ParseUserData(userdata) if err != nil { - log.Printf("Failed parsing user-data: %v", err) + fmt.Printf("Failed parsing user-data: %v\n", err) return err } err = initialize.PrepWorkspace(env.Workspace()) if err != nil { - log.Fatalf("Failed preparing workspace: %v", err) + fmt.Printf("Failed preparing workspace: %v\n", err) + return err } switch t := parsed.(type) { @@ -117,3 +141,48 @@ func processUserdata(userdata string, env *initialize.Environment) error { return err } + +func processNetconf(convertNetconf, configdrive string) error { + openstackRoot := path.Join(configdrive, "openstack") + metadataFilename := path.Join(openstackRoot, "latest", "meta_data.json") + metadataBytes, err := ioutil.ReadFile(metadataFilename) + if err != nil { + return err + } + + var metadata struct { + NetworkConfig struct { + ContentPath string `json:"content_path"` + } `json:"network_config"` + } + if err := json.Unmarshal(metadataBytes, &metadata); err != nil { + return err + } + configPath := metadata.NetworkConfig.ContentPath + if configPath == "" { + fmt.Printf("No network config specified in %q.\n", metadataFilename) + return nil + } + + netconfBytes, err := ioutil.ReadFile(path.Join(openstackRoot, configPath)) + if err != nil { + return err + } + + var interfaces []network.InterfaceGenerator + switch convertNetconf { + case "debian": + interfaces, err = network.ProcessDebianNetconf(string(netconfBytes)) + default: + return fmt.Errorf("Unsupported network config format %q", convertNetconf) + } + + if err != nil { + return err + } + + if err := system.WriteNetworkdConfigs(interfaces); err != nil { + return err + } + return system.RestartNetwork(interfaces) +} diff --git a/network/network.go b/network/network.go index 2f23098..2af5f39 100644 --- a/network/network.go +++ b/network/network.go @@ -1,10 +1,6 @@ package network import ( - "fmt" - "io" - "os" - "path" "strings" ) @@ -26,43 +22,6 @@ func ProcessDebianNetconf(config string) ([]InterfaceGenerator, error) { return buildInterfaces(interfaces), nil } -func WriteConfigs(configPath string, interfaces []InterfaceGenerator) error { - if err := os.MkdirAll(configPath, os.ModePerm+os.ModeDir); err != nil { - fmt.Println(err) - os.Exit(1) - } - - for _, iface := range interfaces { - filename := path.Join(configPath, fmt.Sprintf("%s.netdev", iface.Name())) - if err := writeConfig(filename, iface.GenerateNetdevConfig()); err != nil { - return err - } - filename = path.Join(configPath, fmt.Sprintf("%s.link", iface.Name())) - if err := writeConfig(filename, iface.GenerateLinkConfig()); err != nil { - return err - } - filename = path.Join(configPath, fmt.Sprintf("%s.network", iface.Name())) - if err := writeConfig(filename, iface.GenerateNetworkConfig()); err != nil { - return err - } - } - return nil -} - -func writeConfig(filename string, config string) error { - if config == "" { - return nil - } - - if file, err := os.Create(filename); err == nil { - io.WriteString(file, config) - file.Close() - return nil - } else { - return err - } -} - func formatConfig(config string) []string { lines := []string{} config = strings.Replace(config, "\\\n", "", -1) diff --git a/system/networkd.go b/system/networkd.go new file mode 100644 index 0000000..c795563 --- /dev/null +++ b/system/networkd.go @@ -0,0 +1,89 @@ +package system + +import ( + "fmt" + "io/ioutil" + "net" + "os/exec" + "path" + + "github.com/coreos/coreos-cloudinit/network" + "github.com/coreos/coreos-cloudinit/third_party/github.com/dotcloud/docker/pkg/netlink" +) + +const ( + runtimeNetworkPath = "/run/systemd/network" +) + +func RestartNetwork(interfaces []network.InterfaceGenerator) (err error) { + defer func() { + if e := restartNetworkd(); e != nil { + err = e + } + }() + + if err = downNetworkInterfaces(interfaces); err != nil { + return + } + + if err = probe8012q(); err != nil { + return + } + return +} + +func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error { + sysInterfaceMap := make(map[string]*net.Interface) + if systemInterfaces, err := net.Interfaces(); err == nil { + for _, iface := range systemInterfaces { + sysInterfaceMap[iface.Name] = &iface + } + } else { + return err + } + + for _, iface := range interfaces { + if systemInterface, ok := sysInterfaceMap[iface.Name()]; ok { + if err := netlink.NetworkLinkDown(systemInterface); err != nil { + fmt.Printf("Error while downing interface %q (%s). Continuing...\n", systemInterface.Name, err) + } + } + } + + return nil +} + +func probe8012q() error { + return exec.Command("modprobe", "8021q").Run() +} + +func restartNetworkd() error { + _, err := RunUnitCommand("restart", "systemd-networkd.service") + return err +} + +func WriteNetworkdConfigs(interfaces []network.InterfaceGenerator) error { + for _, iface := range interfaces { + filename := path.Join(runtimeNetworkPath, fmt.Sprintf("%s.netdev", iface.Name())) + if err := writeConfig(filename, iface.Netdev()); err != nil { + return err + } + filename = path.Join(runtimeNetworkPath, fmt.Sprintf("%s.link", iface.Name())) + if err := writeConfig(filename, iface.Link()); err != nil { + return err + } + filename = path.Join(runtimeNetworkPath, fmt.Sprintf("%s.network", iface.Name())) + if err := writeConfig(filename, iface.Network()); err != nil { + return err + } + } + return nil +} + +func writeConfig(filename string, config string) error { + if config == "" { + return nil + } + + return ioutil.WriteFile(filename, []byte(config), 0444) +}