diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 48c29664..49ba564c 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -6,6 +6,7 @@ on: jobs: publish: + if: ${{ github.repository_owner == 'hcpng' }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -51,10 +52,10 @@ jobs: - name: Update website repo run: | git clone git@github.com:hpcng/warewulf-web.git ~/warewulf-web - mkdir -p ~/warewulf-web/src/static/docs - rm -rf ~/warewulf-web/src/static/docs/${GITHUB_REF##*/} - cp -r userdocs/_build/html ~/warewulf-web/src/static/docs/${GITHUB_REF##*/} + mkdir -p ~/warewulf-web/static/docs + rm -rf ~/warewulf-web/static/docs/${GITHUB_REF##*/} + cp -r userdocs/_build/html ~/warewulf-web/static/docs/${GITHUB_REF##*/} cd ~/warewulf-web - git add src/static/docs/${GITHUB_REF##*/} + git add static/docs/${GITHUB_REF##*/} git commit -m "Update ${GITHUB_REF##*/} docs" git push diff --git a/.gitignore b/.gitignore index e07c1275..fa4124cb 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ /wwapic /wwapid /wwapird +/print_defaults # other created files /man_pages diff --git a/CHANGELOG.md b/CHANGELOG.md index abc1ea9a..194deb06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- Changed internal interacation between the on disk YAML format and the in memory + format. This enables the command line options to be autogenerated from the datastructures. + Also this keeps the options for profiles and node set/add commands in strict sync. +- Added `node edit/export/import` where the edit command exposes the YAML config for the + given nodes in an editor. Nodes can also be added with this command. The export command + simply drops the YAML config for the given nodes. With the import command nodes can be + imported as YAML or as a CSV file. The CSV file must havr a header in where the first field + must always be the nodename, the rest of the fields are the same as the long commandline + options. Network device must have the form net.$NETNAME.$NETOPTION, which would e.g. be + net.default.ipaddr +- The default value (like kernel args) are no read in from the configuration file + default.conf. If this file couldn't be read built in default values will be used. +- Template files for NetworkManager are now present in the wwinit overlay. A interface must + have the type ethernet or infiniband that these will be brought up by NetworkManager +- Listings of container via `wwctl container list` will now show the creation,modification + date and the size. + +### Bug fixes +- For the connections of `wwctl ssh` the primary interface will be used +- Fixed kernel version in `wwctl container show` +- remove the container image and not only the rootfs dir on delete +- include and exclude handling for container were fixed + +## [4.3.0] 2022-06-25 ### Added - All configurations files for the host (/etc/exports, /etc/dhcpd.conf, /etc/hosts) are now populated from the (OVERLAYDIR/host/etc/{exports|dhcpd|hosts}.ww . Also other configuration @@ -41,6 +66,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - GID is no longer changed to `0` when unspecified during chown - Proper handling of uid/gid arguments during `wwctl overlay chown` +- /etc/warewulf/excludes stored in the container definition is now + processed correctly, also excluding the contents of an excluded + directory automatically. ## [4.1.0] - 2021-07-29 diff --git a/Makefile b/Makefile index 00a48716..add0a600 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,7 @@ export GOPROXY # built tags needed for wwbuild binary WW_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree -all: config vendor wwctl wwclient bash_completion.d man_pages config_defaults wwapid wwapic wwapird +all: config vendor wwctl wwclient bash_completion.d man_pages config_defaults print_defaults wwapid wwapic wwapird build: lint test-it vet all @@ -174,6 +174,7 @@ files: all test -f $(DESTDIR)$(SYSCONFDIR)/warewulf/wwapic.conf || install -m 644 etc/wwapic.conf $(DESTDIR)$(SYSCONFDIR)/warewulf/ test -f $(DESTDIR)$(SYSCONFDIR)/warewulf/wwapid.conf || install -m 644 etc/wwapid.conf $(DESTDIR)$(SYSCONFDIR)/warewulf/ test -f $(DESTDIR)$(SYSCONFDIR)/warewulf/wwapird.conf || install -m 644 etc/wwapird.conf $(DESTDIR)$(SYSCONFDIR)/warewulf/ + test -f $(DESTDIR)$(SYSCONFDIR)/warewulf/defaults.conf || ./print_defaults > $(DESTDIR)$(SYSCONFDIR)/warewulf/defaults.conf cp -r etc/examples $(DESTDIR)$(WWCONFIGDIR)/ cp -r etc/ipxe $(DESTDIR)$(WWCONFIGDIR)/ cp -r overlays/* $(DESTDIR)$(WWOVERLAYDIR)/ @@ -237,6 +238,9 @@ config_defaults: vendor cmd/config_defaults/config_defaults.go -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ -mod vendor -tags "$(WW_GO_BUILD_TAGS)" -o ../../config_defaults +print_defaults: vendor cmd/print_defaults/print_defaults.go + cd cmd/print_defaults && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'" -o ../../print_defaults + update_configuration: vendor cmd/update_configuration/update_configuration.go cd cmd/update_configuration && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=./etc/warewulf.conf'\ -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=./etc/nodes.conf'"\ @@ -297,6 +301,7 @@ clean: rm -rf $(TOOLS_DIR) rm -f config_defaults rm -f update_configuration + rm -f print_defaults install: files install_wwclient diff --git a/cmd/print_defaults/print_defaults.go b/cmd/print_defaults/print_defaults.go new file mode 100644 index 00000000..397294dd --- /dev/null +++ b/cmd/print_defaults/print_defaults.go @@ -0,0 +1,17 @@ +package main + +import ( + "fmt" + + "github.com/hpcng/warewulf/internal/pkg/node" +) + +/* +Print the build in defaults for the nodes. +Called via Makefile so that there is single upstream +source of the defaults which is FallBackConf +*/ + +func main() { + fmt.Println(node.FallBackConf) +} diff --git a/include/systemd/warewulfd.service.in b/include/systemd/warewulfd.service.in index a343a0df..27b121a4 100644 --- a/include/systemd/warewulfd.service.in +++ b/include/systemd/warewulfd.service.in @@ -10,6 +10,9 @@ User=root Group=root ExecStart=@BINDIR@/wwctl server start +ExecReload=/usr/bin/wwctl server reload +ExecStop=/usr/bin/wwctl server stop + PIDFile=/var/run/warewulfd.pid Restart=always diff --git a/internal/app/wwctl/container/list/main.go b/internal/app/wwctl/container/list/main.go index 50d94448..56b468d7 100644 --- a/internal/app/wwctl/container/list/main.go +++ b/internal/app/wwctl/container/list/main.go @@ -2,8 +2,10 @@ package list import ( "fmt" + "time" "github.com/hpcng/warewulf/internal/pkg/api/container" + "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" ) @@ -16,12 +18,17 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) { return } - fmt.Printf("%-25s %-6s %-6s\n", "CONTAINER NAME", "NODES", "KERNEL VERSION") + fmt.Printf("%-16s %-6s %-16s %-20s %-20s %-8s\n", "CONTAINER NAME", "NODES", "KERNEL VERSION", "CREATION TIME", "MODIFICATION TIME", "SIZE") for i := 0; i < len(containerInfo); i++ { - fmt.Printf("%-25s %-6d %-6s\n", + createTime := time.Unix(int64(containerInfo[i].CreateDate), 0) + modTime := time.Unix(int64(containerInfo[i].ModDate), 0) + fmt.Printf("%-16s %-6d %-16s %-20s %-20s %-8s\n", containerInfo[i].Name, containerInfo[i].NodeCount, - containerInfo[i].KernelVersion) + containerInfo[i].KernelVersion, + createTime.Format(time.RFC822), + modTime.Format(time.RFC822), + util.ByteToString(int64(containerInfo[i].Size))) } return } diff --git a/internal/app/wwctl/node/edit/main.go b/internal/app/wwctl/node/edit/main.go new file mode 100644 index 00000000..9307edef --- /dev/null +++ b/internal/app/wwctl/node/edit/main.go @@ -0,0 +1,115 @@ +package edit + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "os" + "strings" + + apinode "github.com/hpcng/warewulf/internal/pkg/api/node" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + apiutil "github.com/hpcng/warewulf/internal/pkg/api/util" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" + "gopkg.in/yaml.v2" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + canWrite := apiutil.CanWriteConfig() + if !canWrite.CanWriteConfig { + wwlog.Error("Can't write to config exiting") + os.Exit(1) + } + editor := os.Getenv("EDITOR") + if editor == "" { + editor = "/bin/vi" + } + if len(args) == 0 { + args = append(args, ".*") + } + filterList := wwapiv1.NodeList{ + Output: args, + } + nodeListMsg := apinode.FilteredNodes(&filterList) + nodeMap := make(map[string]*node.NodeConf) + // got proper yaml back + _ = yaml.Unmarshal([]byte(nodeListMsg.NodeConfMapYaml), nodeMap) + file, err := ioutil.TempFile("/tmp", "ww4NodeEdit*.yaml") + if err != nil { + wwlog.Error("Could not create temp file:%s \n", err) + } + defer os.Remove(file.Name()) + nodeConf := node.NewConf() + yamlTemplate := nodeConf.UnmarshalConf([]string{"tagsdel", "default", "profiles"}) + for { + _ = file.Truncate(0) + _, _ = file.Seek(0, 0) + if !NoHeader { + _, _ = file.WriteString("#nodename:\n# " + strings.Join(yamlTemplate, "\n# ") + "\n") + } + _, _ = file.WriteString(nodeListMsg.NodeConfMapYaml) + _, _ = file.Seek(0, 0) + hasher := sha256.New() + if _, err := io.Copy(hasher, file); err != nil { + wwlog.Error("Problems getting checksum of file %s\n", err) + } + sum1 := hex.EncodeToString(hasher.Sum(nil)) + err = util.ExecInteractive(editor, file.Name()) + if err != nil { + wwlog.Error("Editor process existed with non-zero\n") + os.Exit(1) + } + _, _ = file.Seek(0, 0) + hasher.Reset() + if _, err := io.Copy(hasher, file); err != nil { + wwlog.Error("Problems getting checksum of file %s\n", err) + } + sum2 := hex.EncodeToString(hasher.Sum(nil)) + wwlog.Debug("Hashes are before %s and after %s\n", sum1, sum2) + if sum1 != sum2 { + wwlog.Debug("Nodes were modified") + modifiedNodeMap := make(map[string]*node.NodeConf) + _, _ = file.Seek(0, 0) + // ignore error as only may occurs under strange circumstances + buffer, _ := io.ReadAll(file) + err = yaml.Unmarshal(buffer, modifiedNodeMap) + if err == nil { + nodeList := make([]string, len(nodeMap)) + i := 0 + for key := range nodeMap { + nodeList[i] = key + i++ + } + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes", len(modifiedNodeMap))) + if !yes { + break + } + err = apinode.NodeDelete(&wwapiv1.NodeDeleteParameter{NodeNames: nodeList, Force: true}) + if err != nil { + wwlog.Verbose("Problem deleting nodes before modification %s") + } + buffer, _ = yaml.Marshal(modifiedNodeMap) + 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) + } + break + } else { + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Got following error on parsing: %s, Retry", err)) + if !yes { + break + } + } + } else { + break + } + } + + return nil +} diff --git a/internal/app/wwctl/node/edit/root.go b/internal/app/wwctl/node/edit/root.go new file mode 100644 index 00000000..387b7cc2 --- /dev/null +++ b/internal/app/wwctl/node/edit/root.go @@ -0,0 +1,39 @@ +package edit + +import ( + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/spf13/cobra" +) + +var ( + baseCmd = &cobra.Command{ + DisableFlagsInUseLine: true, + Use: "edit [OPTIONS] NODENAME", + Short: "Edit node(s) with editor", + Long: "This command opens an editor for the given nodes.", + RunE: CobraRunE, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + nodeDB, _ := node.New() + nodes, _ := nodeDB.FindAllNodes() + var node_names []string + for _, node := range nodes { + node_names = append(node_names, node.Id.Get()) + } + return node_names, cobra.ShellCompDirectiveNoFileComp + }, + } + NoHeader bool +) + +func init() { + baseCmd.PersistentFlags().BoolVar(&NoHeader, "noheader", false, "Do not print header") +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/node/export/main.go b/internal/app/wwctl/node/export/main.go new file mode 100644 index 00000000..1645683c --- /dev/null +++ b/internal/app/wwctl/node/export/main.go @@ -0,0 +1,26 @@ +package export + +import ( + "fmt" + + apinode "github.com/hpcng/warewulf/internal/pkg/api/node" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/spf13/cobra" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + args = append(args, ".*") + } + filterList := wwapiv1.NodeList{ + Output: args, + } + nodeListMsg := apinode.FilteredNodes(&filterList) + /* + nodeMap := make(map[string]*node.NodeConf) + // got proper yaml back + _ = yaml.Unmarshal([]byte(nodeListMsg.NodeConfMapYaml), nodeMap) + */ + fmt.Println(nodeListMsg.NodeConfMapYaml) + return nil +} diff --git a/internal/app/wwctl/node/export/root.go b/internal/app/wwctl/node/export/root.go new file mode 100644 index 00000000..ce423830 --- /dev/null +++ b/internal/app/wwctl/node/export/root.go @@ -0,0 +1,38 @@ +package export + +import ( + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/spf13/cobra" +) + +var ( + baseCmd = &cobra.Command{ + DisableFlagsInUseLine: true, + Use: "export NODENAME", + Short: "Export nodes as yaml to stdout", + Long: "This command exports the given nodes as yaml to stdout.", + RunE: CobraRunE, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + nodeDB, _ := node.New() + nodes, _ := nodeDB.FindAllNodes() + var node_names []string + for _, node := range nodes { + node_names = append(node_names, node.Id.Get()) + } + return node_names, cobra.ShellCompDirectiveNoFileComp + }, + } + NoHeader bool +) + +func init() { +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/node/imprt/main.go b/internal/app/wwctl/node/imprt/main.go new file mode 100644 index 00000000..647d84cb --- /dev/null +++ b/internal/app/wwctl/node/imprt/main.go @@ -0,0 +1,98 @@ +package imprt + +import ( + "bytes" + "encoding/csv" + "fmt" + "io" + "os" + + apinode "github.com/hpcng/warewulf/internal/pkg/api/node" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + apiutil "github.com/hpcng/warewulf/internal/pkg/api/util" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" + "gopkg.in/yaml.v2" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + file, err := os.Open(args[0]) + if err != nil { + wwlog.Error("Could not open file:%s \n", err) + os.Exit(1) + } + defer file.Close() + + importMap := make(map[string]*node.NodeConf) + buffer, err := io.ReadAll(file) + if err != nil { + wwlog.Error("Could not read:%s\n", err) + os.Exit(1) + } + 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) + } + } + } + + return nil +} diff --git a/internal/app/wwctl/node/imprt/root.go b/internal/app/wwctl/node/imprt/root.go new file mode 100644 index 00000000..2332e8fd --- /dev/null +++ b/internal/app/wwctl/node/imprt/root.go @@ -0,0 +1,35 @@ +package imprt + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +var ( + baseCmd = &cobra.Command{ + DisableFlagsInUseLine: true, + Use: "import [OPTIONS] NODENAME", + Short: "Import node(s) from yaml file", + 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{"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/app/wwctl/node/root.go b/internal/app/wwctl/node/root.go index 804c6320..ffc1f80b 100644 --- a/internal/app/wwctl/node/root.go +++ b/internal/app/wwctl/node/root.go @@ -4,6 +4,9 @@ import ( "github.com/hpcng/warewulf/internal/app/wwctl/node/add" "github.com/hpcng/warewulf/internal/app/wwctl/node/console" "github.com/hpcng/warewulf/internal/app/wwctl/node/delete" + "github.com/hpcng/warewulf/internal/app/wwctl/node/edit" + "github.com/hpcng/warewulf/internal/app/wwctl/node/export" + "github.com/hpcng/warewulf/internal/app/wwctl/node/imprt" "github.com/hpcng/warewulf/internal/app/wwctl/node/list" "github.com/hpcng/warewulf/internal/app/wwctl/node/sensors" "github.com/hpcng/warewulf/internal/app/wwctl/node/set" @@ -30,6 +33,9 @@ func init() { baseCmd.AddCommand(delete.GetCommand()) baseCmd.AddCommand(console.GetCommand()) baseCmd.AddCommand(nodestatus.GetCommand()) + baseCmd.AddCommand(edit.GetCommand()) + baseCmd.AddCommand(imprt.GetCommand()) + baseCmd.AddCommand(export.GetCommand()) } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/profile/delete/root.go b/internal/app/wwctl/profile/delete/root.go index a5cea9e1..806a8d68 100644 --- a/internal/app/wwctl/profile/delete/root.go +++ b/internal/app/wwctl/profile/delete/root.go @@ -1,15 +1,30 @@ package delete -import "github.com/spf13/cobra" +import ( + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/spf13/cobra" +) var ( baseCmd = &cobra.Command{ DisableFlagsInUseLine: true, - Use: "delete [OPTIONS] PROFILE", - Short: "Delete a node profile", - Long: "This command deletes the node PROFILE. You may use a pattern for PROFILE.", - RunE: CobraRunE, - Args: cobra.MinimumNArgs(1), + Use: "delete [OPTIONS] PROFILE", + Short: "Delete a node profile", + Long: "This command deletes the node PROFILE. You may use a pattern for PROFILE.", + RunE: CobraRunE, + Args: cobra.MinimumNArgs(1), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + nodeDB, _ := node.New() + profiles, _ := nodeDB.FindAllProfiles() + var p_names []string + for _, profile := range profiles { + p_names = append(p_names, profile.Id.Get()) + } + return p_names, cobra.ShellCompDirectiveNoFileComp + }, } SetYes bool ) diff --git a/internal/app/wwctl/profile/edit/main.go b/internal/app/wwctl/profile/edit/main.go new file mode 100644 index 00000000..a5694e5d --- /dev/null +++ b/internal/app/wwctl/profile/edit/main.go @@ -0,0 +1,115 @@ +package edit + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "os" + "strings" + + apiprofile "github.com/hpcng/warewulf/internal/pkg/api/profile" + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + apiutil "github.com/hpcng/warewulf/internal/pkg/api/util" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" + "gopkg.in/yaml.v2" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + canWrite := apiutil.CanWriteConfig() + if !canWrite.CanWriteConfig { + wwlog.Error("Can't write to config exiting") + os.Exit(1) + } + editor := os.Getenv("EDITOR") + if editor == "" { + editor = "/bin/vi" + } + if len(args) == 0 { + args = append(args, ".*") + } + filterList := wwapiv1.NodeList{ + Output: args, + } + profileListMsg := apiprofile.FilteredProfiles(&filterList) + profileMap := make(map[string]*node.NodeConf) + // got proper yaml back + _ = yaml.Unmarshal([]byte(profileListMsg.NodeConfMapYaml), profileMap) + file, err := ioutil.TempFile("/tmp", "ww4ProfileEdit*.yaml") + if err != nil { + wwlog.Error("Could not create temp file:%s \n", err) + } + defer os.Remove(file.Name()) + nodeConf := node.NewConf() + yamlTemplate := nodeConf.UnmarshalConf([]string{"tagsdel"}) + for { + _ = file.Truncate(0) + _, _ = file.Seek(0, 0) + if !NoHeader { + _, _ = file.WriteString("#profilename:\n# " + strings.Join(yamlTemplate, "\n# ") + "\n") + } + _, _ = file.WriteString(profileListMsg.NodeConfMapYaml) + _, _ = file.Seek(0, 0) + hasher := sha256.New() + if _, err := io.Copy(hasher, file); err != nil { + wwlog.Error("Problems getting checksum of file %s\n", err) + } + sum1 := hex.EncodeToString(hasher.Sum(nil)) + err = util.ExecInteractive(editor, file.Name()) + if err != nil { + wwlog.Error("Editor process existed with non-zero\n") + os.Exit(1) + } + _, _ = file.Seek(0, 0) + hasher.Reset() + if _, err := io.Copy(hasher, file); err != nil { + wwlog.Error("Problems getting checksum of file %s\n", err) + } + sum2 := hex.EncodeToString(hasher.Sum(nil)) + wwlog.Debug("Hashes are before %s and after %s\n", sum1, sum2) + if sum1 != sum2 { + wwlog.Debug("Nodes were modified") + modifiedProfileMap := make(map[string]*node.NodeConf) + _, _ = file.Seek(0, 0) + // ignore error as only may occurs under strange circumstances + buffer, _ := io.ReadAll(file) + err = yaml.Unmarshal(buffer, modifiedProfileMap) + if err == nil { + nodeList := make([]string, len(profileMap)) + i := 0 + for key := range profileMap { + nodeList[i] = key + i++ + } + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Are you sure you want to modify %d nodes", len(modifiedProfileMap))) + if !yes { + break + } + err = apiprofile.ProfileDelete(&wwapiv1.NodeDeleteParameter{NodeNames: nodeList, Force: true}) + if err != nil { + wwlog.Verbose("Problem deleting nodes before modification %s") + } + buffer, _ = yaml.Marshal(modifiedProfileMap) + err = apiprofile.ProfileAddFromYaml(&wwapiv1.NodeYaml{NodeConfMapYaml: string(buffer)}) + if err != nil { + wwlog.Error("Got following problem when writing back yaml: %s", err) + os.Exit(1) + } + break + } else { + yes := apiutil.ConfirmationPrompt(fmt.Sprintf("Got following error on parsing: %s, Retry", err)) + if !yes { + break + } + } + } else { + break + } + } + + return nil +} diff --git a/internal/app/wwctl/profile/edit/root.go b/internal/app/wwctl/profile/edit/root.go new file mode 100644 index 00000000..6a073532 --- /dev/null +++ b/internal/app/wwctl/profile/edit/root.go @@ -0,0 +1,38 @@ +package edit + +import ( + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/spf13/cobra" +) + +var ( + baseCmd = &cobra.Command{ + DisableFlagsInUseLine: true, + Use: "edit [OPTIONS] NODENAME", + Short: "Edit node(s) with editor", + Long: "This command opens an editor for the given profiles.", + RunE: CobraRunE, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + nodeDB, _ := node.New() + profiles, _ := nodeDB.FindAllProfiles() + var p_names []string + for _, profile := range profiles { + p_names = append(p_names, profile.Id.Get()) + } + return p_names, cobra.ShellCompDirectiveNoFileComp + }, + } + NoHeader bool +) + +func init() { + baseCmd.PersistentFlags().BoolVar(&NoHeader, "noheader", false, "Do not print header") +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/app/wwctl/profile/root.go b/internal/app/wwctl/profile/root.go index 2945d04e..4f7b1341 100644 --- a/internal/app/wwctl/profile/root.go +++ b/internal/app/wwctl/profile/root.go @@ -3,6 +3,7 @@ package profile import ( "github.com/hpcng/warewulf/internal/app/wwctl/profile/add" "github.com/hpcng/warewulf/internal/app/wwctl/profile/delete" + "github.com/hpcng/warewulf/internal/app/wwctl/profile/edit" "github.com/hpcng/warewulf/internal/app/wwctl/profile/list" "github.com/hpcng/warewulf/internal/app/wwctl/profile/set" "github.com/spf13/cobra" @@ -11,9 +12,10 @@ import ( var ( baseCmd = &cobra.Command{ DisableFlagsInUseLine: true, - Use: "profile COMMAND [OPTIONS]", - Short: "Node configuration profile management", - Long: "Management of node profile settings", + Use: "profile COMMAND [OPTIONS]", + Short: "Node configuration profile management", + Long: "Management of node profile settings", + Aliases: []string{"nodes"}, } ) @@ -22,6 +24,7 @@ func init() { baseCmd.AddCommand(set.GetCommand()) baseCmd.AddCommand(add.GetCommand()) baseCmd.AddCommand(delete.GetCommand()) + baseCmd.AddCommand(edit.GetCommand()) } // GetRootCommand returns the root cobra.Command for the application. diff --git a/internal/app/wwctl/ssh/main.go b/internal/app/wwctl/ssh/main.go index 06582d40..04a8bca8 100644 --- a/internal/app/wwctl/ssh/main.go +++ b/internal/app/wwctl/ssh/main.go @@ -38,23 +38,27 @@ func CobraRunE(cmd *cobra.Command, args []string) error { cmd.Usage() os.Exit(1) } - for _, node := range nodes { - - if _, ok := node.NetDevs["default"]; !ok { - fmt.Fprintf(os.Stderr, "%s: Default network device doesn't exist\n", node.Id.Get()) + var primaryNet string + for netName := range node.NetDevs { + if node.NetDevs[netName].Primary.GetB() { + primaryNet = netName + break + } + } + if primaryNet == "" { + wwlog.Error("%s: Primary network device doesn't exist\n", node.Id.Get()) continue } - - if node.NetDevs["default"].Ipaddr.Get() == "" { - fmt.Fprintf(os.Stderr, "%s: Default network IP address not configured\n", node.Id.Get()) + if node.NetDevs[primaryNet].Ipaddr.Get() == "" { + wwlog.Error("%s: Primary network IP address not configured\n", node.Id.Get()) continue } nodename := node.Id.Print() var command []string - command = append(command, node.NetDevs["default"].Ipaddr.Get()) + command = append(command, node.NetDevs[primaryNet].Ipaddr.Get()) command = append(command, args[1:]...) batchpool.Submit(func() { diff --git a/internal/pkg/api/container/container.go b/internal/pkg/api/container/container.go index 1a6fa467..d02cd950 100644 --- a/internal/pkg/api/container/container.go +++ b/internal/pkg/api/container/container.go @@ -294,11 +294,38 @@ func ContainerList() (containerInfo []*wwapiv1.ContainerInfo, err error) { wwlog.Debug("Finding kernel version for: %s", source) kernelVersion := container.KernelVersion(source) + creationTime, err := os.Stat(container.SourceDir(source)) + if err != nil { + wwlog.Error("%s\n", err) + } + modTime, err := os.Stat(container.ImageFile(source)) + if err != nil { + wwlog.Error("%s\n", err) + } + size, err := util.DirSize(container.SourceDir(source)) + if err != nil { + wwlog.Error("%s\n", err) + } + imgSize, err := os.Stat(container.ImageFile(source)) + if err != nil { + wwlog.Error("%s\n", err) + } + size += imgSize.Size() + imgSize, err = os.Stat(container.ImageFile(source) + ".gz") + if err != nil { + wwlog.Error("%s\n", err) + } + size += imgSize.Size() + containerInfo = append(containerInfo, &wwapiv1.ContainerInfo{ Name: source, NodeCount: uint32(nodemap[source]), KernelVersion: kernelVersion, + CreateDate: uint64(creationTime.ModTime().Unix()), + ModDate: uint64(modTime.ModTime().Unix()), + Size: uint64(size), }) + } return } diff --git a/internal/pkg/api/node/edit.go b/internal/pkg/api/node/edit.go new file mode 100644 index 00000000..6cfa8768 --- /dev/null +++ b/internal/pkg/api/node/edit.go @@ -0,0 +1,70 @@ +package apinode + +import ( + "os" + + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/pkg/errors" + "gopkg.in/yaml.v2" +) + +/* +Returns the nodes as a yaml string +*/ +func FindAllNodeConfs() *wwapiv1.NodeYaml { + nodeDB, err := node.New() + if err != nil { + wwlog.Error("Could not open nodeDB: %s\n", err) + os.Exit(1) + } + nodeMap := nodeDB.Nodes + // ignore err as nodeDB should always be correct + buffer, _ := yaml.Marshal(nodeMap) + retVal := wwapiv1.NodeYaml{ + NodeConfMapYaml: string(buffer), + } + return &retVal +} + +/* +Returns filtered list of nodes +*/ +func FilteredNodes(nodeList *wwapiv1.NodeList) *wwapiv1.NodeYaml { + nodeDB, err := node.New() + if err != nil { + wwlog.Error("Could not open nodeDB: %s\n", err) + os.Exit(1) + } + nodeMap := nodeDB.Nodes + nodeMap = node.FilterMapByName(nodeMap, nodeList.Output) + buffer, _ := yaml.Marshal(nodeMap) + retVal := wwapiv1.NodeYaml{ + NodeConfMapYaml: string(buffer), + } + return &retVal +} + +/* +Add nodes from yaml +*/ +func NodeAddFromYaml(nodeList *wwapiv1.NodeYaml) (err error) { + nodeDB, err := node.New() + if err != nil { + return errors.Wrap(err, "Could not open NodeDB: %s\n") + } + nodeMap := make(map[string]*node.NodeConf) + err = yaml.Unmarshal([]byte(nodeList.NodeConfMapYaml), nodeMap) + if err != nil { + return errors.Wrap(err, "Could not unmarshall Yaml: %s\n") + } + for nodeName, node := range nodeMap { + nodeDB.Nodes[nodeName] = node + } + err = nodeDB.Persist() + if err != nil { + return errors.Wrap(err, "failed to persist nodedb") + } + return nil +} diff --git a/internal/pkg/api/node/node.go b/internal/pkg/api/node/node.go index 1c025a80..47cc4c0b 100644 --- a/internal/pkg/api/node/node.go +++ b/internal/pkg/api/node/node.go @@ -102,7 +102,7 @@ func NodeDelete(ndp *wwapiv1.NodeDeleteParameter) (err error) { wwlog.Error("%s", err) } else { //count++ - fmt.Printf("Deleting node: %s\n", n.Id.Print()) + wwlog.Verbose("Deleting node: %s\n", n.Id.Print()) } } diff --git a/internal/pkg/api/profile/delete.go b/internal/pkg/api/profile/delete.go new file mode 100644 index 00000000..e3c13185 --- /dev/null +++ b/internal/pkg/api/profile/delete.go @@ -0,0 +1,94 @@ +package apiprofile + +import ( + "fmt" + "os" + + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/warewulfd" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" + "github.com/pkg/errors" +) + +// ProfileDelete adds profile deletion for management by Warewulf. +func ProfileDelete(ndp *wwapiv1.NodeDeleteParameter) (err error) { + + var profileList []node.NodeInfo + profileList, err = ProfileDeleteParameterCheck(ndp, false) + if err != nil { + return + } + + nodeDB, err := node.New() + if err != nil { + wwlog.Error("Failed to open node database: %s\n", err) + return + } + + for _, p := range profileList { + err := nodeDB.DelProfile(p.Id.Get()) + if err != nil { + wwlog.Error("%s\n", err) + } else { + //count++ + wwlog.Verbose("Deleting node: %s\n", p.Id.Print()) + } + } + + err = nodeDB.Persist() + if err != nil { + return errors.Wrap(err, "failed to persist nodedb") + } + + err = warewulfd.DaemonReload() + if err != nil { + return errors.Wrap(err, "failed to reload warewulf daemon") + } + return +} + +// ProfileDeleteParameterCheck does error checking on ProfileDeleteParameter. +// Output to the console if console is true. +// Returns the profiles to delete. +func ProfileDeleteParameterCheck(ndp *wwapiv1.NodeDeleteParameter, console bool) (profileList []node.NodeInfo, err error) { + + if ndp == nil { + err = fmt.Errorf("ProfileDeleteParameter is nil") + return + } + + nodeDB, err := node.New() + if err != nil { + wwlog.Error("Failed to open node database: %s\n", err) + return + } + + profiles, err := nodeDB.FindAllProfiles() + if err != nil { + wwlog.Error("Could not get node list: %s\n", err) + return + } + + node_args := hostlist.Expand(ndp.NodeNames) + + for _, r := range node_args { + var match bool + for _, p := range profiles { + if p.Id.Get() == r { + profileList = append(profileList, p) + match = true + } + } + + if !match { + fmt.Fprintf(os.Stderr, "ERROR: No match for node: %s\n", r) + } + } + + if len(profileList) == 0 { + fmt.Printf("No s found\n") + } + return +} diff --git a/internal/pkg/api/profile/edit.go b/internal/pkg/api/profile/edit.go new file mode 100644 index 00000000..5361f36c --- /dev/null +++ b/internal/pkg/api/profile/edit.go @@ -0,0 +1,70 @@ +package apiprofile + +import ( + "os" + + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/pkg/errors" + "gopkg.in/yaml.v2" +) + +/* +Returns the nodes as a yaml string +*/ +func FindAllProfileConfs() *wwapiv1.NodeYaml { + nodeDB, err := node.New() + if err != nil { + wwlog.Error("Could not open nodeDB: %s\n", err) + os.Exit(1) + } + profileMap := nodeDB.NodeProfiles + // ignore err as nodeDB should always be correct + buffer, _ := yaml.Marshal(profileMap) + retVal := wwapiv1.NodeYaml{ + NodeConfMapYaml: string(buffer), + } + return &retVal +} + +/* +Returns filtered list of nodes +*/ +func FilteredProfiles(profileList *wwapiv1.NodeList) *wwapiv1.NodeYaml { + nodeDB, err := node.New() + if err != nil { + wwlog.Error("Could not open nodeDB: %s\n", err) + os.Exit(1) + } + profileMap := nodeDB.NodeProfiles + profileMap = node.FilterMapByName(profileMap, profileList.Output) + buffer, _ := yaml.Marshal(profileMap) + retVal := wwapiv1.NodeYaml{ + NodeConfMapYaml: string(buffer), + } + return &retVal +} + +/* +Add profiles from yaml +*/ +func ProfileAddFromYaml(nodeList *wwapiv1.NodeYaml) (err error) { + nodeDB, err := node.New() + if err != nil { + return errors.Wrap(err, "Could not open NodeDB: %s\n") + } + profileMap := make(map[string]*node.NodeConf) + err = yaml.Unmarshal([]byte(nodeList.NodeConfMapYaml), profileMap) + if err != nil { + return errors.Wrap(err, "Could not unmarshall Yaml: %s\n") + } + for profileName, profile := range profileMap { + nodeDB.NodeProfiles[profileName] = profile + } + err = nodeDB.Persist() + if err != nil { + return errors.Wrap(err, "failed to persist nodedb") + } + return nil +} diff --git a/internal/pkg/api/routes/v1/routes.proto b/internal/pkg/api/routes/v1/routes.proto index eb2fcb44..bf350e09 100644 --- a/internal/pkg/api/routes/v1/routes.proto +++ b/internal/pkg/api/routes/v1/routes.proto @@ -43,6 +43,9 @@ message ContainerInfo { string name = 1; uint32 nodeCount = 2; string kernelVersion = 3; + uint64 createDate = 4; // creation date in unix time + uint64 modDate = 5; // date of last modification in unix time + uint64 size = 6; // size of chroot and images in bytes } // ContainerListResponse has all information that ContainerList provides. @@ -91,7 +94,6 @@ message NetDev { // NodeInfo contains details about a node managed by Warewulf/ message NodeInfo { map Fields = 1; - map NetDevs = 23; map Tags = 24; map Keys = 25; // TODO: We may not need this. Tags may be it. Ask Greg. @@ -126,6 +128,12 @@ message NodeAddParameter { repeated string nodeNames = 10; } +// NodeYaml is just the updated YAML config which will be added +// to nodes.conf (is resused for profile edit) +message NodeYaml { + string nodeConfMapYaml = 1; +} + // NodeDeleteParameter contains input for removing nodes from Warewulf // management. message NodeDeleteParameter { @@ -167,6 +175,11 @@ message VersionResponse { string warewulfVersion = 3; } +// Check if config is writeable +message CanWriteConfig { + bool canWriteConfig = 1; +} + // WWApi defines the wwapid service web interface. service WWApi { diff --git a/internal/pkg/api/routes/wwapiv1/routes.pb.go b/internal/pkg/api/routes/wwapiv1/routes.pb.go index 5d38b99e..fa6e9550 100644 --- a/internal/pkg/api/routes/wwapiv1/routes.pb.go +++ b/internal/pkg/api/routes/wwapiv1/routes.pb.go @@ -307,6 +307,9 @@ type ContainerInfo struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` NodeCount uint32 `protobuf:"varint,2,opt,name=nodeCount,proto3" json:"nodeCount,omitempty"` KernelVersion string `protobuf:"bytes,3,opt,name=kernelVersion,proto3" json:"kernelVersion,omitempty"` + CreateDate uint64 `protobuf:"varint,4,opt,name=createDate,proto3" json:"createDate,omitempty"` // creation date in unix time + ModDate uint64 `protobuf:"varint,5,opt,name=modDate,proto3" json:"modDate,omitempty"` // date of last modification in unix time + Size uint64 `protobuf:"varint,6,opt,name=size,proto3" json:"size,omitempty"` // size of chroot and images in bytes } func (x *ContainerInfo) Reset() { @@ -362,6 +365,27 @@ func (x *ContainerInfo) GetKernelVersion() string { return "" } +func (x *ContainerInfo) GetCreateDate() uint64 { + if x != nil { + return x.CreateDate + } + return 0 +} + +func (x *ContainerInfo) GetModDate() uint64 { + if x != nil { + return x.ModDate + } + return 0 +} + +func (x *ContainerInfo) GetSize() uint64 { + if x != nil { + return x.Size + } + return 0 +} + // ContainerListResponse has all information that ContainerList provides. type ContainerListResponse struct { state protoimpl.MessageState @@ -1026,6 +1050,55 @@ func (x *NodeAddParameter) GetNodeNames() []string { return nil } +// NodeYaml is just the updated YAML config which will be added +// to nodes.conf (is resused for profile edit) +type NodeYaml struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeConfMapYaml string `protobuf:"bytes,1,opt,name=nodeConfMapYaml,proto3" json:"nodeConfMapYaml,omitempty"` +} + +func (x *NodeYaml) Reset() { + *x = NodeYaml{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NodeYaml) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NodeYaml) ProtoMessage() {} + +func (x *NodeYaml) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NodeYaml.ProtoReflect.Descriptor instead. +func (*NodeYaml) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{16} +} + +func (x *NodeYaml) GetNodeConfMapYaml() string { + if x != nil { + return x.NodeConfMapYaml + } + return "" +} + // NodeDeleteParameter contains input for removing nodes from Warewulf // management. type NodeDeleteParameter struct { @@ -1040,7 +1113,7 @@ type NodeDeleteParameter struct { func (x *NodeDeleteParameter) Reset() { *x = NodeDeleteParameter{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[16] + mi := &file_routes_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1053,7 +1126,7 @@ func (x *NodeDeleteParameter) String() string { func (*NodeDeleteParameter) ProtoMessage() {} func (x *NodeDeleteParameter) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[16] + mi := &file_routes_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1066,7 +1139,7 @@ func (x *NodeDeleteParameter) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeDeleteParameter.ProtoReflect.Descriptor instead. func (*NodeDeleteParameter) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{16} + return file_routes_proto_rawDescGZIP(), []int{17} } func (x *NodeDeleteParameter) GetForce() bool { @@ -1101,7 +1174,7 @@ type NodeSetParameter struct { func (x *NodeSetParameter) Reset() { *x = NodeSetParameter{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[17] + mi := &file_routes_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1114,7 +1187,7 @@ func (x *NodeSetParameter) String() string { func (*NodeSetParameter) ProtoMessage() {} func (x *NodeSetParameter) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[17] + mi := &file_routes_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1127,7 +1200,7 @@ func (x *NodeSetParameter) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeSetParameter.ProtoReflect.Descriptor instead. func (*NodeSetParameter) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{17} + return file_routes_proto_rawDescGZIP(), []int{18} } func (x *NodeSetParameter) GetNodeConfYaml() string { @@ -1188,7 +1261,7 @@ type NodeStatus struct { func (x *NodeStatus) Reset() { *x = NodeStatus{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[18] + mi := &file_routes_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1201,7 +1274,7 @@ func (x *NodeStatus) String() string { func (*NodeStatus) ProtoMessage() {} func (x *NodeStatus) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[18] + mi := &file_routes_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1214,7 +1287,7 @@ func (x *NodeStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeStatus.ProtoReflect.Descriptor instead. func (*NodeStatus) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{18} + return file_routes_proto_rawDescGZIP(), []int{19} } func (x *NodeStatus) GetNodeName() string { @@ -1264,7 +1337,7 @@ type NodeStatusResponse struct { func (x *NodeStatusResponse) Reset() { *x = NodeStatusResponse{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[19] + mi := &file_routes_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1277,7 +1350,7 @@ func (x *NodeStatusResponse) String() string { func (*NodeStatusResponse) ProtoMessage() {} func (x *NodeStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[19] + mi := &file_routes_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1290,7 +1363,7 @@ func (x *NodeStatusResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use NodeStatusResponse.ProtoReflect.Descriptor instead. func (*NodeStatusResponse) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{19} + return file_routes_proto_rawDescGZIP(), []int{20} } func (x *NodeStatusResponse) GetNodeStatus() []*NodeStatus { @@ -1314,7 +1387,7 @@ type VersionResponse struct { func (x *VersionResponse) Reset() { *x = VersionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_routes_proto_msgTypes[20] + mi := &file_routes_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1327,7 +1400,7 @@ func (x *VersionResponse) String() string { func (*VersionResponse) ProtoMessage() {} func (x *VersionResponse) ProtoReflect() protoreflect.Message { - mi := &file_routes_proto_msgTypes[20] + mi := &file_routes_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1340,7 +1413,7 @@ func (x *VersionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VersionResponse.ProtoReflect.Descriptor instead. func (*VersionResponse) Descriptor() ([]byte, []int) { - return file_routes_proto_rawDescGZIP(), []int{20} + return file_routes_proto_rawDescGZIP(), []int{21} } func (x *VersionResponse) GetApiPrefix() string { @@ -1364,6 +1437,54 @@ func (x *VersionResponse) GetWarewulfVersion() string { return "" } +// Check if config is writeable +type CanWriteConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CanWriteConfig bool `protobuf:"varint,1,opt,name=canWriteConfig,proto3" json:"canWriteConfig,omitempty"` +} + +func (x *CanWriteConfig) Reset() { + *x = CanWriteConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_routes_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CanWriteConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CanWriteConfig) ProtoMessage() {} + +func (x *CanWriteConfig) ProtoReflect() protoreflect.Message { + mi := &file_routes_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CanWriteConfig.ProtoReflect.Descriptor instead. +func (*CanWriteConfig) Descriptor() ([]byte, []int) { + return file_routes_proto_rawDescGZIP(), []int{22} +} + +func (x *CanWriteConfig) GetCanWriteConfig() bool { + if x != nil { + return x.CanWriteConfig + } + return false +} + var File_routes_proto protoreflect.FileDescriptor var file_routes_proto_rawDesc = []byte{ @@ -1397,222 +1518,234 @@ var file_routes_proto_rawDesc = []byte{ 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x79, 0x6e, 0x63, 0x55, 0x73, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x79, 0x6e, 0x63, 0x55, 0x73, 0x65, 0x72, - 0x22, 0x67, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6b, 0x65, 0x72, 0x6e, - 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x50, 0x0a, 0x15, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x3e, 0x0a, 0x16, 0x43, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x7f, 0x0a, 0x15, 0x43, - 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x52, 0x6f, 0x6f, 0x74, - 0x66, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x52, 0x6f, 0x6f, 0x74, 0x66, 0x73, - 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x4b, - 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x42, 0x0a, 0x1a, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x79, 0x6e, 0x63, 0x55, 0x73, 0x65, - 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, - 0x22, 0x29, 0x0a, 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1c, 0x0a, - 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x09, 0x4e, - 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x22, 0x88, 0x02, 0x0a, - 0x06, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x12, 0x31, 0x0a, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x54, 0x61, - 0x67, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x54, 0x61, 0x67, 0x73, 0x1a, 0x4d, 0x0a, 0x0a, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4c, 0x0a, 0x09, 0x54, 0x61, 0x67, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9b, 0x04, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x36, 0x0a, 0x06, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, - 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x07, - 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, - 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, - 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x73, 0x12, 0x30, 0x0a, 0x04, 0x54, 0x61, 0x67, 0x73, 0x18, - 0x18, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x04, 0x54, 0x61, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x04, 0x4b, 0x65, 0x79, - 0x73, 0x18, 0x19, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4b, 0x65, 0x79, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x4b, 0x65, 0x79, 0x73, 0x1a, 0x4e, 0x0a, 0x0b, 0x46, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4c, 0x0a, 0x0c, 0x4e, - 0x65, 0x74, 0x44, 0x65, 0x76, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x26, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x77, - 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4c, 0x0a, 0x09, 0x54, 0x61, 0x67, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4c, 0x0a, 0x09, 0x4b, 0x65, 0x79, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3c, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x6e, 0x6f, 0x64, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x6e, 0x6f, - 0x64, 0x65, 0x73, 0x22, 0x99, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4c, - 0x69, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1e, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, - 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x40, 0x0a, - 0x08, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x69, 0x6d, - 0x70, 0x6c, 0x65, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x70, 0x6d, 0x69, 0x10, 0x01, 0x12, - 0x0b, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, - 0x4c, 0x6f, 0x6e, 0x67, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x6c, 0x6c, 0x10, 0x04, 0x22, - 0x22, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x4f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x22, 0x54, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, - 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, - 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x6e, - 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, - 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x13, 0x4e, 0x6f, 0x64, - 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x22, 0xb5, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, + 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6b, 0x65, 0x72, + 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, + 0x64, 0x44, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6d, 0x6f, 0x64, + 0x44, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x50, 0x0a, 0x15, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x37, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x22, 0x3e, 0x0a, 0x16, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x7f, 0x0a, 0x15, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x52, 0x6f, 0x6f, 0x74, 0x66, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x52, 0x6f, 0x6f, 0x74, 0x66, 0x73, 0x12, + 0x14, 0x0a, 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, + 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x4b, 0x65, + 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x42, 0x0a, 0x1a, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x79, 0x6e, 0x63, 0x55, 0x73, 0x65, 0x72, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, + 0x29, 0x0a, 0x09, 0x4e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, + 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x4f, 0x0a, 0x09, 0x4e, 0x6f, + 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x22, 0x88, 0x02, 0x0a, 0x06, + 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x12, 0x31, 0x0a, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x54, 0x61, 0x67, + 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x04, 0x54, 0x61, 0x67, 0x73, 0x1a, 0x4d, 0x0a, 0x0a, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4c, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9b, 0x04, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x36, 0x0a, 0x06, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, + 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x06, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x07, 0x4e, + 0x65, 0x74, 0x44, 0x65, 0x76, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x77, + 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, + 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x4e, + 0x65, 0x74, 0x44, 0x65, 0x76, 0x73, 0x12, 0x30, 0x0a, 0x04, 0x54, 0x61, 0x67, 0x73, 0x18, 0x18, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x04, 0x54, 0x61, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x04, 0x4b, 0x65, 0x79, 0x73, + 0x18, 0x19, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4b, 0x65, 0x79, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x4b, 0x65, 0x79, 0x73, 0x1a, 0x4e, 0x0a, 0x0b, 0x46, 0x69, + 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4c, 0x0a, 0x0c, 0x4e, 0x65, + 0x74, 0x44, 0x65, 0x76, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x77, 0x77, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x65, 0x74, 0x44, 0x65, 0x76, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4c, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4c, 0x0a, 0x09, 0x4b, 0x65, 0x79, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, + 0x4e, 0x6f, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3c, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x6e, 0x6f, 0x64, + 0x65, 0x73, 0x22, 0x99, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x32, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1e, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4e, + 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x18, + 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x40, 0x0a, 0x08, + 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x69, 0x6d, 0x70, + 0x6c, 0x65, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x70, 0x6d, 0x69, 0x10, 0x01, 0x12, 0x0b, + 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4c, + 0x6f, 0x6e, 0x67, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x6c, 0x6c, 0x10, 0x04, 0x22, 0x22, + 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x4f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x22, 0x54, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, 0x50, 0x61, 0x72, + 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, + 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, + 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, + 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x34, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, + 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x28, 0x0a, 0x0f, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x4d, 0x61, 0x70, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6e, + 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x4d, 0x61, 0x70, 0x59, 0x61, 0x6d, 0x6c, 0x22, 0x49, + 0x0a, 0x13, 0x4e, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, + 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, + 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xc8, 0x01, 0x0a, 0x10, 0x4e, 0x6f, + 0x64, 0x65, 0x53, 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, + 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, + 0x6d, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, + 0x18, 0x1b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, + 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, - 0x61, 0x6d, 0x65, 0x73, 0x22, 0xc8, 0x01, 0x0a, 0x10, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x74, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x59, 0x61, 0x6d, 0x6c, 0x12, 0x1c, 0x0a, - 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x6e, - 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x6e, 0x65, 0x74, 0x64, 0x65, 0x76, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, - 0x1a, 0x0a, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x1b, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x08, 0x61, 0x6c, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x66, - 0x6f, 0x72, 0x63, 0x65, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, - 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x27, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, - 0x86, 0x01, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, - 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, - 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x73, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x70, 0x61, 0x64, 0x64, 0x72, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x61, 0x64, 0x64, 0x72, 0x12, 0x1a, 0x0a, 0x08, - 0x6c, 0x61, 0x73, 0x74, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, - 0x6c, 0x61, 0x73, 0x74, 0x73, 0x65, 0x65, 0x6e, 0x22, 0x4a, 0x0a, 0x12, 0x4e, 0x6f, 0x64, 0x65, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, - 0x0a, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, - 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x22, 0x79, 0x0a, 0x0f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x50, 0x72, - 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x70, 0x69, 0x50, - 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x77, 0x61, 0x72, 0x65, 0x77, 0x75, 0x6c, - 0x66, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x77, 0x61, 0x72, 0x65, 0x77, 0x75, 0x6c, 0x66, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x32, - 0xa6, 0x08, 0x0a, 0x05, 0x57, 0x57, 0x41, 0x70, 0x69, 0x12, 0x73, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x21, 0x2e, 0x77, 0x77, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x1f, + 0x6d, 0x65, 0x73, 0x18, 0x27, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x22, 0x86, 0x01, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x65, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x70, 0x61, + 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x70, 0x61, 0x64, 0x64, + 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x73, 0x65, 0x65, 0x6e, 0x22, 0x4a, 0x0a, + 0x12, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x6e, + 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x79, 0x0a, 0x0f, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x61, 0x70, 0x69, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x61, 0x70, 0x69, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x70, + 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x77, 0x61, + 0x72, 0x65, 0x77, 0x75, 0x6c, 0x66, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x61, 0x72, 0x65, 0x77, 0x75, 0x6c, 0x66, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x38, 0x0a, 0x0e, 0x43, 0x61, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x61, 0x6e, 0x57, 0x72, 0x69, + 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, + 0x63, 0x61, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xa6, + 0x08, 0x0a, 0x05, 0x57, 0x57, 0x41, 0x70, 0x69, 0x12, 0x73, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x21, 0x2e, 0x77, 0x77, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x1f, 0x2e, + 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x64, 0x0a, + 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x22, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x74, 0x65, 0x72, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x15, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x0f, 0x2a, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x12, 0x70, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x22, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6d, 0x70, 0x6f, 0x72, + 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x1f, 0x2e, 0x77, 0x77, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1f, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x3a, 0x01, 0x2a, 0x12, 0x64, - 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x12, 0x22, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x15, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x2a, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x12, 0x70, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x22, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x6d, 0x70, 0x6f, - 0x72, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x1f, 0x2e, 0x77, 0x77, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x18, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x12, 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x3a, 0x01, 0x2a, 0x12, 0x5f, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, - 0x1f, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x6d, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, 0x12, 0x20, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, - 0x77, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x1f, 0x2e, 0x77, 0x77, 0x61, - 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, - 0x68, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, - 0x65, 0x72, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x56, 0x0a, 0x07, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, - 0x64, 0x12, 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x41, 0x64, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x1a, 0x2e, - 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x0d, 0x22, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x55, - 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x77, - 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x2a, 0x08, 0x2f, 0x76, 0x31, - 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x4d, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, - 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x59, 0x0a, 0x07, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x74, 0x12, - 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, - 0x65, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x1a, 0x2e, 0x77, 0x77, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x22, - 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x65, 0x74, 0x3a, 0x01, 0x2a, 0x12, - 0x57, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x13, 0x2e, - 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x73, 0x1a, 0x1c, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, - 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, - 0x64, 0x65, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x4e, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x77, 0x77, - 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, - 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x29, 0x5a, 0x27, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x6f, 0x75, - 0x74, 0x65, 0x73, 0x2f, 0x77, 0x77, 0x61, 0x70, 0x69, 0x76, 0x31, 0x3b, 0x77, 0x77, 0x61, 0x70, - 0x69, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x6d, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, + 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, 0x12, 0x20, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, 0x6f, 0x77, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x1f, 0x2e, 0x77, 0x77, 0x61, 0x70, + 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x53, 0x68, + 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x13, 0x12, 0x11, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x56, 0x0a, 0x07, 0x4e, 0x6f, 0x64, 0x65, 0x41, 0x64, 0x64, + 0x12, 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x41, 0x64, 0x64, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x1a, 0x2e, 0x77, + 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, + 0x22, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x55, 0x0a, + 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x77, 0x77, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x2a, 0x08, 0x2f, 0x76, 0x31, 0x2f, + 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x4d, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, + 0x12, 0x13, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x1a, 0x1a, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, 0x76, 0x31, 0x2f, 0x6e, + 0x6f, 0x64, 0x65, 0x12, 0x59, 0x0a, 0x07, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x74, 0x12, 0x1a, + 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x65, + 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x1a, 0x1a, 0x2e, 0x77, 0x77, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x22, 0x0b, + 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x65, 0x74, 0x3a, 0x01, 0x2a, 0x12, 0x57, + 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x13, 0x2e, 0x77, + 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x1a, 0x1c, 0x2e, 0x77, 0x77, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4e, 0x6f, 0x64, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x6f, 0x64, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x4e, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x77, 0x77, 0x61, + 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x10, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0a, 0x12, 0x08, 0x2f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x29, 0x5a, 0x27, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x2f, 0x77, 0x77, 0x61, 0x70, 0x69, 0x76, 0x31, 0x3b, 0x77, 0x77, 0x61, 0x70, 0x69, + 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1628,7 +1761,7 @@ func file_routes_proto_rawDescGZIP() []byte { } var file_routes_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_routes_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_routes_proto_msgTypes = make([]protoimpl.MessageInfo, 29) var file_routes_proto_goTypes = []interface{}{ (GetNodeList_ListType)(0), // 0: wwapi.v1.GetNodeList.ListType (*ContainerBuildParameter)(nil), // 1: wwapi.v1.ContainerBuildParameter @@ -1647,30 +1780,32 @@ var file_routes_proto_goTypes = []interface{}{ (*GetNodeList)(nil), // 14: wwapi.v1.GetNodeList (*NodeList)(nil), // 15: wwapi.v1.NodeList (*NodeAddParameter)(nil), // 16: wwapi.v1.NodeAddParameter - (*NodeDeleteParameter)(nil), // 17: wwapi.v1.NodeDeleteParameter - (*NodeSetParameter)(nil), // 18: wwapi.v1.NodeSetParameter - (*NodeStatus)(nil), // 19: wwapi.v1.NodeStatus - (*NodeStatusResponse)(nil), // 20: wwapi.v1.NodeStatusResponse - (*VersionResponse)(nil), // 21: wwapi.v1.VersionResponse - nil, // 22: wwapi.v1.NetDev.FieldEntry - nil, // 23: wwapi.v1.NetDev.TagsEntry - nil, // 24: wwapi.v1.NodeInfo.FieldsEntry - nil, // 25: wwapi.v1.NodeInfo.NetDevsEntry - nil, // 26: wwapi.v1.NodeInfo.TagsEntry - nil, // 27: wwapi.v1.NodeInfo.KeysEntry - (*empty.Empty)(nil), // 28: google.protobuf.Empty + (*NodeYaml)(nil), // 17: wwapi.v1.NodeYaml + (*NodeDeleteParameter)(nil), // 18: wwapi.v1.NodeDeleteParameter + (*NodeSetParameter)(nil), // 19: wwapi.v1.NodeSetParameter + (*NodeStatus)(nil), // 20: wwapi.v1.NodeStatus + (*NodeStatusResponse)(nil), // 21: wwapi.v1.NodeStatusResponse + (*VersionResponse)(nil), // 22: wwapi.v1.VersionResponse + (*CanWriteConfig)(nil), // 23: wwapi.v1.CanWriteConfig + nil, // 24: wwapi.v1.NetDev.FieldEntry + nil, // 25: wwapi.v1.NetDev.TagsEntry + nil, // 26: wwapi.v1.NodeInfo.FieldsEntry + nil, // 27: wwapi.v1.NodeInfo.NetDevsEntry + nil, // 28: wwapi.v1.NodeInfo.TagsEntry + nil, // 29: wwapi.v1.NodeInfo.KeysEntry + (*empty.Empty)(nil), // 30: google.protobuf.Empty } var file_routes_proto_depIdxs = []int32{ 4, // 0: wwapi.v1.ContainerListResponse.containers:type_name -> wwapi.v1.ContainerInfo - 22, // 1: wwapi.v1.NetDev.Field:type_name -> wwapi.v1.NetDev.FieldEntry - 23, // 2: wwapi.v1.NetDev.Tags:type_name -> wwapi.v1.NetDev.TagsEntry - 24, // 3: wwapi.v1.NodeInfo.Fields:type_name -> wwapi.v1.NodeInfo.FieldsEntry - 25, // 4: wwapi.v1.NodeInfo.NetDevs:type_name -> wwapi.v1.NodeInfo.NetDevsEntry - 26, // 5: wwapi.v1.NodeInfo.Tags:type_name -> wwapi.v1.NodeInfo.TagsEntry - 27, // 6: wwapi.v1.NodeInfo.Keys:type_name -> wwapi.v1.NodeInfo.KeysEntry + 24, // 1: wwapi.v1.NetDev.Field:type_name -> wwapi.v1.NetDev.FieldEntry + 25, // 2: wwapi.v1.NetDev.Tags:type_name -> wwapi.v1.NetDev.TagsEntry + 26, // 3: wwapi.v1.NodeInfo.Fields:type_name -> wwapi.v1.NodeInfo.FieldsEntry + 27, // 4: wwapi.v1.NodeInfo.NetDevs:type_name -> wwapi.v1.NodeInfo.NetDevsEntry + 28, // 5: wwapi.v1.NodeInfo.Tags:type_name -> wwapi.v1.NodeInfo.TagsEntry + 29, // 6: wwapi.v1.NodeInfo.Keys:type_name -> wwapi.v1.NodeInfo.KeysEntry 12, // 7: wwapi.v1.NodeListResponse.nodes:type_name -> wwapi.v1.NodeInfo 0, // 8: wwapi.v1.GetNodeList.type:type_name -> wwapi.v1.GetNodeList.ListType - 19, // 9: wwapi.v1.NodeStatusResponse.nodeStatus:type_name -> wwapi.v1.NodeStatus + 20, // 9: wwapi.v1.NodeStatusResponse.nodeStatus:type_name -> wwapi.v1.NodeStatus 10, // 10: wwapi.v1.NetDev.FieldEntry.value:type_name -> wwapi.v1.NodeField 10, // 11: wwapi.v1.NetDev.TagsEntry.value:type_name -> wwapi.v1.NodeField 10, // 12: wwapi.v1.NodeInfo.FieldsEntry.value:type_name -> wwapi.v1.NodeField @@ -1680,25 +1815,25 @@ var file_routes_proto_depIdxs = []int32{ 1, // 16: wwapi.v1.WWApi.ContainerBuild:input_type -> wwapi.v1.ContainerBuildParameter 2, // 17: wwapi.v1.WWApi.ContainerDelete:input_type -> wwapi.v1.ContainerDeleteParameter 3, // 18: wwapi.v1.WWApi.ContainerImport:input_type -> wwapi.v1.ContainerImportParameter - 28, // 19: wwapi.v1.WWApi.ContainerList:input_type -> google.protobuf.Empty + 30, // 19: wwapi.v1.WWApi.ContainerList:input_type -> google.protobuf.Empty 6, // 20: wwapi.v1.WWApi.ContainerShow:input_type -> wwapi.v1.ContainerShowParameter 16, // 21: wwapi.v1.WWApi.NodeAdd:input_type -> wwapi.v1.NodeAddParameter - 17, // 22: wwapi.v1.WWApi.NodeDelete:input_type -> wwapi.v1.NodeDeleteParameter + 18, // 22: wwapi.v1.WWApi.NodeDelete:input_type -> wwapi.v1.NodeDeleteParameter 9, // 23: wwapi.v1.WWApi.NodeList:input_type -> wwapi.v1.NodeNames - 18, // 24: wwapi.v1.WWApi.NodeSet:input_type -> wwapi.v1.NodeSetParameter + 19, // 24: wwapi.v1.WWApi.NodeSet:input_type -> wwapi.v1.NodeSetParameter 9, // 25: wwapi.v1.WWApi.NodeStatus:input_type -> wwapi.v1.NodeNames - 28, // 26: wwapi.v1.WWApi.Version:input_type -> google.protobuf.Empty + 30, // 26: wwapi.v1.WWApi.Version:input_type -> google.protobuf.Empty 5, // 27: wwapi.v1.WWApi.ContainerBuild:output_type -> wwapi.v1.ContainerListResponse - 28, // 28: wwapi.v1.WWApi.ContainerDelete:output_type -> google.protobuf.Empty + 30, // 28: wwapi.v1.WWApi.ContainerDelete:output_type -> google.protobuf.Empty 5, // 29: wwapi.v1.WWApi.ContainerImport:output_type -> wwapi.v1.ContainerListResponse 5, // 30: wwapi.v1.WWApi.ContainerList:output_type -> wwapi.v1.ContainerListResponse 7, // 31: wwapi.v1.WWApi.ContainerShow:output_type -> wwapi.v1.ContainerShowResponse 13, // 32: wwapi.v1.WWApi.NodeAdd:output_type -> wwapi.v1.NodeListResponse - 28, // 33: wwapi.v1.WWApi.NodeDelete:output_type -> google.protobuf.Empty + 30, // 33: wwapi.v1.WWApi.NodeDelete:output_type -> google.protobuf.Empty 13, // 34: wwapi.v1.WWApi.NodeList:output_type -> wwapi.v1.NodeListResponse 13, // 35: wwapi.v1.WWApi.NodeSet:output_type -> wwapi.v1.NodeListResponse - 20, // 36: wwapi.v1.WWApi.NodeStatus:output_type -> wwapi.v1.NodeStatusResponse - 21, // 37: wwapi.v1.WWApi.Version:output_type -> wwapi.v1.VersionResponse + 21, // 36: wwapi.v1.WWApi.NodeStatus:output_type -> wwapi.v1.NodeStatusResponse + 22, // 37: wwapi.v1.WWApi.Version:output_type -> wwapi.v1.VersionResponse 27, // [27:38] is the sub-list for method output_type 16, // [16:27] is the sub-list for method input_type 16, // [16:16] is the sub-list for extension type_name @@ -1905,7 +2040,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeDeleteParameter); i { + switch v := v.(*NodeYaml); i { case 0: return &v.state case 1: @@ -1917,7 +2052,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeSetParameter); i { + switch v := v.(*NodeDeleteParameter); i { case 0: return &v.state case 1: @@ -1929,7 +2064,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeStatus); i { + switch v := v.(*NodeSetParameter); i { case 0: return &v.state case 1: @@ -1941,7 +2076,7 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NodeStatusResponse); i { + switch v := v.(*NodeStatus); i { case 0: return &v.state case 1: @@ -1953,6 +2088,18 @@ func file_routes_proto_init() { } } file_routes_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NodeStatusResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_routes_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*VersionResponse); i { case 0: return &v.state @@ -1964,6 +2111,18 @@ func file_routes_proto_init() { return nil } } + file_routes_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CanWriteConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1971,7 +2130,7 @@ func file_routes_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_routes_proto_rawDesc, NumEnums: 1, - NumMessages: 27, + NumMessages: 29, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/pkg/api/util/util.go b/internal/pkg/api/util/util.go index 273b5594..093183ed 100644 --- a/internal/pkg/api/util/util.go +++ b/internal/pkg/api/util/util.go @@ -1,6 +1,11 @@ package util import ( + "syscall" + + "github.com/hpcng/warewulf/internal/pkg/api/routes/wwapiv1" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/manifoldco/promptui" ) @@ -18,4 +23,19 @@ func ConfirmationPrompt(label string) (yes bool) { yes = true } return -} \ No newline at end of file +} + +/* +Simple check if the config can be written in case wwctl isn't run as root +*/ +func CanWriteConfig() (canwrite *wwapiv1.CanWriteConfig) { + canwrite = new(wwapiv1.CanWriteConfig) + err := syscall.Access(node.ConfigFile, syscall.O_RDWR) + if err != nil { + wwlog.Warn("Couldn't open %s:%s", node.ConfigFile, err) + canwrite.CanWriteConfig = false + } else { + canwrite.CanWriteConfig = true + } + return canwrite +} diff --git a/internal/pkg/container/build.go b/internal/pkg/container/build.go index 147334ce..cd3e45d9 100644 --- a/internal/pkg/container/build.go +++ b/internal/pkg/container/build.go @@ -2,7 +2,6 @@ package container import ( "path" - "strings" "github.com/pkg/errors" @@ -27,20 +26,14 @@ func Build(name string, buildForce bool) error { } } - excludes_file := path.Join(rootfsPath, "./etc/warewulf/excludes") ignore := []string{} - + excludes_file := path.Join(rootfsPath, "./etc/warewulf/excludes") if util.IsFile(excludes_file) { - ignore, err := util.ReadFile(excludes_file) + var err error + ignore, err = util.ReadFile(excludes_file) if err != nil { return errors.Wrapf(err, "Failed creating directory: %s", imagePath) } - - for i, pattern := range ignore { - if ( strings.HasPrefix(pattern, "/") ) { - ignore[i] = pattern[1:] - } - } } err := util.BuildFsImage( diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 02a0ec29..cb3ab52d 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -14,11 +14,34 @@ import ( ) var ConfigFile string +var DefaultConfig string + +// used as fallback if DefaultConfig can't be read +var FallBackConf = ` +defaultnode: + runtime overlay: + - generic + system overlay: + - wwinit + kernel: + args: quiet crashkernel=no vga=791 net.naming-scheme=v238 + init: /sbin/init + root: initramfs + profiles: + - default + network devices: + dummy: + device: eth0 + type: ethernet + netmask: 255.255.255.0` func init() { if ConfigFile == "" { ConfigFile = path.Join(buildconfig.SYSCONFDIR(), "warewulf/nodes.conf") } + if DefaultConfig == "" { + DefaultConfig = path.Join(buildconfig.SYSCONFDIR(), "warewulf/defaults.conf") + } } func New() (NodeYaml, error) { @@ -54,6 +77,30 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) { return ret, err } */ + var defConf map[string]*NodeConf + wwlog.Verbose("Opening defaults failed %s\n", DefaultConfig) + defData, err := ioutil.ReadFile(DefaultConfig) + if err != nil { + wwlog.Verbose("Couldn't read DefaultConfig :%s\n", err) + } + wwlog.Debug("Unmarshalling default config\n") + err = yaml.Unmarshal(defData, &defConf) + if err != nil { + wwlog.Verbose("Couldn't unmarshall defaults from file :%s\n", err) + wwlog.Verbose("Using building defaults") + err = yaml.Unmarshal([]byte(FallBackConf), &defConf) + if err != nil { + wwlog.Warn("Could not get any defaults") + } + } + var defConfNet *NetDevs + if _, ok := defConf["defaultnode"]; ok { + if _, ok := defConf["defaultnode"].NetDevs["dummy"]; ok { + defConfNet = defConf["defaultnode"].NetDevs["dummy"] + } + defConf["defaultnode"].NetDevs = nil + } + wwlog.Debug("Finding all nodes...") for nodename, node := range config.Nodes { var n NodeInfo @@ -63,13 +110,7 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) { n.Tags = make(map[string]*Entry) n.Kernel = new(KernelEntry) n.Ipmi = new(IpmiEntry) - n.SystemOverlay.SetDefault("wwinit") - n.RuntimeOverlay.SetDefault("generic") - n.Ipxe.SetDefault("default") - n.Init.SetDefault("/sbin/init") - n.Root.SetDefault("initramfs") - n.Kernel.Args.SetDefault("quiet crashkernel=no vga=791") - + n.SetDefFrom(defConf["defaultnode"]) fullname := strings.SplitN(nodename, ".", 2) if len(fullname) > 1 { n.ClusterName.SetDefault(fullname[1]) @@ -80,14 +121,18 @@ func (config *NodeYaml) FindAllNodes() ([]NodeInfo, error) { } else { n.Profiles.SetSlice(node.Profiles) } - // node explciti nodename field in NodeConf + // node explicitly nodename field in NodeConf n.Id.Set(nodename) - // backward compatibilty + // backward compatibility for keyname, key := range node.Keys { node.Tags[keyname] = key delete(node.Keys, keyname) } n.SetFrom(node) + // only now the netdevs start to exist so that default values can be set + for _, netdev := range n.NetDevs { + netdev.SetDefFrom(defConfNet) + } // set default/primary network is just one network exist if len(n.NetDevs) == 1 { // only way to get the key diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index 2087299f..cf2fa48a 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -1,14 +1,11 @@ package node -import ( - "github.com/hpcng/warewulf/internal/pkg/util" - "github.com/hpcng/warewulf/internal/pkg/wwlog" -) - /****** * YAML data representations ******/ - +/* +Structure of which goes to disk +*/ type NodeYaml struct { WWInternal int `yaml:"WW_INTERNAL"` NodeProfiles map[string]*NodeConf @@ -16,7 +13,7 @@ type NodeYaml struct { } /* -NodeConf is the datastructure which is stored on disk. +NodeConf is the datastructure describing a node and a profile which in disk format. */ type NodeConf struct { Comment string `yaml:"comment,omitempty" lopt:"comment" comment:"Set arbitrary string comment"` @@ -24,11 +21,11 @@ type NodeConf struct { ContainerName string `yaml:"container name,omitempty" lopt:"container" sopt:"C" comment:"Set container name"` Ipxe string `yaml:"ipxe template,omitempty" lopt:"ipxe" comment:"Set the iPXE template name"` // Deprecated start - // Kernel settings here are deprecated and here for backward comptability + // Kernel settings here are deprecated and here for backward compatibility KernelVersion string `yaml:"kernel version,omitempty"` KernelOverride string `yaml:"kernel override,omitempty"` KernelArgs string `yaml:"kernel args,omitempty"` - // Ipmi settings herer are deprecated and here for backward comptability + // Ipmi settings herer are deprecated and here for backward compatibility IpmiUserName string `yaml:"ipmi username,omitempty"` IpmiPassword string `yaml:"ipmi password,omitempty"` IpmiIpaddr string `yaml:"ipmi ipaddr,omitempty"` @@ -82,6 +79,7 @@ type NetDevs struct { Prefix string `yaml:"prefix,omitempty"` Netmask string `yaml:"netmask,omitempty" lopt:"netmask" sopt:"M" comment:"Set the networks netmask"` Gateway string `yaml:"gateway,omitempty" lopt:"gateway" sopt:"G" comment:"Set the node's network device gateway"` + MTU string `yaml:"mtu,omitempty" lopt:"mtu" comment:"Set the mtu"` Primary string `yaml:"primary,omitempty" lopt:"primary" comment:"Enable/disable network device as primary (yes/no)"` Default string `yaml:"default,omitempty"` /* backward compatibility */ Tags map[string]string `yaml:"tags,omitempty" lopt:"nettagadd" comment:"network tags"` @@ -156,6 +154,7 @@ type NetDevEntry struct { Prefix Entry Netmask Entry Gateway Entry + MTU Entry Primary Entry Tags map[string]*Entry } @@ -163,6 +162,8 @@ type NetDevEntry struct { // string which is printed if no value is set const NoValue = "--" +/* +Has no real purpose as only New() needs it func init() { // Check that nodes.conf is found if !util.IsFile(ConfigFile) { @@ -171,3 +172,4 @@ func init() { return } } +*/ diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index 9ed639e4..66ba8acc 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -26,8 +26,7 @@ func FilterByName(set []NodeInfo, searchList []string) []NodeInfo { if len(searchList) > 0 { for _, search := range searchList { for _, entry := range set { - b, _ := regexp.MatchString("^"+search+"$", entry.Id.Get()) - if b { + if match, _ := regexp.MatchString("^"+search+"$", entry.Id.Get()); match { unique[entry.Id.Get()] = entry } } @@ -42,6 +41,24 @@ func FilterByName(set []NodeInfo, searchList []string) []NodeInfo { return ret } +/* +Filter a given map of NodeConf against given regular expression. +*/ +func FilterMapByName(inputMap map[string]*NodeConf, searchList []string) (retMap map[string]*NodeConf) { + retMap = map[string]*NodeConf{} + if len(searchList) > 0 { + for _, search := range searchList { + for name, nConf := range inputMap { + if match, _ := regexp.MatchString("^"+search+"$", name); match { + retMap[name] = nConf + } + } + + } + } + return retMap +} + /********** * * Sets diff --git a/internal/pkg/node/transformers.go b/internal/pkg/node/transformers.go index a50ca263..bed18213 100644 --- a/internal/pkg/node/transformers.go +++ b/internal/pkg/node/transformers.go @@ -2,8 +2,10 @@ package node import ( "reflect" + "strings" "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/wwlog" "github.com/spf13/cobra" ) @@ -186,6 +188,10 @@ func (nodeConf *NodeConf) getterFrom(nodeInfo NodeInfo, } } } + +/* +Create cmd line flags from the NodeConf fields +*/ func (nodeConf *NodeConf) CreateFlags(baseCmd *cobra.Command, excludeList []string) { nodeInfoType := reflect.TypeOf(nodeConf) nodeInfoVal := reflect.ValueOf(nodeConf) @@ -303,6 +309,20 @@ func (node *NodeInfo) SetAltFrom(n *NodeConf, profileName string) { node.setterFrom(n, profileName, (*Entry).SetAlt, (*Entry).SetAltSlice) } +/* +Populates all fields of NodeInfo with SetDefault from the +values of NodeConf. +*/ +func (node *NodeInfo) SetDefFrom(n *NodeConf) { + setWrap := func(entr *Entry, val string, nameArg string) { + entr.SetDefault(val) + } + setSliceWrap := func(entr *Entry, val []string, nameArg string) { + entr.SetDefaultSlice(val) + } + node.setterFrom(n, "", setWrap, setSliceWrap) +} + /* Abstract function which populates a NodeInfo from a NodeConf via setter functionns. @@ -438,3 +458,213 @@ func (info *NodeConf) Flatten() { } } } + +/* +Populates all fields of NetDevEntry with Set from the +values of NetDevs. +Actually not used, just for completeness. +*/ +func (netDev *NetDevEntry) SetFrom(netYaml *NetDevs) { + setWrap := func(entr *Entry, val string, nameArg string) { + entr.Set(val) + } + setSliceWrap := func(entr *Entry, val []string, nameArg string) { + entr.SetSlice(val) + } + netDev.setterFrom(netYaml, "", setWrap, setSliceWrap) +} + +/* +Populates all fields of NetDevEntry with SetAlt from the +values of NetDevs. The string profileName is used to +destermine from which source/NodeInfo the entry came +from. +Actually not used, just for completeness. +*/ +func (netDev *NetDevEntry) SetAltFrom(netYaml *NetDevs, profileName string) { + netDev.setterFrom(netYaml, profileName, (*Entry).SetAlt, (*Entry).SetAltSlice) +} + +/* +Populates all fields of NodeInfo with SetDefault from the +values of NodeConf. +*/ +func (netDev *NetDevEntry) SetDefFrom(netYaml *NetDevs) { + setWrap := func(entr *Entry, val string, nameArg string) { + entr.SetDefault(val) + } + setSliceWrap := func(entr *Entry, val []string, nameArg string) { + entr.SetDefaultSlice(val) + } + netDev.setterFrom(netYaml, "", setWrap, setSliceWrap) +} + +/* +Abstract function for setting a NetDevEntry from a NetDevs +*/ +func (netDev *NetDevEntry) setterFrom(netYaml *NetDevs, nameArg string, + setter func(*Entry, string, string), + setterSlice func(*Entry, []string, string)) { + netValues := reflect.ValueOf(netDev) + netInfoType := reflect.TypeOf(*netYaml) + netInfoVal := reflect.ValueOf(*netYaml) + for j := 0; j < netInfoType.NumField(); j++ { + netVal := netValues.Elem().FieldByName(netInfoType.Field(j).Name) + if netVal.IsValid() { + if netInfoVal.Field(j).Type().Kind() == reflect.String { + setter(netVal.Addr().Interface().((*Entry)), netInfoVal.Field(j).String(), nameArg) + } else if netVal.Type() == reflect.TypeOf(map[string]string{}) { + // danger zone following code is not tested + for key, val := range (netVal.Interface()).(map[string]string) { + //netTagMap := netInfoVal.Elem().Field(j).Interface().((map[string](*Entry))) + if _, ok := netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key]; !ok { + netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key] = new(Entry) + } + setter(netInfoVal.Elem().Field(j).Interface().((map[string](*Entry)))[key], val, nameArg) + } + } + } + } +} + +/* +Create a string slice, where every element represents a yaml entry +*/ +func (nodeConf *NodeConf) UnmarshalConf(excludeList []string) (lines []string) { + nodeInfoType := reflect.TypeOf(nodeConf) + nodeInfoVal := reflect.ValueOf(nodeConf) + // now iterate of every field + for i := 0; i < nodeInfoVal.Elem().NumField(); i++ { + if nodeInfoType.Elem().Field(i).Tag.Get("lopt") != "" { + if ymlStr, ok := getYamlString(nodeInfoType.Elem().Field(i), excludeList); ok { + lines = append(lines, ymlStr...) + } + } else if nodeInfoType.Elem().Field(i).Type.Kind() == reflect.Ptr { + nestType := reflect.TypeOf(nodeInfoVal.Elem().Field(i).Interface()) + if ymlStr, ok := getYamlString(nodeInfoType.Elem().Field(i), excludeList); ok { + lines = append(lines, ymlStr...) + } + for j := 0; j < nestType.Elem().NumField(); j++ { + if nestType.Elem().Field(j).Tag.Get("lopt") != "" && + !util.InSlice(excludeList, nestType.Elem().Field(j).Tag.Get("lopt")) { + if ymlStr, ok := getYamlString(nestType.Elem().Field(j), excludeList); ok { + for _, str := range ymlStr { + lines = append(lines, " "+str) + } + } + } + } + } else if nodeInfoType.Elem().Field(i).Type == reflect.TypeOf(map[string]*NetDevs(nil)) { + netMap := nodeInfoVal.Elem().Field(i).Interface().(map[string]*NetDevs) + // add a default network so that it can hold values + key := "default" + if len(netMap) == 0 { + netMap[key] = new(NetDevs) + } else { + for keyIt := range netMap { + key = keyIt + break + } + } + if ymlStr, ok := getYamlString(nodeInfoType.Elem().Field(i), excludeList); ok { + lines = append(lines, ymlStr[0]+":", " "+key+":") + netType := reflect.TypeOf(netMap[key]) + for j := 0; j < netType.Elem().NumField(); j++ { + if ymlStr, ok := getYamlString(netType.Elem().Field(j), excludeList); ok { + for _, str := range ymlStr { + lines = append(lines, " "+str) + } + } + } // lines + } // this + } //not + } //do + return lines +} + +/* +Get the string of the yaml tag +*/ +func getYamlString(myType reflect.StructField, excludeList []string) ([]string, bool) { + ymlStr := myType.Tag.Get("yaml") + if len(strings.Split(ymlStr, ",")) > 1 { + ymlStr = strings.Split(ymlStr, ",")[0] + } + if util.InSlice(excludeList, ymlStr) { + return []string{""}, false + } else if myType.Tag.Get("lopt") == "" && myType.Type.Kind() == reflect.String { + return []string{""}, false + } + if myType.Type.Kind() == reflect.String { + ymlStr += ": string" + return []string{ymlStr}, true + } else if myType.Type == reflect.TypeOf([]string{}) { + return []string{ymlStr + ":", " - string"}, true + } else if myType.Type == reflect.TypeOf(map[string]string{}) { + return []string{ymlStr + ":", " key: value"}, true + } else if myType.Type.Kind() == reflect.Ptr { + return []string{ymlStr + ":"}, true + } + 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 +} diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go index 05ebfe0d..ae2b7351 100644 --- a/internal/pkg/util/util.go +++ b/internal/pkg/util/util.go @@ -260,34 +260,17 @@ func FindFilterFiles( defer func() { err = FirstError(err, os.Chdir(cwd)) }() - err = os.Chdir(path) if err != nil { return ofiles, errors.Wrapf(err, "Failed to change path: %s", path) } - files := []string{} - - for _, pattern := range include { - - _files, err := filepath.Glob(pattern) - if err != nil { - return ofiles, errors.Wrapf(err, "Failed to apply pattern: %s", pattern) - } - wwlog.Debug("Including pattern: %s -> %d matches", pattern, len(_files)) - - files = append(files, _files...) - } - - - for i, pattern := range(ignore) { - if strings.HasPrefix(pattern, "./") { - ignore[i] = pattern[2:] - } + for i := range(ignore) { + ignore[i] = strings.TrimLeft(ignore[i], "/") + ignore[i] = strings.TrimPrefix(ignore[i], "./") wwlog.Debug("Ignore pattern (%d): %s", i, ignore[i]) } - if ignore_xdev { wwlog.Debug("Ignoring cross-device (xdev) files") } @@ -299,66 +282,72 @@ func FindFilterFiles( dev := path_stat.Sys().(*syscall.Stat_t).Dev - for _, ifile := range files { - stat, err := os.Stat(ifile) + + includeDirs := []string{} + ignoreDirs := []string{} + err = filepath.Walk(".", func(location string, info os.FileInfo, err error) error { if err != nil { - return ofiles, err + return err } - if stat.IsDir() { - // recursivly include from the matched directory + if location == "." { + return nil + } - num_init := len(ofiles) - err = filepath.Walk(ifile, func(location string, info os.FileInfo, err error) error { - var file string + var file string + if info.IsDir() { + file = location + "/" + } else { + file = location + } - if err != nil { - return err - } - - if location == "." { - return nil - } - - if info.IsDir() { - file = location + "/" - } else { - file = location - } - - if ignore_xdev && info.Sys().(*syscall.Stat_t).Dev != dev { - wwlog.Debug("Ignored (cross-device): %s", file) - return nil - } - - for i, pattern := range(ignore) { - m, err := filepath.Match(pattern, location) - if err != nil { - return err - } - - if m { - wwlog.Debug("Ignored (%d): %s", i, file) - return nil - } - } - - ofiles = append(ofiles, file) + if ignore_xdev && info.Sys().(*syscall.Stat_t).Dev != dev { + wwlog.Debug("Ignored (cross-device): %s", file) + return nil + } + for _, ignoreDir := range(ignoreDirs) { + if strings.HasPrefix(location, ignoreDir) { + wwlog.Debug("Ignored (dir): %s", file) return nil - }) - - num_final := len(ofiles) - wwlog.Debug("Included: %s -> %d files", ifile, num_final-num_init) - - if err != nil { - return ofiles, err } - }else{ - wwlog.Debug("Included: %s", ifile) - ofiles = append(ofiles, ifile) } - } + for i, pattern := range(ignore) { + m, err := filepath.Match(pattern, location) + if err != nil { + return err + } else if m { + wwlog.Debug("Ignored (%d): %s", i, file) + if info.IsDir() { + ignoreDirs = append(ignoreDirs, file) + } + return nil + } + } + + for _, includeDir := range(includeDirs) { + if strings.HasPrefix(location, includeDir) { + wwlog.Debug("Included (dir): %s", file) + ofiles = append(ofiles, location) + return nil + } + } + for i, pattern := range(include) { + m, err := filepath.Match(pattern, location) + if err != nil { + return err + } else if m { + wwlog.Debug("Included (%d): %s", i, file) + ofiles = append(ofiles, location) + if info.IsDir() { + includeDirs = append(includeDirs, file) + } + return nil + } + } + + return nil + }) return ofiles, err } @@ -715,3 +704,36 @@ func RunWWCTL(args ...string) (out []byte, err error) { return out, err } + +/* +Get size of given directory in bytes +*/ +func DirSize(path string) (int64, error) { + var size int64 + err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + size += info.Size() + } + return err + }) + return size, err +} + +/* +Convert bytes to human friendly format +*/ +func ByteToString(b int64) string { + const base = 1024 + if b < base { + return fmt.Sprintf("%d B", b) + } + div, exp := int64(base), 0 + for n := b / base; n >= base; n /= base { + div *= base + exp++ + } + return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp]) +} diff --git a/overlays/wwinit/etc/NetworkManager/system-connections/ww4-managed.ww b/overlays/wwinit/etc/NetworkManager/system-connections/ww4-managed.ww new file mode 100644 index 00000000..b58d1733 --- /dev/null +++ b/overlays/wwinit/etc/NetworkManager/system-connections/ww4-managed.ww @@ -0,0 +1,48 @@ +{{- $host := .BuildHost }} +{{- $time := .BuildTime }} +{{- $source := .BuildSource }} +{{- range $devname, $netdev := .NetDevs }} +{{- $filename := print "warewulf-" $devname ".conf" }} +{{- file $filename }} +# This file is autogenerated by warewulf +# Host: {{ $host }} +# Time: {{ $time }} +# Source: {{ $source }} +[connection] +id={{ $devname }} +interface-name={{ $netdev.Device }} +{{ if $netdev.Type -}} +type={{ $netdev.Type }} +{{ end -}} +autoconnect=true + +{{ if $netdev.Hwaddr -}} +{{ if eq $netdev.Type "ethernet" -}} +[ethernet] +mac-address={{ $netdev.Hwaddr }} +{{ end -}} +{{ end -}} + +{{ if eq $netdev.Type "infiniband" -}} +[infiniband] +transport-mode=datagram +{{ end -}} + +{{ if $netdev.IpCIDR -}} +[ipv4] +address={{ $netdev.IpCIDR }} +{{ if $netdev.Gateway -}} +gateway={{ $netdev.Gateway }} +{{ end -}} +method=manual +{{ end -}} + +{{/* always autoconfigure ipv6 */}} +[ipv6] +addr-gen-mode=stable-privacy +method=ignore +never-default=true +{{ if $netdev.Ipaddr6 -}} +ipaddr="{{ $netdev.Ipaddr6 }}" +{{ end -}} +{{ end -}} diff --git a/overlays/wwinit/etc/systemd/network/10-persistent-net.link.ww b/overlays/wwinit/etc/systemd/network/10-persistent-net.link.ww index e56c063c..aea7b8b3 100644 --- a/overlays/wwinit/etc/systemd/network/10-persistent-net.link.ww +++ b/overlays/wwinit/etc/systemd/network/10-persistent-net.link.ww @@ -3,7 +3,7 @@ # Time: {{.BuildTime}} # Source: {{.BuildSource}} {{range $devname, $netdev := .NetDevs}} -{{- if $netdev.Primary}} +{{- if $netdev.Hwaddr }} [Match] MACAddress={{$netdev.Hwaddr}} [Link] diff --git a/overlays/wwinit/etc/udev/rules.d/70-ww4-netname.rules b/overlays/wwinit/etc/udev/rules.d/70-ww4-netname.rules new file mode 100644 index 00000000..b15fac55 --- /dev/null +++ b/overlays/wwinit/etc/udev/rules.d/70-ww4-netname.rules @@ -0,0 +1,9 @@ +# This file is autogenerated by warewulf +# Host: {{.BuildHost}} +# Time: {{.BuildTime}} +# Source: {{.BuildSource}} +{{range $devname, $netdev := .NetDevs}} +{{- if $netdev.Hwaddr }} +SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="{{ $netdev.Hwaddr }}", NAME="{{ $netdev.Device }}" +{{ end -}} +{{ end -}} \ No newline at end of file