diff --git a/internal/app/wwctl/node/imprt/main.go b/internal/app/wwctl/node/imprt/main.go index 76e422a7..647d84cb 100644 --- a/internal/app/wwctl/node/imprt/main.go +++ b/internal/app/wwctl/node/imprt/main.go @@ -1,6 +1,8 @@ package imprt import ( + "bytes" + "encoding/csv" "fmt" "io" "os" @@ -28,18 +30,68 @@ func CobraRunE(cmd *cobra.Command, args []string) error { wwlog.Error("Could not read:%s\n", err) os.Exit(1) } - err = yaml.Unmarshal(buffer, importMap) - if err == nil { - yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes", len(importMap))) + if !ImportCVS { + err = yaml.Unmarshal(buffer, importMap) + if err == nil { + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes", len(importMap))) + if yes { + err = apinode.NodeAddFromYaml(&wwapiv1.NodeYaml{NodeConfMapYaml: string(buffer)}) + if err != nil { + wwlog.Error("Got following problem when writing back yaml: %s", err) + os.Exit(1) + } + } + } else { + wwlog.Error("Could not parse import file") + } + } else { + // reading from buffer is a bit overshot + csvReader := csv.NewReader(bytes.NewReader(buffer)) + records, err := csvReader.ReadAll() + if err != nil { + wwlog.Error("Could not parse %s: %s\n", args[0], err) + os.Exit(1) + } + if len(records) < 1 || len(records[0]) < 1 { + wwlog.Error("Did not find any data in %s\n", args[0]) + os.Exit(1) + } + if !(records[0][0] == "node" || records[0][0] == "nodename") { + Usage() + os.Exit(1) + } + argsLen := len(records[0]) + for i, line := range records[1:] { + if len(line) != argsLen { + wwlog.Error("Wrong number of fields in lube %u\n", i+1) + os.Exit(1) + } + for j := range line { + if j == 0 { + continue + } + if importMap[line[0]] == nil { + importMap[line[0]] = new(node.NodeConf) + } + ok := importMap[line[0]].SetLopt(records[0][j], line[j]) + if !(ok) { + wwlog.Debug("Could not import %s\n", line[j]) + } + } + } + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to import %d nodes", len(importMap))) if yes { + // create second buffer an marshall nodeMap to it + buffer, err = yaml.Marshal(importMap) + if err != nil { + wwlog.Error("Got following problem when creating yaml: %s", err) + } err = apinode.NodeAddFromYaml(&wwapiv1.NodeYaml{NodeConfMapYaml: string(buffer)}) if err != nil { wwlog.Error("Got following problem when writing back yaml: %s", err) os.Exit(1) } } - } else { - wwlog.Error("Could not parse import file") } return nil diff --git a/internal/app/wwctl/node/imprt/root.go b/internal/app/wwctl/node/imprt/root.go index 393958a4..2332e8fd 100644 --- a/internal/app/wwctl/node/imprt/root.go +++ b/internal/app/wwctl/node/imprt/root.go @@ -1,6 +1,8 @@ package imprt import ( + "fmt" + "github.com/spf13/cobra" ) @@ -12,14 +14,22 @@ var ( Long: "This command imports all the nodes defined in a file. It will overwrite nodes with same name.", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), - Aliases: []string{"imprt"}, + Aliases: []string{"import"}, } + ImportCVS bool ) func init() { + baseCmd.PersistentFlags().BoolVarP(&ImportCVS, "cvs", "c", false, "Import CVS file") } // GetRootCommand returns the root cobra.Command for the application. func GetCommand() *cobra.Command { return baseCmd } + +func Usage() { + fmt.Println(`The csv file must be structured in following way: +node,option1,option2,net.netname1.netopt +node01,value1,value2,net.netname1,netvalue`) +} diff --git a/internal/pkg/node/transformers.go b/internal/pkg/node/transformers.go index 75d9d1d2..48ce585b 100644 --- a/internal/pkg/node/transformers.go +++ b/internal/pkg/node/transformers.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" ) @@ -525,3 +526,64 @@ func getYamlString(myType reflect.StructField, excludeList []string) ([]string, } return []string{ymlStr}, true } + +/* +Set the field of the NodeConf with the given lopt name, returns true if the +field was found. String slices must be comma separated. Network must have the form +net.$NETNAME.lopt or netname.$NETNAME.lopt +*/ +func (nodeConf *NodeConf) SetLopt(lopt string, value string) (found bool) { + found = false + nodeInfoType := reflect.TypeOf(nodeConf) + nodeInfoVal := reflect.ValueOf(nodeConf) + // try to find the normal fields, networks come later + for i := 0; i < nodeInfoVal.Elem().NumField(); i++ { + //fmt.Println(nodeInfoType.Elem().Field(i).Tag.Get("lopt"), lopt) + if nodeInfoType.Elem().Field(i).Tag.Get("lopt") == lopt { + if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.String { + wwlog.Verbose("Found lopt %s mapping to %s, setting to %s\n", + lopt, nodeInfoType.Elem().Field(i).Name, value) + confVal := nodeInfoVal.Elem().Field(i).Addr().Interface().(*string) + *confVal = value + found = true + } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf([]string{}) { + wwlog.Verbose("Found lopt %s mapping to %s, setting to %s\n", + lopt, nodeInfoType.Elem().Field(i).Name, value) + confVal := nodeInfoVal.Elem().Field(i).Addr().Interface().(*[]string) + *confVal = strings.Split(value, ",") + found = true + } + } + } + // check network + loptSlice := strings.Split(lopt, ".") + wwlog.Debug("Trying to get network out of %s\n", loptSlice) + if !found && len(loptSlice) == 3 && (loptSlice[0] == "net" || loptSlice[0] == "network" || loptSlice[0] == "netname") { + if nodeConf.NetDevs == nil { + nodeConf.NetDevs = make(map[string]*NetDevs) + } + if nodeConf.NetDevs[loptSlice[1]] == nil { + nodeConf.NetDevs[loptSlice[1]] = new(NetDevs) + } + netInfoType := reflect.TypeOf(nodeConf.NetDevs[loptSlice[1]]) + netInfoVal := reflect.ValueOf(nodeConf.NetDevs[loptSlice[1]]) + for i := 0; i < netInfoVal.Elem().NumField(); i++ { + if netInfoType.Elem().Field(i).Tag.Get("lopt") == loptSlice[2] { + if netInfoType.Elem().Field(i).Type.Kind() == reflect.String { + wwlog.Verbose("Found lopt %s for network %s mapping to %s, setting to %s\n", + lopt, loptSlice[1], netInfoType.Elem().Field(i).Name, value) + confVal := netInfoVal.Elem().Field(i).Addr().Interface().(*string) + *confVal = value + found = true + } else if netInfoType.Elem().Field(i).Type == reflect.TypeOf([]string{}) { + wwlog.Verbose("Found lopt %s for network %s mapping to %s, setting to %s\n", + lopt, loptSlice[1], netInfoType.Elem().Field(i).Name, value) + confVal := netInfoVal.Elem().Field(i).Addr().Interface().(*[]string) + *confVal = strings.Split(value, ",") + found = true + } + } + } + } + return found +}