From 81ed8b242ce59e7865074357d8f1435e432674d6 Mon Sep 17 00:00:00 2001 From: Enno Gotthold Date: Tue, 23 Jul 2024 14:50:22 +0200 Subject: [PATCH 1/5] CI: Add Sphinx to docs linter --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5779ea6..bab0247 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -18,7 +18,7 @@ jobs: with: python-version: 3.9 - name: Install dependencies - run: pip install -U rstcheck doc8 + run: pip install -U rstcheck doc8 sphinx - name: Run rstcheck run: rstcheck -r docs - name: Run doc8 From 2910ffad9adf05a3e5dfc78dcd0bebd1e8d98ba0 Mon Sep 17 00:00:00 2001 From: Enno Gotthold Date: Tue, 23 Jul 2024 14:41:35 +0200 Subject: [PATCH 2/5] Update client to 0.5.0 --- Makefile | 2 +- cmd/aclsetup.go | 33 +- cmd/buildiso.go | 69 +- cmd/distro.go | 724 ++++++++++----- cmd/file.go | 356 ++++++-- cmd/hardlink.go | 10 +- cmd/image.go | 498 +++++++---- cmd/import.go | 53 +- cmd/item.go | 140 +++ cmd/list.go | 60 +- cmd/menu.go | 253 ++++-- cmd/metadata.go | 948 ++++++++++++++++++++ cmd/mgmtclass.go | 339 +++++-- cmd/mkloaders.go | 10 +- cmd/package.go | 296 ++++-- cmd/profile.go | 1088 +++++++++++++--------- cmd/replicate.go | 83 +- cmd/repo.go | 570 +++++++----- cmd/report.go | 120 ++- cmd/reposync.go | 32 +- cmd/root.go | 146 ++- cmd/setting.go | 45 +- cmd/signature.go | 83 +- cmd/sync.go | 47 +- cmd/system.go | 1689 +++++++++++++++++++++-------------- cmd/utils.go | 33 + cmd/utils_test.go | 39 + cmd/validateAutoinstalls.go | 10 +- cmd/version.go | 28 +- go.mod | 5 +- go.sum | 15 +- 31 files changed, 5698 insertions(+), 2126 deletions(-) create mode 100644 cmd/item.go create mode 100644 cmd/metadata.go create mode 100644 cmd/utils.go create mode 100644 cmd/utils_test.go diff --git a/Makefile b/Makefile index 3a2e19c..62eb235 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ build: clean: go clean - rm ${BINARY_NAME} + rm -f ${BINARY_NAME} rm -rf config/completions/* cleandoc: ## Cleans the docs directory. diff --git a/cmd/aclsetup.go b/cmd/aclsetup.go index 15d9a99..b55ae69 100644 --- a/cmd/aclsetup.go +++ b/cmd/aclsetup.go @@ -5,6 +5,8 @@ package cmd import ( + "fmt" + "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" ) @@ -14,9 +16,36 @@ var aclsetupCmd = &cobra.Command{ Short: "Adjust the access control list", Long: "Configures users/groups to run the Cobbler CLI as non-root.", Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient + addUserOption, err := cmd.Flags().GetString("adduser") + if err != nil { + return err + } + addGroupOption, err := cmd.Flags().GetString("addgroup") + if err != nil { + return err + } + removeUserOption, err := cmd.Flags().GetString("removeuser") + if err != nil { + return err + } + removeGroupOption, err := cmd.Flags().GetString("removegroup") + if err != nil { + return err + } + aclSetupOptions := cobblerclient.AclSetupOptions{ + AddUser: addUserOption, + AddGroup: addGroupOption, + RemoveUser: removeUserOption, + RemoveGroup: removeGroupOption, + } + eventId, err := Client.BackgroundAclSetup(aclSetupOptions) + if err != nil { + return err + } + fmt.Println("Event ID: ", eventId) + return nil }, } diff --git a/cmd/buildiso.go b/cmd/buildiso.go index a03bfb5..5ce6054 100644 --- a/cmd/buildiso.go +++ b/cmd/buildiso.go @@ -5,6 +5,8 @@ package cmd import ( + "fmt" + "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" ) @@ -13,9 +15,66 @@ var buildisoCmd = &cobra.Command{ Use: "buildiso", Short: "Build an ISO", Long: "Build all profiles into a bootable CD image. All flags are optional.", - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient + isoOption, err := cmd.Flags().GetString("iso") + if err != nil { + return err + } + distroOption, err := cmd.Flags().GetString("distro") + if err != nil { + return err + } + xorrisofsOption, err := cmd.Flags().GetString("mkisofs-opts") + if err != nil { + return err + } + profilesOption, err := cmd.Flags().GetStringSlice("profiles") + if err != nil { + return err + } + sourceOption, err := cmd.Flags().GetString("source") + if err != nil { + return err + } + systemsOption, err := cmd.Flags().GetStringSlice("systems") + if err != nil { + return err + } + tempdirOption, err := cmd.Flags().GetString("tempdir") + if err != nil { + return err + } + standaloneOption, err := cmd.Flags().GetBool("standalone") + if err != nil { + return err + } + excludeDnsOption, err := cmd.Flags().GetBool("exclude-dns") + if err != nil { + return err + } + airgappedOption, err := cmd.Flags().GetBool("airgapped") + if err != nil { + return err + } + buildisoOptions := cobblerclient.BuildisoOptions{ + Iso: isoOption, + Profiles: profilesOption, + Systems: systemsOption, + BuildisoDir: tempdirOption, + Distro: distroOption, + Standalone: standaloneOption, + Airgapped: airgappedOption, + Source: sourceOption, + ExcludeDns: excludeDnsOption, + XorrisofsOpts: xorrisofsOption, + } + eventId, err := Client.BackgroundBuildiso(buildisoOptions) + if err != nil { + return err + } + fmt.Printf("Event ID: %s\n", eventId) + return nil }, } @@ -28,9 +87,9 @@ func init() { buildisoCmd.Flags().Bool("exclude-dns", false, "prevents addition of name server addresses to the kernel boot options") buildisoCmd.Flags().String("iso", "", "output ISO to this file") buildisoCmd.Flags().String("mkisofs-opts", "", "extra options for mkisofs") - buildisoCmd.Flags().String("profiles", "", "use these profiles only") + buildisoCmd.Flags().StringSlice("profiles", []string{}, "use these profiles only") buildisoCmd.Flags().String("source", "", "used with --standalone to specify a source for the distribution files") - buildisoCmd.Flags().String("standalone", "", "creates a standalone ISO with all required distro files, but without any added repos") - buildisoCmd.Flags().String("systems", "", "use these systems only") + buildisoCmd.Flags().Bool("standalone", false, "creates a standalone ISO with all required distro files, but without any added repos") + buildisoCmd.Flags().StringSlice("systems", []string{}, "use these systems only") buildisoCmd.Flags().String("tempdir", "", "working directory") } diff --git a/cmd/distro.go b/cmd/distro.go index 0213a09..a579b3a 100644 --- a/cmd/distro.go +++ b/cmd/distro.go @@ -6,15 +6,314 @@ package cmd import ( "fmt" - "os" - - "github.com/spf13/cobra" - cobbler "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "github.com/spf13/pflag" ) -var distro *cobbler.Distro //nolint:golint,unused -var distros []*cobbler.Distro +func updateDistroFromFlags(cmd *cobra.Command, distro *cobbler.Distro) error { + var inPlace bool + var err error + if cmd.Flags().Lookup("in-place") != nil { + inPlace, err = cmd.Flags().GetBool("in-place") + if err != nil { + return err + } + } + cmd.Flags().Visit(func(flag *pflag.Flag) { + if err != nil { + // If one of the previous flags has had an error just directly return. + return + } + switch flag.Name { + // The rename & copy operations are special operations as such we cannot blindly set this inside here. + // Any rename & copy operation must be handled outside of this method. + case "arch": + var distroNewArch string + distroNewArch, err = cmd.Flags().GetString("arch") + if err != nil { + return + } + distro.Arch = distroNewArch + case "autoinstall-meta": + fallthrough + case "autoinstall-meta-inherit": + var distroNewAutoinstallMeta map[string]string + distroNewAutoinstallMeta, err = cmd.Flags().GetStringToString("autoinstall-meta") + if err != nil { + return + } + if inPlace { + err = Client.ModifyItemInPlace( + "distro", + distro.Name, + "autoinstall_meta", + convertMapStringToMapInterface(distroNewAutoinstallMeta), + ) + if err != nil { + return + } + } else { + distro.AutoinstallMeta.IsInherited = false + distro.AutoinstallMeta.Data = convertMapStringToMapInterface(distroNewAutoinstallMeta) + } + case "boot-files": + fallthrough + case "boot-files-inherit": + var distroNewBootFiles map[string]string + distroNewBootFiles, err = cmd.Flags().GetStringToString("boot-files") + if err != nil { + return + } + if cmd.Flags().Lookup("boot-files-inherit").Changed { + distro.BootFiles.Data = make(map[string]interface{}) + distro.BootFiles.IsInherited, err = cmd.Flags().GetBool("boot-files-inherit") + if err != nil { + return + } + } else { + distro.BootFiles.IsInherited = false + distro.BootFiles.Data = convertMapStringToMapInterface(distroNewBootFiles) + } + case "boot-loaders": + fallthrough + case "boot-loaders-inherit": + var distroNewBootLoaders []string + distroNewBootLoaders, err = cmd.Flags().GetStringSlice("boot-loaders") + if err != nil { + return + } + if cmd.Flags().Lookup("boot-loaders-inherit").Changed { + distro.BootLoaders.Data = []string{} + distro.BootLoaders.IsInherited, err = cmd.Flags().GetBool("boot-loaders-inherit") + if err != nil { + return + } + } else { + distro.BootLoaders.IsInherited = false + distro.BootLoaders.Data = distroNewBootLoaders + } + case "breed": + var distroNewBreed string + distroNewBreed, err = cmd.Flags().GetString("breed") + if err != nil { + return + } + distro.Breed = distroNewBreed + case "comment": + var distroNewComment string + distroNewComment, err = cmd.Flags().GetString("comment") + if err != nil { + return + } + distro.Comment = distroNewComment + case "fetchable-files": + fallthrough + case "fetchable-files-inherit": + var newFetchableFiles map[string]string + newFetchableFiles, err = cmd.Flags().GetStringToString("fetchable-files") + if err != nil { + return + } + if cmd.Flags().Lookup("fetchable-files-inherit").Changed { + distro.FetchableFiles.Data = make(map[string]interface{}) + distro.FetchableFiles.IsInherited, err = cmd.Flags().GetBool("fetchable-files-inherit") + if err != nil { + return + } + } else { + if inPlace { + err = Client.ModifyItemInPlace( + "distro", + distro.Name, + "fetchable_files", + convertMapStringToMapInterface(newFetchableFiles), + ) + if err != nil { + return + } + } else { + distro.FetchableFiles.IsInherited = false + distro.FetchableFiles.Data = convertMapStringToMapInterface(newFetchableFiles) + } + } + case "initrd": + var distroNewInitrd string + distroNewInitrd, err = cmd.Flags().GetString("initrd") + if err != nil { + return + } + distro.Initrd = distroNewInitrd + case "remote-boot-initrd": + var distroNewRemoteBootInitrd string + distroNewRemoteBootInitrd, err = cmd.Flags().GetString("remote-boot-initrd") + if err != nil { + return + } + distro.RemoteBootInitrd = distroNewRemoteBootInitrd + case "kernel": + var distroNewKernel string + distroNewKernel, err = cmd.Flags().GetString("kernel") + if err != nil { + return + } + distro.Kernel = distroNewKernel + case "remote-boot-kernel": + var distroNewRemoteBootKernel string + distroNewRemoteBootKernel, err = cmd.Flags().GetString("remote-boot-kernel") + if err != nil { + return + } + distro.RemoteBootKernel = distroNewRemoteBootKernel + case "kernel-options": + fallthrough + case "kernel-options-inherit": + if cmd.Flags().Lookup("kernel-options-inherit").Changed { + distro.KernelOptions.Data = make(map[string]interface{}) + distro.KernelOptions.IsInherited, err = cmd.Flags().GetBool("kernel-options-inherit") + if err != nil { + return + } + } else { + var newKernelOptions map[string]string + newKernelOptions, err = cmd.Flags().GetStringToString("kernel-options") + if err != nil { + return + } + if inPlace { + err = Client.ModifyItemInPlace( + "distro", + distro.Name, + "kernel_options", + convertMapStringToMapInterface(newKernelOptions), + ) + if err != nil { + return + } + } else { + distro.KernelOptions.IsInherited = false + distro.KernelOptions.Data = convertMapStringToMapInterface(newKernelOptions) + } + } + case "kernel-options-post": + fallthrough + case "kernel-options-post-inherit": + if cmd.Flags().Lookup("kernel-options-post-inherit").Changed { + distro.KernelOptionsPost.Data = make(map[string]interface{}) + distro.KernelOptionsPost.IsInherited, err = cmd.Flags().GetBool("kernel-options-post-inherit") + if err != nil { + return + } + } else { + var newKernelOptionsPost map[string]string + newKernelOptionsPost, err = cmd.Flags().GetStringToString("kernel-options-post") + if err != nil { + return + } + if inPlace { + err = Client.ModifyItemInPlace( + "distro", + distro.Name, + "kernel_options_post", + convertMapStringToMapInterface(newKernelOptionsPost), + ) + if err != nil { + return + } + } else { + distro.KernelOptionsPost.IsInherited = false + distro.KernelOptions.Data = convertMapStringToMapInterface(newKernelOptionsPost) + } + } + case "mgmt-classes": + fallthrough + case "mgmt-classes-inherit": + var distroNewMgmtClasses []string + distroNewMgmtClasses, err = cmd.Flags().GetStringSlice("mgmt-classes") + if err != nil { + return + } + if cmd.Flags().Lookup("mgmt-classes-inherit").Changed { + distro.MgmtClasses.Data = []string{} + distro.MgmtClasses.IsInherited, err = cmd.Flags().GetBool("mgmt-classes-inherit") + if err != nil { + return + } + } else { + distro.MgmtClasses.IsInherited = false + distro.MgmtClasses.Data = distroNewMgmtClasses + } + case "os-version": + var distroNewOsVersion string + distroNewOsVersion, err = cmd.Flags().GetString("os-version") + if err != nil { + return + } + distro.OSVersion = distroNewOsVersion + case "owners": + fallthrough + case "owners-inherit": + var distroNewOwners []string + distroNewOwners, err = cmd.Flags().GetStringSlice("owners") + if err != nil { + return + } + if cmd.Flags().Lookup("owners-inherit").Changed { + distro.Owners.Data = []string{} + distro.Owners.IsInherited, err = cmd.Flags().GetBool("owners-inherit") + if err != nil { + return + } + } else { + distro.Owners.IsInherited = false + distro.Owners.Data = distroNewOwners + } + case "redhat-management-key": + var distroNewRedhatManagementKey string + distroNewRedhatManagementKey, err = cmd.Flags().GetString("redhat-management-key") + if err != nil { + return + } + distro.RedhatManagementKey = distroNewRedhatManagementKey + case "template-files": + fallthrough + case "template-files-inherit": + if cmd.Flags().Lookup("template-files-inherit").Changed { + distro.TemplateFiles.Data = make(map[string]interface{}) + distro.TemplateFiles.IsInherited, err = cmd.Flags().GetBool("template-files-inherit") + if err != nil { + return + } + } else { + var newTemplateFiles map[string]string + newTemplateFiles, err = cmd.Flags().GetStringToString("template-files") + if err != nil { + return + } + if inPlace { + err = Client.ModifyItemInPlace( + "distro", + distro.Name, + "template_files", + convertMapStringToMapInterface(newTemplateFiles), + ) + if err != nil { + return + } + } else { + distro.TemplateFiles.IsInherited = false + distro.TemplateFiles.Data = convertMapStringToMapInterface(newTemplateFiles) + } + } + } + }) + if inPlace { + // Update distro in case we did modify the distro + distro.Meta.IsDirty = true + } + // Don't blindly return nil because maybe one of the flags had an issue retrieving an argument. + return err +} // distroCmd represents the distro command var distroCmd = &cobra.Command{ @@ -23,8 +322,7 @@ var distroCmd = &cobra.Command{ Long: `Let you manage distributions. See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-distro for more information.`, Run: func(cmd *cobra.Command, args []string) { - // TODO: call cobblerclient - cmd.Help() + _ = cmd.Help() }, } @@ -32,36 +330,28 @@ var distroAddCmd = &cobra.Command{ Use: "add", Short: "add distribution", Long: `Adds a distribution.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + newDistro := cobbler.NewDistro() + var err error - var newDistro cobbler.Distro // internal fields (ctime, mtime, depth, uid, source-repos, tree-build-time) cannot be modified - newDistro.Arch, _ = cmd.Flags().GetString("arch") - newDistro.BootFiles, _ = cmd.Flags().GetStringArray("boot-files") - newDistro.BootLoaders, _ = cmd.Flags().GetStringArray("boot-loaders") - newDistro.Breed, _ = cmd.Flags().GetString("breed") - newDistro.Comment, _ = cmd.Flags().GetString("comment") - newDistro.FetchableFiles, _ = cmd.Flags().GetStringArray("fetchable-files") - newDistro.Initrd, _ = cmd.Flags().GetString("initrd") - newDistro.Kernel, _ = cmd.Flags().GetString("kernel") - newDistro.KernelOptions, _ = cmd.Flags().GetStringArray("kernel-options") - newDistro.KernelOptionsPost, _ = cmd.Flags().GetStringArray("kernel-options-post") - newDistro.MGMTClasses, _ = cmd.Flags().GetStringArray("mgmt-classes") - newDistro.Name, _ = cmd.Flags().GetString("name") - newDistro.OSVersion, _ = cmd.Flags().GetString("os-version") - newDistro.Owners, _ = cmd.Flags().GetStringArray("owners") - // newDistro.RedHatManagementKey, _ = cmd.Flags().GetString("redhat-management-key") - newDistro.TemplateFiles, _ = cmd.Flags().GetStringArray("template-files") - - distro, err = Client.CreateDistro(newDistro) - - if checkError(err) == nil { - fmt.Printf("Distro %s created", newDistro.Name) - } else { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + newDistro.Name, err = cmd.Flags().GetString("name") + if err != nil { + return err } + // Update distro in-memory + err = updateDistroFromFlags(cmd, &newDistro) + if err != nil { + return err + } + // Now create the distro via XML-RPC + distro, err := Client.CreateDistro(newDistro) + if err != nil { + return err + } + fmt.Printf("Distro %s created\n", distro.Name) + return nil }, } @@ -69,10 +359,36 @@ var distroCopyCmd = &cobra.Command{ Use: "copy", Short: "copy distribution", Long: `Copies a distribution.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + dname, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + distroNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + + dhandle, err := Client.GetDistroHandle(dname) + if err != nil { + return err + } + err = Client.CopyDistro(dhandle, distroNewName) + if err != nil { + return err + } + newDistro, err := Client.GetDistro(distroNewName, false, false) + if err != nil { + return err + } + // Update distro in-memory + err = updateDistroFromFlags(cmd, newDistro) + if err != nil { + return err + } + // Update the distro via XML-RPC + return Client.UpdateDistro(newDistro) }, } @@ -80,93 +396,36 @@ var distroEditCmd = &cobra.Command{ Use: "edit", Short: "edit distribution", Long: `Edits a distribution.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() // find distro through its name - dname, _ := cmd.Flags().GetString("name") - var updateDistro, err = Client.GetDistro(dname) - - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) - } - - // internal fields (ctime, mtime, depth, uid, source-repos, tree-build-time) cannot be modified - var tmpArgs, _ = cmd.Flags().GetString("arch") - - if tmpArgs != "" { - updateDistro.Arch, _ = cmd.Flags().GetString("arch") - } - var tmpArgsArray, _ = cmd.Flags().GetStringArray("boot-files") - if len(tmpArgsArray) > 0 { - updateDistro.BootFiles, _ = cmd.Flags().GetStringArray("boot-files") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("boot-loaders") - if len(tmpArgsArray) > 0 { - updateDistro.BootLoaders, _ = cmd.Flags().GetStringArray("boot-loaders") - } - tmpArgs, _ = cmd.Flags().GetString("breed") - if tmpArgs != "" { - updateDistro.Breed, _ = cmd.Flags().GetString("breed") + dname, err := cmd.Flags().GetString("name") + if err != nil { + return err } - tmpArgs, _ = cmd.Flags().GetString("comment") - if tmpArgs != "" { - updateDistro.Comment, _ = cmd.Flags().GetString("comment") + // Get distro from the API + updateDistro, err := Client.GetDistro(dname, false, false) + if err != nil { + return err } - tmpArgsArray, _ = cmd.Flags().GetStringArray("fetchable-files") - if len(tmpArgsArray) > 0 { - updateDistro.FetchableFiles, _ = cmd.Flags().GetStringArray("fetchable-files") + // Update distro in-memory + err = updateDistroFromFlags(cmd, updateDistro) + if err != nil { + return err } - tmpArgs, _ = cmd.Flags().GetString("initrd") - if tmpArgs != "" { - updateDistro.Initrd, _ = cmd.Flags().GetString("initrd") - } - tmpArgs, _ = cmd.Flags().GetString("kernel") - if tmpArgs != "" { - updateDistro.Kernel, _ = cmd.Flags().GetString("kernel") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("kernel-options") - if len(tmpArgsArray) > 0 { - updateDistro.KernelOptions, _ = cmd.Flags().GetStringArray("kernel-options") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("kernel-options-post") - if len(tmpArgsArray) > 0 { - updateDistro.KernelOptionsPost, _ = cmd.Flags().GetStringArray("kernel-options-post") - } - tmpArgs, _ = cmd.Flags().GetString("mgmt-classes") - if tmpArgs != "" { - updateDistro.MGMTClasses, _ = cmd.Flags().GetStringArray("mgmt-classes") - } - tmpArgs, _ = cmd.Flags().GetString("name") - if tmpArgs != "" { - updateDistro.Name, _ = cmd.Flags().GetString("name") - } - tmpArgs, _ = cmd.Flags().GetString("os-version") - if tmpArgs != "" { - updateDistro.OSVersion, _ = cmd.Flags().GetString("os-version") - } - tmpArgs, _ = cmd.Flags().GetString("owners") - if tmpArgs != "" { - updateDistro.Owners, _ = cmd.Flags().GetStringArray("owners") - } - /* - tmpArgs, _ = cmd.Flags().GetString("redhat-management-key") - if tmpArgs != "" { - updateDistro.RedHatManagementKey, _ = cmd.Flags().GetString("redhat-management-key") + if updateDistro.Meta.IsDirty { + updateDistro, err = Client.GetDistro( + updateDistro.Name, + updateDistro.Meta.IsFlattened, + updateDistro.Meta.IsResolved, + ) + if err != nil { + return err } - */ - tmpArgsArray, _ = cmd.Flags().GetStringArray("template-files") - if len(tmpArgsArray) > 0 { - updateDistro.TemplateFiles, _ = cmd.Flags().GetStringArray("template-files") - } - - err = Client.UpdateDistro(updateDistro) - - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) } + // Now update distro via XML-RPC + return Client.UpdateDistro(updateDistro) }, } @@ -174,20 +433,9 @@ var distroFindCmd = &cobra.Command{ Use: "find", Short: "find distribution", Long: `Finds a given distribution.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - /* - dname, _ := cmd.Flags().GetString("name") - distro, err = Client.GetDistro(dname) - - if checkError(err) == nil { - str, _ := json.MarshalIndent(distro, "", " ") - fmt.Println(string(str)) - } else { - fmt.Fprintln(os.Stderr, err.Error()) - } - */ - notImplemented() + return FindItemNames(cmd, args, "distro") }, } @@ -195,17 +443,14 @@ var distroListCmd = &cobra.Command{ Use: "list", Short: "list all distributions", Long: `Lists all available distributions.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - distros, err = Client.GetDistros() - - if checkError(err) == nil { - fmt.Println(distros) - } else { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + distroNames, err := Client.ListDistroNames() + if err != nil { + return err } + listItems("distros", distroNames) + return nil }, } @@ -213,15 +458,18 @@ var distroRemoveCmd = &cobra.Command{ Use: "remove", Short: "remove distribution", Long: `Removes a given distribution.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - dname, _ := cmd.Flags().GetString("name") - err := Client.DeleteDistro(dname) - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + dname, err := cmd.Flags().GetString("name") + if err != nil { + return err } + recursiveDelete, err := cmd.Flags().GetBool("recursive") + if err != nil { + return err + } + return Client.DeleteDistroRecursive(dname, recursiveDelete) }, } @@ -229,21 +477,76 @@ var distroRenameCmd = &cobra.Command{ Use: "rename", Short: "rename distribution", Long: `Renames a given distribution.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + + // Get the name and newname flags + distroName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + distroNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + + // Get the distro API handle + distroHandle, err := Client.GetDistroHandle(distroName) + if err != nil { + return err + } + // Perform the rename operation server-side + err = Client.RenameDistro(distroHandle, distroNewName) + if err != nil { + return err + } + // Retrieve the renamed distro from the API + newDistro, err := Client.GetDistro(distroNewName, false, false) + if err != nil { + return err + } + // Now edit the distro in-memory + err = updateDistroFromFlags(cmd, newDistro) + if err != nil { + return err + } + // Now update the distro via XML-RPC + return Client.UpdateDistro(newDistro) }, } +func reportDistros(distroNames []string) error { + for _, itemName := range distroNames { + distro, err := Client.GetDistro(itemName, false, false) + if err != nil { + return err + } + printStructured(distro) + fmt.Println("") + } + return nil +} + var distroReportCmd = &cobra.Command{ Use: "report", Short: "list all distributions in detail", Long: `Shows detailed information about all distributions.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + name, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + itemNames := make([]string, 0) + if name == "" { + itemNames, err = Client.ListDistroNames() + if err != nil { + return err + } + } else { + itemNames = append(itemNames, name) + } + return reportDistros(itemNames) }, } @@ -259,124 +562,53 @@ func init() { distroCmd.AddCommand(distroReportCmd) // local flags for distro add - distroAddCmd.Flags().String("name", "", "the distro name") - distroAddCmd.Flags().String("arch", "", "Architecture") - distroAddCmd.Flags().String("autoinstall-meta", "", "automatic installation template metadata") - distroAddCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - distroAddCmd.Flags().String("boot-loaders", "", "boot loaders (network installation boot loaders)") - distroAddCmd.Flags().String("breed", "", "Breed (what is the type of the distribution?)") - distroAddCmd.Flags().String("comment", "", "free form text description") - distroAddCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - distroAddCmd.Flags().String("initrd", "", "initrd (absolute path on filesystem)") - distroAddCmd.Flags().String("remote-boot-initrd", "", "remote boot initrd (URL the bootloader directly retrieves and boots from)") - distroAddCmd.Flags().String("kernel", "", "Kernel (absolute path on filesystem)") - distroAddCmd.Flags().String("remote-boot-kernel", "", "remote boot kernel (URL the bootloader directly retrieves and boots from)") - distroAddCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - distroAddCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - distroAddCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - distroAddCmd.Flags().String("os-version", "", "OS version (needed for some virtualization optimizations)") - distroAddCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - distroAddCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - distroAddCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") - distroAddCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") + addCommonArgs(distroAddCmd) + addStringFlags(distroAddCmd, distroStringFlagMetadata) + addStringSliceFlags(distroAddCmd, distroStringSliceFlagMetadata) + addMapFlags(distroAddCmd, distroMapFlagMetadata) + // Required Flags + err := distroAddCmd.MarkFlagRequired("name") + if err != nil { + panic(err) + } // local flags for distro copy - distroCopyCmd.Flags().String("name", "", "the distro name") + addCommonArgs(distroCopyCmd) + addStringFlags(distroCopyCmd, distroStringFlagMetadata) + addStringSliceFlags(distroCopyCmd, distroStringSliceFlagMetadata) + addMapFlags(distroCopyCmd, distroMapFlagMetadata) distroCopyCmd.Flags().String("newname", "", "the new distro name") - distroCopyCmd.Flags().String("arch", "", "Architecture") - distroCopyCmd.Flags().String("autoinstall-meta", "", "automatic installation template metadata") - distroCopyCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - distroCopyCmd.Flags().String("boot-loaders", "", "boot loaders (network installation boot loaders)") - distroCopyCmd.Flags().String("breed", "", "Breed (what is the type of the distribution?)") - distroCopyCmd.Flags().String("comment", "", "free form text description") - distroCopyCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - distroCopyCmd.Flags().String("initrd", "", "initrd (absolute path on filesystem)") - distroCopyCmd.Flags().String("remote-boot-initrd", "", "remote boot initrd (URL the bootloader directly retrieves and boots from)") - distroCopyCmd.Flags().String("kernel", "", "Kernel (absolute path on filesystem)") - distroCopyCmd.Flags().String("remote-boot-kernel", "", "remote boot kernel (URL the bootloader directly retrieves and boots from)") - distroCopyCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - distroCopyCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - distroCopyCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - distroCopyCmd.Flags().String("os-version", "", "OS version (needed for some virtualization optimizations)") - distroCopyCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - distroCopyCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - distroCopyCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") distroCopyCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") // local flags for distro edit - distroEditCmd.Flags().String("name", "", "the distro name") - distroEditCmd.Flags().String("arch", "", "Architecture") - distroEditCmd.Flags().String("autoinstall-meta", "", "automatic installation template metadata") - distroEditCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - distroEditCmd.Flags().String("boot-loaders", "", "boot loaders (network installation boot loaders)") - distroEditCmd.Flags().String("breed", "", "Breed (what is the type of the distribution?)") - distroEditCmd.Flags().String("comment", "", "free form text description") - distroEditCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - distroEditCmd.Flags().String("initrd", "", "initrd (absolute path on filesystem)") - distroEditCmd.Flags().String("remote-boot-initrd", "", "remote boot initrd (URL the bootloader directly retrieves and boots from)") - distroEditCmd.Flags().String("kernel", "", "Kernel (absolute path on filesystem)") - distroEditCmd.Flags().String("remote-boot-kernel", "", "remote boot kernel (URL the bootloader directly retrieves and boots from)") - distroEditCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - distroEditCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - distroEditCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - distroEditCmd.Flags().String("os-version", "", "OS version (needed for some virtualization optimizations)") - distroEditCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - distroEditCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - distroEditCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") + addCommonArgs(distroEditCmd) + addStringFlags(distroEditCmd, distroStringFlagMetadata) + addStringSliceFlags(distroEditCmd, distroStringSliceFlagMetadata) + addMapFlags(distroEditCmd, distroMapFlagMetadata) distroEditCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") // local flags for distro find - distroFindCmd.Flags().String("name", "", "the distro name") + addCommonArgs(distroFindCmd) + addStringFlags(distroFindCmd, distroStringFlagMetadata) + addStringSliceFlags(distroFindCmd, distroStringSliceFlagMetadata) + addMapFlags(distroFindCmd, distroMapFlagMetadata) distroFindCmd.Flags().String("ctime", "", "") distroFindCmd.Flags().String("depth", "", "") distroFindCmd.Flags().String("mtime", "", "") distroFindCmd.Flags().String("source-repos", "", "source repositories") distroFindCmd.Flags().String("tree-build-time", "", "tree build time") distroFindCmd.Flags().String("uid", "", "UID") - distroFindCmd.Flags().String("arch", "", "Architecture") - distroFindCmd.Flags().String("autoinstall-meta", "", "automatic installation template metadata") - distroFindCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - distroFindCmd.Flags().String("boot-loaders", "", "boot loaders (network installation boot loaders)") - distroFindCmd.Flags().String("breed", "", "Breed (what is the type of the distribution?)") - distroFindCmd.Flags().String("comment", "", "free form text description") - distroFindCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - distroFindCmd.Flags().String("initrd", "", "initrd (absolute path on filesystem)") - distroFindCmd.Flags().String("remote-boot-initrd", "", "remote boot initrd (URL the bootloader directly retrieves and boots from)") - distroFindCmd.Flags().String("kernel", "", "Kernel (absolute path on filesystem)") - distroFindCmd.Flags().String("remote-boot-kernel", "", "remote boot kernel (URL the bootloader directly retrieves and boots from)") - distroFindCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - distroFindCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - distroFindCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - distroFindCmd.Flags().String("os-version", "", "OS version (needed for some virtualization optimizations)") - distroFindCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - distroFindCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - distroFindCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") // local flags for distro remove distroRemoveCmd.Flags().String("name", "", "the distro name") distroRemoveCmd.Flags().Bool("recursive", false, "also delete child objects") // local flags for distro rename - distroRenameCmd.Flags().String("name", "", "the distro name") + addCommonArgs(distroRenameCmd) + addStringFlags(distroRenameCmd, distroStringFlagMetadata) + addStringSliceFlags(distroRenameCmd, distroStringSliceFlagMetadata) + addMapFlags(distroRenameCmd, distroMapFlagMetadata) distroRenameCmd.Flags().String("newname", "", "the new distro name") - distroRenameCmd.Flags().String("arch", "", "Architecture") - distroRenameCmd.Flags().String("autoinstall-meta", "", "automatic installation template metadata") - distroRenameCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - distroRenameCmd.Flags().String("boot-loaders", "", "boot loaders (network installation boot loaders)") - distroRenameCmd.Flags().String("breed", "", "Breed (what is the type of the distribution?)") - distroRenameCmd.Flags().String("comment", "", "free form text description") - distroRenameCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - distroRenameCmd.Flags().String("initrd", "", "initrd (absolute path on filesystem)") - distroRenameCmd.Flags().String("remote-boot-initrd", "", "remote boot initrd (URL the bootloader directly retrieves and boots from)") - distroRenameCmd.Flags().String("kernel", "", "Kernel (absolute path on filesystem)") - distroRenameCmd.Flags().String("remote-boot-kernel", "", "remote boot kernel (URL the bootloader directly retrieves and boots from)") - distroRenameCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - distroRenameCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - distroRenameCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - distroRenameCmd.Flags().String("os-version", "", "OS version (needed for some virtualization optimizations)") - distroRenameCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - distroRenameCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - distroRenameCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") distroRenameCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") // local flags for distro report diff --git a/cmd/file.go b/cmd/file.go index 2e562f2..0c5eded 100644 --- a/cmd/file.go +++ b/cmd/file.go @@ -5,9 +5,116 @@ package cmd import ( + "fmt" + cobbler "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) +func updateFileFromFlags(cmd *cobra.Command, file *cobbler.File) error { + // TODO: in-place flag + // var inPlace bool + var err error + if cmd.Flags().Lookup("in-place") != nil { + // inPlace, err := cmd.Flags().GetBool("in-place") + _, err = cmd.Flags().GetBool("in-place") + if err != nil { + return err + } + } + cmd.Flags().Visit(func(flag *pflag.Flag) { + if err != nil { + // If one of the previous flags has had an error just directly return. + return + } + switch flag.Name { + // The rename & copy operations are special operations as such we cannot blindly set this inside here. + // Any rename & copy operation must be handled outside of this method. + case "comment": + var fileNewComment string + fileNewComment, err = cmd.Flags().GetString("comment") + if err != nil { + return + } + file.Comment = fileNewComment + case "owners": + fallthrough + case "owners-inherit": + var fileNewOwners []string + fileNewOwners, err = cmd.Flags().GetStringSlice("owners") + if err != nil { + return + } + if cmd.Flags().Lookup("owners-inherit").Changed { + file.Owners.Data = []string{} + file.Owners.IsInherited, err = cmd.Flags().GetBool("owners-inherit") + if err != nil { + return + } + } else { + file.Owners.IsInherited = false + file.Owners.Data = fileNewOwners + } + case "action": + var fileNewAction string + fileNewAction, err = cmd.Flags().GetString("action") + if err != nil { + return + } + file.Action = fileNewAction + case "mode": + var fileNewMode string + fileNewMode, err = cmd.Flags().GetString("mode") + if err != nil { + return + } + file.Mode = fileNewMode + case "template": + var fileNewTemplate string + fileNewTemplate, err = cmd.Flags().GetString("template") + if err != nil { + return + } + file.Template = fileNewTemplate + case "path": + var fileNewPath string + fileNewPath, err = cmd.Flags().GetString("path") + if err != nil { + return + } + file.Path = fileNewPath + case "group": + var fileNewGroup string + fileNewGroup, err = cmd.Flags().GetString("group") + if err != nil { + return + } + file.Group = fileNewGroup + case "owner": + if cmd.Flags().Lookup("owners-inherit").Changed { + file.Owners.IsInherited, err = cmd.Flags().GetBool("owners-inherit") + if err != nil { + return + } + } else { + file.Owners.Data, err = cmd.Flags().GetStringSlice("owners") + if err != nil { + return + } + } + case "is-dir": + var fileNewIsDir bool + fileNewIsDir, err = cmd.Flags().GetBool("is-dir") + if err != nil { + return + } + file.IsDir = fileNewIsDir + } + }) + // Don't blindly return nil because maybe one of the flags had an issue retrieving an argument. + return err +} + // fileCmd represents the file command var fileCmd = &cobra.Command{ Use: "file", @@ -15,7 +122,7 @@ var fileCmd = &cobra.Command{ Long: `Let you manage files. See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-file for more information.`, Run: func(cmd *cobra.Command, args []string) { - cmd.Help() + _ = cmd.Help() }, } @@ -23,11 +130,28 @@ var fileAddCmd = &cobra.Command{ Use: "add", Short: "add file", Long: `Adds a file.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + newFile := cobbler.NewFile() + var err error - // TODO: call cobblerclient - notImplemented() + // Get special name flag + newFile.Name, err = cmd.Flags().GetString("name") + if err != nil { + return err + } + // Update with the rest of the flags + err = updateFileFromFlags(cmd, &newFile) + if err != nil { + return err + } + // Now create the file via XML-RPC + file, err := Client.CreateFile(newFile) + if err != nil { + return err + } + fmt.Printf("File %s created\n", file.Name) + return nil }, } @@ -35,11 +159,37 @@ var fileCopyCmd = &cobra.Command{ Use: "copy", Short: "copy file", Long: `Copies a file.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get special name and newname flags + fileName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + fileNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + + // Now copy the file + fileHandle, err := Client.GetFileHandle(fileName) + if err != nil { + return err + } + err = Client.CopyFile(fileHandle, fileNewName) + if err != nil { + return err + } + newFile, err := Client.GetFile(fileNewName, false, false) + if err != nil { + return err + } + err = updateFileFromFlags(cmd, newFile) + if err != nil { + return err + } + return Client.UpdateFile(newFile) }, } @@ -47,11 +197,27 @@ var fileEditCmd = &cobra.Command{ Use: "edit", Short: "edit file", Long: `Edits a file.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get the file name + fileName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + + // Now get the file from the API + newFile, err := Client.GetFile(fileName, false, false) + if err != nil { + return err + } + // Update the file in-memory + err = updateFileFromFlags(cmd, newFile) + if err != nil { + return err + } + // Now update the file via XML-RPC + return Client.UpdateFile(newFile) }, } @@ -59,11 +225,9 @@ var fileFindCmd = &cobra.Command{ Use: "find", Short: "find file", Long: `Finds a given file.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return FindItemNames(cmd, args, "file") }, } @@ -73,9 +237,11 @@ var fileListCmd = &cobra.Command{ Long: `Lists all available files.`, Run: func(cmd *cobra.Command, args []string) { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + fileNames, err := Client.ListFileNames() + if err != nil { + fmt.Println(err) + } + listItems("files", fileNames) }, } @@ -83,11 +249,9 @@ var fileRemoveCmd = &cobra.Command{ Use: "remove", Short: "remove file", Long: `Removes a given file.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return RemoveItemRecursive(cmd, args, "file") }, } @@ -95,23 +259,76 @@ var fileRenameCmd = &cobra.Command{ Use: "rename", Short: "rename file", Long: `Renames a given file.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get the special name and newname flags + fileName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + fileNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + + // Get the file handle + fileHandle, err := Client.GetFileHandle(fileName) + if err != nil { + return err + } + // Rename the file (server-side) + err = Client.RenameFile(fileHandle, fileNewName) + if err != nil { + return err + } + // Get the renamed file from the API + newFile, err := Client.GetFile(fileNewName, false, false) + if err != nil { + return err + } + // Update the file in-memory + err = updateFileFromFlags(cmd, newFile) + if err != nil { + return err + } + // Update the file via XML-RPC + return Client.UpdateFile(newFile) }, } +func reportFiles(fileNames []string) error { + for _, itemName := range fileNames { + file, err := Client.GetFile(itemName, false, false) + if err != nil { + return err + } + printStructured(file) + fmt.Println("") + } + return nil +} + var fileReportCmd = &cobra.Command{ Use: "report", Short: "list all files in detail", Long: `Shows detailed information about all files.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + name, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + itemNames := make([]string, 0) + if name == "" { + itemNames, err = Client.ListFileNames() + if err != nil { + return err + } + } else { + itemNames = append(itemNames, name) + } + return reportFiles(itemNames) }, } @@ -127,95 +344,42 @@ func init() { fileCmd.AddCommand(fileReportCmd) // local flags for file add - fileAddCmd.Flags().String("name", "", "the file name") - fileAddCmd.Flags().String("ctime", "", "") - fileAddCmd.Flags().String("depth", "", "") - fileAddCmd.Flags().String("mtime", "", "") - fileAddCmd.Flags().String("uid", "", "") - fileAddCmd.Flags().String("comment", "", "free form text description") - fileAddCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - fileAddCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - fileAddCmd.Flags().String("action", "", "create or remove file resource") - fileAddCmd.Flags().String("mode", "", "file modes") - fileAddCmd.Flags().String("template", "", "the template for the file") - fileAddCmd.Flags().String("path", "", "the path of the file") - fileAddCmd.Flags().String("group", "", "file owner group in file system") - fileAddCmd.Flags().String("owner", "", "file owner user in file system") - fileAddCmd.Flags().Bool("is-dir", false, "treat file resource as a directory") + addCommonArgs(fileAddCmd) + addStringFlags(fileAddCmd, fileStringFlagMetadata) + addBoolFlags(fileAddCmd, fileBoolFlagMetadata) // local flags for file copy - fileCopyCmd.Flags().String("name", "", "the file name") + addCommonArgs(fileCopyCmd) + addStringFlags(fileCopyCmd, fileStringFlagMetadata) + addBoolFlags(fileCopyCmd, fileBoolFlagMetadata) fileCopyCmd.Flags().String("newname", "", "the new file name") - fileCopyCmd.Flags().String("ctime", "", "") - fileCopyCmd.Flags().String("depth", "", "") - fileCopyCmd.Flags().String("mtime", "", "") - fileCopyCmd.Flags().String("uid", "", "") - fileCopyCmd.Flags().String("comment", "", "free form text description") - fileCopyCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") fileCopyCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - fileCopyCmd.Flags().String("action", "", "create or remove file resource") - fileCopyCmd.Flags().String("mode", "", "file modes") - fileCopyCmd.Flags().String("template", "", "the template for the file") - fileCopyCmd.Flags().String("path", "", "the path of the file") - fileCopyCmd.Flags().String("group", "", "file owner group in file system") - fileCopyCmd.Flags().String("owner", "", "file owner user in file system") - fileCopyCmd.Flags().Bool("is-dir", false, "treat file resource as a directory") // local flags for file edit - fileEditCmd.Flags().String("name", "", "the file name") - fileEditCmd.Flags().String("ctime", "", "") - fileEditCmd.Flags().String("depth", "", "") - fileEditCmd.Flags().String("mtime", "", "") - fileEditCmd.Flags().String("uid", "", "") - fileEditCmd.Flags().String("comment", "", "free form text description") - fileEditCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") + addCommonArgs(fileEditCmd) + addStringFlags(fileEditCmd, fileStringFlagMetadata) + addBoolFlags(fileEditCmd, fileBoolFlagMetadata) fileEditCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - fileEditCmd.Flags().String("action", "", "create or remove file resource") - fileEditCmd.Flags().String("mode", "", "file modes") - fileEditCmd.Flags().String("template", "", "the template for the file") - fileEditCmd.Flags().String("path", "", "the path of the file") - fileEditCmd.Flags().String("group", "", "file owner group in file system") - fileEditCmd.Flags().String("owner", "", "file owner user in file system") - fileEditCmd.Flags().Bool("is-dir", false, "treat file resource as a directory") // local flags for file find - fileFindCmd.Flags().String("name", "", "the file name") + addCommonArgs(fileFindCmd) + addStringFlags(fileFindCmd, fileStringFlagMetadata) + addBoolFlags(fileFindCmd, fileBoolFlagMetadata) fileFindCmd.Flags().String("ctime", "", "") fileFindCmd.Flags().String("depth", "", "") fileFindCmd.Flags().String("mtime", "", "") fileFindCmd.Flags().String("uid", "", "") - fileFindCmd.Flags().String("comment", "", "free form text description") - fileFindCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - fileFindCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - fileFindCmd.Flags().String("action", "", "create or remove file resource") - fileFindCmd.Flags().String("mode", "", "file modes") - fileFindCmd.Flags().String("template", "", "the template for the file") - fileFindCmd.Flags().String("path", "", "the path of the file") - fileFindCmd.Flags().String("group", "", "file owner group in file system") - fileFindCmd.Flags().String("owner", "", "file owner user in file system") - fileFindCmd.Flags().Bool("is-dir", false, "treat file resource as a directory") // local flags for file remove fileRemoveCmd.Flags().String("name", "", "the file name") fileRemoveCmd.Flags().Bool("recursive", false, "also delete child objects") // local flags for file rename - fileRenameCmd.Flags().String("name", "", "the file name") + addCommonArgs(fileRenameCmd) + addStringFlags(fileRenameCmd, fileStringFlagMetadata) + addBoolFlags(fileRenameCmd, fileBoolFlagMetadata) fileRenameCmd.Flags().String("newname", "", "the new file name") - fileRenameCmd.Flags().String("ctime", "", "") - fileRenameCmd.Flags().String("depth", "", "") - fileRenameCmd.Flags().String("mtime", "", "") - fileRenameCmd.Flags().String("uid", "", "") - fileRenameCmd.Flags().String("comment", "", "free form text description") - fileRenameCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") fileRenameCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - fileRenameCmd.Flags().String("action", "", "create or remove file resource") - fileRenameCmd.Flags().String("mode", "", "file modes") - fileRenameCmd.Flags().String("template", "", "the template for the file") - fileRenameCmd.Flags().String("path", "", "the path of the file") - fileRenameCmd.Flags().String("group", "", "file owner group in file system") - fileRenameCmd.Flags().String("owner", "", "file owner user in file system") - fileRenameCmd.Flags().Bool("is-dir", false, "treat file resource as a directory") // local flags for file report fileReportCmd.Flags().String("name", "", "the file name") diff --git a/cmd/hardlink.go b/cmd/hardlink.go index 28df71c..f84d447 100644 --- a/cmd/hardlink.go +++ b/cmd/hardlink.go @@ -5,6 +5,7 @@ package cmd import ( + "fmt" "github.com/spf13/cobra" ) @@ -13,9 +14,14 @@ var hardlinkCmd = &cobra.Command{ Use: "hardlink", Short: "Hardlink files", Long: "Hardlink all files where it is possible to improve performance.", - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient + eventId, err := Client.BackgroundHardlink() + if err != nil { + return err + } + fmt.Printf("Event ID: %s\n", eventId) + return nil }, } diff --git a/cmd/image.go b/cmd/image.go index 761efed..bf142d2 100644 --- a/cmd/image.go +++ b/cmd/image.go @@ -5,9 +5,194 @@ package cmd import ( + "fmt" + cobbler "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) +func updateImageFromFlags(cmd *cobra.Command, image *cobbler.Image) error { + // TODO: in-place flag + // inPlace, err := cmd.Flags().GetBool("in-place") + _, err := cmd.Flags().GetBool("in-place") + if err != nil { + return err + } + cmd.Flags().Visit(func(flag *pflag.Flag) { + switch flag.Name { + // The rename & copy operations are special operations as such we cannot blindly set this inside here. + // Any rename & copy operation must be handled outside of this method. + case "comment": + var imageNewComment string + imageNewComment, err = cmd.Flags().GetString("comment") + if err != nil { + return + } + image.Comment = imageNewComment + case "arch": + var imageNewArch string + imageNewArch, err = cmd.Flags().GetString("arch") + if err != nil { + return + } + image.Arch = imageNewArch + case "breed": + var imageNewBreed string + imageNewBreed, err = cmd.Flags().GetString("breed") + if err != nil { + return + } + image.Breed = imageNewBreed + case "owners": + fallthrough + case "owners-inherit": + if cmd.Flags().Lookup("owners-inherit").Changed { + image.Owners.Data = []string{} + image.Owners.IsInherited, err = cmd.Flags().GetBool("owners-inherit") + if err != nil { + return + } + } else { + var imageNewOwners []string + imageNewOwners, err = cmd.Flags().GetStringSlice("owners") + if err != nil { + return + } + image.Owners.IsInherited = false + image.Owners.Data = imageNewOwners + } + case "parent": + var imageNewParent string + imageNewParent, err = cmd.Flags().GetString("parent") + if err != nil { + return + } + image.Parent = imageNewParent + case "file": + var imageNewFile string + imageNewFile, err = cmd.Flags().GetString("file") + if err != nil { + return + } + image.File = imageNewFile + case "image-type": + var imageNewImageType string + imageNewImageType, err = cmd.Flags().GetString("image-type") + if err != nil { + return + } + image.ImageType = imageNewImageType + case "network-count": + var imageNewNetworkCount int + imageNewNetworkCount, err = cmd.Flags().GetInt("network-count") + if err != nil { + return + } + image.NetworkCount = imageNewNetworkCount + case "os-version": + var imageNewOsVersion string + imageNewOsVersion, err = cmd.Flags().GetString("os-version") + if err != nil { + return + } + image.OsVersion = imageNewOsVersion + case "menu": + var imageNewMenu string + imageNewMenu, err = cmd.Flags().GetString("menu") + if err != nil { + return + } + image.Menu = imageNewMenu + case "boot-loaders": + var imageNewBootLoaders []string + imageNewBootLoaders, err = cmd.Flags().GetStringSlice("boot-loaders") + if err != nil { + return + } + image.BootLoaders = imageNewBootLoaders + case "virt-auto-boot": + var imageNewVirtAutoBoot bool + imageNewVirtAutoBoot, err = cmd.Flags().GetBool("virt-auto-boot") + if err != nil { + return + } + image.VirtAutoBoot = imageNewVirtAutoBoot + case "virt-bridge": + var imageNewVirtBridge string + imageNewVirtBridge, err = cmd.Flags().GetString("virt-bridge") + if err != nil { + return + } + image.VirtBridge = imageNewVirtBridge + case "virt-cpus": + var imageNewVirtCpus int + imageNewVirtCpus, err = cmd.Flags().GetInt("virt-cpus") + if err != nil { + return + } + image.VirtCpus = imageNewVirtCpus + case "virt-disk-driver": + var imageNewVirtDiskDriver string + imageNewVirtDiskDriver, err = cmd.Flags().GetString("virt-disk-driver") + if err != nil { + return + } + image.VirtDiskDriver = imageNewVirtDiskDriver + case "virt-file-size": + fallthrough + case "virt-file-size-inherit": + if cmd.Flags().Lookup("owners-inherit").Changed { + image.VirtFileSize.Data = 0 + image.VirtFileSize.IsInherited, err = cmd.Flags().GetBool("owners-inherit") + if err != nil { + return + } + } else { + var imageNewVirtFileSize float64 + imageNewVirtFileSize, err = cmd.Flags().GetFloat64("virt-file-size") + if err != nil { + return + } + image.VirtFileSize.IsInherited = false + image.VirtFileSize.Data = imageNewVirtFileSize + } + case "virt-path": + var imageNewVirtPath string + imageNewVirtPath, err = cmd.Flags().GetString("virt-path") + if err != nil { + return + } + image.VirtPath = imageNewVirtPath + case "virt-ram": + fallthrough + case "virt-ram-inherit": + if cmd.Flags().Lookup("owners-inherit").Changed { + image.VirtRam.Data = 0 + image.VirtRam.IsInherited, err = cmd.Flags().GetBool("owners-inherit") + if err != nil { + return + } + } else { + var imageNewVirtRam int + imageNewVirtRam, err = cmd.Flags().GetInt("virt-ram") + if err != nil { + return + } + image.VirtRam.IsInherited = false + image.VirtRam.Data = imageNewVirtRam + } + case "virt-type": + var imageNewVirtType string + imageNewVirtType, err = cmd.Flags().GetString("virt-type") + if err != nil { + return + } + image.VirtType = imageNewVirtType + } + }) + return err +} + // imageCmd represents the image command var imageCmd = &cobra.Command{ Use: "image", @@ -15,7 +200,7 @@ var imageCmd = &cobra.Command{ Long: `Let you manage images. See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-image for more information.`, Run: func(cmd *cobra.Command, args []string) { - cmd.Help() + _ = cmd.Help() }, } @@ -23,11 +208,26 @@ var imageAddCmd = &cobra.Command{ Use: "add", Short: "add image", Long: `Adds a image.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + newImage := cobbler.NewImage() + var err error + newImage.Name, err = cmd.Flags().GetString("name") + if err != nil { + return err + } + // Update image in-memory + err = updateImageFromFlags(cmd, &newImage) + if err != nil { + return err + } + // Now create the image via XML-RPC + system, err := Client.CreateImage(newImage) + if err != nil { + return err + } + fmt.Printf("System %s created\n", system.Name) + return nil }, } @@ -35,11 +235,36 @@ var imageCopyCmd = &cobra.Command{ Use: "copy", Short: "copy image", Long: `Copies a image.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + imageName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + imageNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + + imageHandle, err := Client.GetImageHandle(imageName) + if err != nil { + return err + } + err = Client.CopyImage(imageHandle, imageNewName) + if err != nil { + return err + } + copiedImage, err := Client.GetImage(imageNewName, false, false) + if err != nil { + return err + } + // Update image in-memory + err = updateImageFromFlags(cmd, copiedImage) + if err != nil { + return err + } + return Client.UpdateImage(copiedImage) }, } @@ -47,11 +272,24 @@ var imageEditCmd = &cobra.Command{ Use: "edit", Short: "edit image", Long: `Edits a image.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + imageName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + + imageToEdit, err := Client.GetImage(imageName, false, false) + if err != nil { + return err + } + // Update image in-memory + err = updateImageFromFlags(cmd, imageToEdit) + if err != nil { + return err + } + return Client.UpdateImage(imageToEdit) }, } @@ -59,11 +297,9 @@ var imageFindCmd = &cobra.Command{ Use: "find", Short: "find image", Long: `Finds a given image.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return FindItemNames(cmd, args, "image") }, } @@ -73,9 +309,11 @@ var imageListCmd = &cobra.Command{ Long: `Lists all available images.`, Run: func(cmd *cobra.Command, args []string) { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + imageNames, err := Client.ListImageNames() + if err != nil { + fmt.Println(err) + } + listItems("images", imageNames) }, } @@ -83,11 +321,9 @@ var imageRemoveCmd = &cobra.Command{ Use: "remove", Short: "remove image", Long: `Removes a given image.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return RemoveItemRecursive(cmd, args, "image") }, } @@ -95,23 +331,71 @@ var imageRenameCmd = &cobra.Command{ Use: "rename", Short: "rename image", Long: `Renames a given image.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + imageName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + imageNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + + imageHandle, err := Client.GetImageHandle(imageName) + if err != nil { + return err + } + err = Client.RenameImage(imageHandle, imageNewName) + if err != nil { + return err + } + renamedImage, err := Client.GetImage(imageNewName, false, false) + if err != nil { + return err + } + // Update image in-memory + err = updateImageFromFlags(cmd, renamedImage) + if err != nil { + return err + } + return Client.UpdateImage(renamedImage) }, } -var imageimagertCmd = &cobra.Command{ - Use: "imagert", +func reportImages(imageNames []string) error { + for _, itemName := range imageNames { + system, err := Client.GetImage(itemName, false, false) + if err != nil { + return err + } + printStructured(system) + fmt.Println("") + } + return nil +} + +var imageReportCmd = &cobra.Command{ + Use: "report", Short: "list all images in detail", Long: `Shows detailed information about all images.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + name, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + itemNames := make([]string, 0) + if name == "" { + itemNames, err = Client.ListImageNames() + if err != nil { + return err + } + } else { + itemNames = append(itemNames, name) + } + return reportImages(itemNames) }, } @@ -124,149 +408,63 @@ func init() { imageCmd.AddCommand(imageListCmd) imageCmd.AddCommand(imageRemoveCmd) imageCmd.AddCommand(imageRenameCmd) - imageCmd.AddCommand(imageimagertCmd) + imageCmd.AddCommand(imageReportCmd) // local flags for image add - imageAddCmd.Flags().String("name", "", "the image name") - imageAddCmd.Flags().String("ctime", "", "") - imageAddCmd.Flags().String("depth", "", "") - imageAddCmd.Flags().String("mtime", "", "") - imageAddCmd.Flags().String("uid", "", "UID") - imageAddCmd.Flags().String("arch", "", "Architecture") - imageAddCmd.Flags().String("breed", "", "Breed (valid options: none,rsync,rhn,yum,apt,wget)") - imageAddCmd.Flags().String("comment", "", "free form text description") - imageAddCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") + addCommonArgs(imageAddCmd) + addStringFlags(imageAddCmd, imageStringFlagMetadata) + addIntFlags(imageAddCmd, imageIntFlagMetadata) + addFloatFlags(imageAddCmd, imageFloatFlagMetadata) + addBoolFlags(imageAddCmd, imageBoolFlagMetadata) + addStringSliceFlags(imageAddCmd, imageStringSliceFlagMetadata) imageAddCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - imageAddCmd.Flags().String("parent", "", "") - imageAddCmd.Flags().String("file", "", "path to local file or nfs://user@host:path") - imageAddCmd.Flags().String("image-type", "", "image type. Valid options: iso,direct,memdisk,virt-image") - imageAddCmd.Flags().String("network-count", "", "") - imageAddCmd.Flags().String("os-version", "", "OS version (needed for some virtualization optimizations)") - imageAddCmd.Flags().String("menu", "", "parent boot menu") - imageAddCmd.Flags().String("boot-loaders", "", "boot loaders (network installation boot loaders)") - imageAddCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - imageAddCmd.Flags().String("virt-bridge", "", "virt bridge") - imageAddCmd.Flags().String("virt-cpus", "", "virt CPUs") - imageAddCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - imageAddCmd.Flags().String("virt-file-size", "", "virt file size in GB") - imageAddCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - imageAddCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - imageAddCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: xenpv,xenfv,qemu,kvm,vmware") // local flags for image copy - imageCopyCmd.Flags().String("name", "", "the image name") + addCommonArgs(imageCopyCmd) + addStringFlags(imageCopyCmd, imageStringFlagMetadata) + addIntFlags(imageCopyCmd, imageIntFlagMetadata) + addFloatFlags(imageCopyCmd, imageFloatFlagMetadata) + addBoolFlags(imageCopyCmd, imageBoolFlagMetadata) + addStringSliceFlags(imageCopyCmd, imageStringSliceFlagMetadata) imageCopyCmd.Flags().String("newname", "", "the new image name") - imageCopyCmd.Flags().String("ctime", "", "") - imageCopyCmd.Flags().String("depth", "", "") - imageCopyCmd.Flags().String("mtime", "", "") - imageCopyCmd.Flags().String("uid", "", "UID") - imageCopyCmd.Flags().String("arch", "", "Architecture") - imageCopyCmd.Flags().String("breed", "", "Breed (valid options: none,rsync,rhn,yum,apt,wget)") - imageCopyCmd.Flags().String("comment", "", "free form text description") - imageCopyCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") imageCopyCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - imageCopyCmd.Flags().String("parent", "", "") - imageCopyCmd.Flags().String("file", "", "path to local file or nfs://user@host:path") - imageCopyCmd.Flags().String("image-type", "", "image type. Valid options: iso,direct,memdisk,virt-image") - imageCopyCmd.Flags().String("network-count", "", "") - imageCopyCmd.Flags().String("os-version", "", "OS version (needed for some virtualization optimizations)") - imageCopyCmd.Flags().String("menu", "", "parent boot menu") - imageCopyCmd.Flags().String("boot-loaders", "", "boot loaders (network installation boot loaders)") - imageCopyCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - imageCopyCmd.Flags().String("virt-bridge", "", "virt bridge") - imageCopyCmd.Flags().String("virt-cpus", "", "virt CPUs") - imageCopyCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - imageCopyCmd.Flags().String("virt-file-size", "", "virt file size in GB") - imageCopyCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - imageCopyCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - imageCopyCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: xenpv,xenfv,qemu,kvm,vmware") // local flags for image edit - imageEditCmd.Flags().String("name", "", "the image name") - imageEditCmd.Flags().String("ctime", "", "") - imageEditCmd.Flags().String("depth", "", "") - imageEditCmd.Flags().String("mtime", "", "") - imageEditCmd.Flags().String("uid", "", "UID") - imageEditCmd.Flags().String("arch", "", "Architecture") - imageEditCmd.Flags().String("breed", "", "Breed (valid options: none,rsync,rhn,yum,apt,wget)") - imageEditCmd.Flags().String("comment", "", "free form text description") - imageEditCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") + addCommonArgs(imageEditCmd) + addStringFlags(imageEditCmd, imageStringFlagMetadata) + addIntFlags(imageEditCmd, imageIntFlagMetadata) + addFloatFlags(imageEditCmd, imageFloatFlagMetadata) + addBoolFlags(imageEditCmd, imageBoolFlagMetadata) + addStringSliceFlags(imageEditCmd, imageStringSliceFlagMetadata) imageEditCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - imageEditCmd.Flags().String("parent", "", "") - imageEditCmd.Flags().String("file", "", "path to local file or nfs://user@host:path") - imageEditCmd.Flags().String("image-type", "", "image type. Valid options: iso,direct,memdisk,virt-image") - imageEditCmd.Flags().String("network-count", "", "") - imageEditCmd.Flags().String("os-version", "", "OS version (needed for some virtualization optimizations)") - imageEditCmd.Flags().String("menu", "", "parent boot menu") - imageEditCmd.Flags().String("boot-loaders", "", "boot loaders (network installation boot loaders)") - imageEditCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - imageEditCmd.Flags().String("virt-bridge", "", "virt bridge") - imageEditCmd.Flags().String("virt-cpus", "", "virt CPUs") - imageEditCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - imageEditCmd.Flags().String("virt-file-size", "", "virt file size in GB") - imageEditCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - imageEditCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - imageEditCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: xenpv,xenfv,qemu,kvm,vmware") // local flags for image find - imageFindCmd.Flags().String("name", "", "the image name") + addCommonArgs(imageFindCmd) + addStringFlags(imageFindCmd, imageStringFlagMetadata) + addIntFlags(imageFindCmd, imageIntFlagMetadata) + addFloatFlags(imageFindCmd, imageFloatFlagMetadata) + addBoolFlags(imageFindCmd, imageBoolFlagMetadata) + addStringSliceFlags(imageFindCmd, imageStringSliceFlagMetadata) imageFindCmd.Flags().String("ctime", "", "") imageFindCmd.Flags().String("depth", "", "") imageFindCmd.Flags().String("mtime", "", "") imageFindCmd.Flags().String("uid", "", "UID") - imageFindCmd.Flags().String("arch", "", "Architecture") - imageFindCmd.Flags().String("breed", "", "Breed (valid options: none,rsync,rhn,yum,apt,wget)") - imageFindCmd.Flags().String("comment", "", "free form text description") - imageFindCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") imageFindCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - imageFindCmd.Flags().String("parent", "", "") - imageFindCmd.Flags().String("file", "", "path to local file or nfs://user@host:path") - imageFindCmd.Flags().String("image-type", "", "image type. Valid options: iso,direct,memdisk,virt-image") - imageFindCmd.Flags().String("network-count", "", "") - imageFindCmd.Flags().String("os-version", "", "OS version (needed for some virtualization optimizations)") - imageFindCmd.Flags().String("menu", "", "parent boot menu") - imageFindCmd.Flags().String("boot-loaders", "", "boot loaders (network installation boot loaders)") - imageFindCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - imageFindCmd.Flags().String("virt-bridge", "", "virt bridge") - imageFindCmd.Flags().String("virt-cpus", "", "virt CPUs") - imageFindCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - imageFindCmd.Flags().String("virt-file-size", "", "virt file size in GB") - imageFindCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - imageFindCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - imageFindCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: xenpv,xenfv,qemu,kvm,vmware") // local flags for image remove imageRemoveCmd.Flags().String("name", "", "the image name") imageRemoveCmd.Flags().Bool("recursive", false, "also delete child objects") // local flags for image rename - imageRenameCmd.Flags().String("name", "", "the image name") + addCommonArgs(imageRenameCmd) + addStringFlags(imageRenameCmd, imageStringFlagMetadata) + addIntFlags(imageRenameCmd, imageIntFlagMetadata) + addFloatFlags(imageRenameCmd, imageFloatFlagMetadata) + addBoolFlags(imageRenameCmd, imageBoolFlagMetadata) + addStringSliceFlags(imageRenameCmd, imageStringSliceFlagMetadata) imageRenameCmd.Flags().String("newname", "", "the new image name") - imageRenameCmd.Flags().String("ctime", "", "") - imageRenameCmd.Flags().String("depth", "", "") - imageRenameCmd.Flags().String("mtime", "", "") - imageRenameCmd.Flags().String("uid", "", "UID") - imageRenameCmd.Flags().String("arch", "", "Architecture") - imageRenameCmd.Flags().String("breed", "", "Breed (valid options: none,rsync,rhn,yum,apt,wget)") - imageRenameCmd.Flags().String("comment", "", "free form text description") - imageRenameCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") imageRenameCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - imageRenameCmd.Flags().String("parent", "", "") - imageRenameCmd.Flags().String("file", "", "path to local file or nfs://user@host:path") - imageRenameCmd.Flags().String("image-type", "", "image type. Valid options: iso,direct,memdisk,virt-image") - imageRenameCmd.Flags().String("network-count", "", "") - imageRenameCmd.Flags().String("os-version", "", "OS version (needed for some virtualization optimizations)") - imageRenameCmd.Flags().String("menu", "", "parent boot menu") - imageRenameCmd.Flags().String("boot-loaders", "", "boot loaders (network installation boot loaders)") - imageRenameCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - imageRenameCmd.Flags().String("virt-bridge", "", "virt bridge") - imageRenameCmd.Flags().String("virt-cpus", "", "virt CPUs") - imageRenameCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - imageRenameCmd.Flags().String("virt-file-size", "", "virt file size in GB") - imageRenameCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - imageRenameCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - imageRenameCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: xenpv,xenfv,qemu,kvm,vmware") - // local flags for image imagert - imageimagertCmd.Flags().String("name", "", "the image name") + // local flags for image report + imageReportCmd.Flags().String("name", "", "the image name") } diff --git a/cmd/import.go b/cmd/import.go index 3e81f68..0cd5c16 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -5,6 +5,8 @@ package cmd import ( + "fmt" + "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" ) @@ -15,9 +17,56 @@ var importCmd = &cobra.Command{ Long: `Import operating system distributions into Cobbler. This could be a mounted ISO, network rsync mirror or a tree in the filesystem. See https://cobbler.readthedocs.io/en/latest/quickstart-guide.html#importing-your-first-distribution for more information.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient + archOption, err := cmd.Flags().GetString("arch") + if err != nil { + return err + } + autoinstallOption, err := cmd.Flags().GetString("autoinstall") + if err != nil { + return err + } + availableAsOption, err := cmd.Flags().GetString("available-as") + if err != nil { + return err + } + breedOption, err := cmd.Flags().GetString("breed") + if err != nil { + return err + } + nameOption, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + osVersionOption, err := cmd.Flags().GetString("os-version") + if err != nil { + return err + } + pathOption, err := cmd.Flags().GetString("path") + if err != nil { + return err + } + rsyncFlagsOption, err := cmd.Flags().GetString("rsync-flags") + if err != nil { + return err + } + var backgroundOptions = cobblerclient.BackgroundImportOptions{ + Path: pathOption, + Name: nameOption, + AvailableAs: availableAsOption, + AutoinstallFile: autoinstallOption, + RsyncFlags: rsyncFlagsOption, + Arch: archOption, + Breed: breedOption, + OsVersion: osVersionOption, + } + eventId, err := Client.BackgroundImport(backgroundOptions) + if err != nil { + return err + } + fmt.Printf("Event ID: %s\n", eventId) + return nil }, } diff --git a/cmd/item.go b/cmd/item.go new file mode 100644 index 0000000..8e18171 --- /dev/null +++ b/cmd/item.go @@ -0,0 +1,140 @@ +package cmd + +import ( + "fmt" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + "strings" +) + +var inheritedUsageFormat = "Mark %s as inherited and remove its concrete value" + +func addStringFlags(command *cobra.Command, metadata map[string]FlagMetadata[string]) { + for _, value := range metadata { + command.Flags().String(value.Name, value.DefaultValue, value.Usage) + } +} + +func addBoolFlags(command *cobra.Command, metadata map[string]FlagMetadata[bool]) { + for _, value := range metadata { + if value.IsInheritable { + var inheritFlagName = value.Name + "-inherit" + command.Flags().Bool(value.Name, value.DefaultValue, value.Usage) + command.Flags().Bool( + inheritFlagName, + false, + fmt.Sprintf(inheritedUsageFormat, value.Name), + ) + } else { + command.Flags().Bool(value.Name, value.DefaultValue, value.Usage) + } + } +} + +func addIntFlags(command *cobra.Command, metadata map[string]FlagMetadata[int]) { + for _, value := range metadata { + if value.IsInheritable { + var inheritFlagName = value.Name + "-inherit" + command.Flags().Int(value.Name, value.DefaultValue, value.Usage) + command.Flags().Bool( + inheritFlagName, + false, + fmt.Sprintf(inheritedUsageFormat, value.Name), + ) + } else { + command.Flags().Int(value.Name, value.DefaultValue, value.Usage) + } + } +} + +func addFloatFlags(command *cobra.Command, metadata map[string]FlagMetadata[float64]) { + for _, value := range metadata { + if value.IsInheritable { + var inheritFlagName = value.Name + "-inherit" + command.Flags().Float64(value.Name, value.DefaultValue, value.Usage) + command.Flags().Bool( + inheritFlagName, + false, + fmt.Sprintf(inheritedUsageFormat, value.Name), + ) + } else { + command.Flags().Float64(value.Name, value.DefaultValue, value.Usage) + } + } +} + +func addStringSliceFlags(command *cobra.Command, metadata map[string]FlagMetadata[[]string]) { + for _, value := range metadata { + if value.IsInheritable { + var inheritedFlagName = value.Name + "-inherit" + command.Flags().StringSlice(value.Name, value.DefaultValue, value.Usage) + command.Flags().Bool( + inheritedFlagName, + false, + fmt.Sprintf(inheritedUsageFormat, value.Name), + ) + command.MarkFlagsMutuallyExclusive(value.Name, inheritedFlagName) + } else { + command.Flags().StringSlice(value.Name, value.DefaultValue, value.Usage) + } + } +} + +func addMapFlags(command *cobra.Command, metadata map[string]FlagMetadata[map[string]string]) { + for _, value := range metadata { + if value.IsInheritable { + var inheritedFlagName = value.Name + "-inherit" + command.Flags().StringToString(value.Name, value.DefaultValue, value.Usage) + command.Flags().Bool( + inheritedFlagName, + false, + fmt.Sprintf(inheritedUsageFormat, value.Name), + ) + command.MarkFlagsMutuallyExclusive(value.Name, inheritedFlagName) + } else { + command.Flags().StringToString(value.Name, value.DefaultValue, value.Usage) + } + } +} + +func addCommonArgs(command *cobra.Command) { + addStringFlags(command, commonStringFlagMetadata) + addStringSliceFlags(command, commonStringSliceFlagMetadata) +} + +// RemoveItemRecursive accesses the given flags and attempts to remove a given item +func RemoveItemRecursive(cmd *cobra.Command, args []string, what string) error { + _ = args + itemName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + recursiveDelete, err := cmd.Flags().GetBool("recursive") + if err != nil { + return err + } + return Client.RemoveItem(what, itemName, recursiveDelete) +} + +// FindItemNames accesses the given flags and attempts to perform a search for the given item type +func FindItemNames(cmd *cobra.Command, args []string, what string) error { + _ = args + criteria := make(map[string]interface{}) + cmd.Flags().Visit(func(flag *pflag.Flag) { + if flag.Name == "config" { + return + } + key := strings.Replace(flag.Name, "-", "_", -1) + criteria[key] = flag.Value.String() + }) + + // Now perform the actual search + itemNames, err := Client.FindItemNames(what, criteria, "name") + if err != nil { + return err + } + for _, distroName := range itemNames { + fmt.Println(distroName) + } + return nil +} diff --git a/cmd/list.go b/cmd/list.go index c8b0aae..42e3fbc 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -5,7 +5,9 @@ package cmd import ( + "fmt" "github.com/spf13/cobra" + "sort" ) // listCmd represents the list command @@ -16,12 +18,66 @@ var listCmd = &cobra.Command{ most of the other Cobbler commands (currently: distro, profile, system, repo, image, mgmtclass, package, file, menu). Identical to 'cobbler report'`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient + distroNames, err := Client.ListDistroNames() + if err != nil { + return err + } + profileNames, err := Client.ListProfileNames() + if err != nil { + return err + } + systemNames, err := Client.ListSystemNames() + if err != nil { + return err + } + repoNames, err := Client.ListRepoNames() + if err != nil { + return err + } + imageNames, err := Client.ListImageNames() + if err != nil { + return err + } + mgmtClassNames, err := Client.ListMgmtClassNames() + if err != nil { + return err + } + packageNames, err := Client.ListPackageNames() + if err != nil { + return err + } + fileNames, err := Client.ListFileNames() + if err != nil { + return err + } + menuNames, err := Client.ListMenuNames() + if err != nil { + return err + } + listItems("distros", distroNames) + listItems("profiles", profileNames) + listItems("systems", systemNames) + listItems("repos", repoNames) + listItems("images", imageNames) + listItems("mgmtclasses", mgmtClassNames) + listItems("packages", packageNames) + listItems("files", fileNames) + listItems("menus", menuNames) + return nil }, } +func listItems(what string, items []string) { + fmt.Printf("%s:\n", what) + sort.Strings(items) + for _, item := range items { + fmt.Printf(" %s\n", item) + } + fmt.Println() +} + func init() { rootCmd.AddCommand(listCmd) } diff --git a/cmd/menu.go b/cmd/menu.go index a7ed30b..eceb588 100644 --- a/cmd/menu.go +++ b/cmd/menu.go @@ -5,9 +5,60 @@ package cmd import ( + "fmt" + cobbler "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) +func updateMenuFromFlags(cmd *cobra.Command, menu *cobbler.Menu) error { + // TODO: in-place flag + // var inPlace bool + var err error + if cmd.Flags().Lookup("in-place") != nil { + // inPlace, err := cmd.Flags().GetBool("in-place") + _, err = cmd.Flags().GetBool("in-place") + if err != nil { + return err + } + } + cmd.Flags().Visit(func(flag *pflag.Flag) { + if err != nil { + // If one of the previous flags has had an error just directly return. + return + } + switch flag.Name { + case "name": + return + case "newname": + return + case "comment": + var menuNewComment string + menuNewComment, err = cmd.Flags().GetString("comment") + if err != nil { + return + } + menu.Comment = menuNewComment + case "parent": + var menuNewParent string + menuNewParent, err = cmd.Flags().GetString("parent") + if err != nil { + return + } + menu.Parent = menuNewParent + case "display-name": + var menuNewDisplayName string + menuNewDisplayName, err = cmd.Flags().GetString("display-name") + if err != nil { + return + } + menu.DisplayName = menuNewDisplayName + } + }) + // Don't blindly return nil because maybe one of the flags had an issue retrieving an argument. + return err +} + // menuCmd represents the menu command var menuCmd = &cobra.Command{ Use: "menu", @@ -15,7 +66,7 @@ var menuCmd = &cobra.Command{ Long: `Let you manage menus. See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-menu for more information.`, Run: func(cmd *cobra.Command, args []string) { - cmd.Help() + _ = cmd.Help() }, } @@ -23,11 +74,28 @@ var menuAddCmd = &cobra.Command{ Use: "add", Short: "add menu", Long: `Adds a menu.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + newMenu := cobbler.NewMenu() + var err error - // TODO: call cobblerclient - notImplemented() + // internal fields (ctime, mtime, depth, uid, source-repos, tree-build-time) cannot be modified + newMenu.Name, err = cmd.Flags().GetString("name") + if err != nil { + return err + } + // Update menu in-memory + err = updateMenuFromFlags(cmd, &newMenu) + if err != nil { + return err + } + // Now create the menu via XML-RPC + menu, err := Client.CreateMenu(newMenu) + if err != nil { + return err + } + fmt.Printf("Menu %s created\n", menu.Name) + return nil }, } @@ -35,11 +103,34 @@ var menuCopyCmd = &cobra.Command{ Use: "copy", Short: "copy menu", Long: `Copies a menu.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + menuName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + menuNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } - // TODO: call cobblerclient - notImplemented() + menuHandle, err := Client.GetMenuHandle(menuName) + if err != nil { + return err + } + err = Client.CopyMenu(menuHandle, menuNewName) + if err != nil { + return err + } + newMenu, err := Client.GetMenu(menuNewName, false, false) + if err != nil { + return err + } + err = updateMenuFromFlags(cmd, newMenu) + if err != nil { + return err + } + return Client.UpdateMenu(newMenu) }, } @@ -47,11 +138,22 @@ var menuEditCmd = &cobra.Command{ Use: "edit", Short: "edit menu", Long: `Edits a menu.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + menuName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } - // TODO: call cobblerclient - notImplemented() + menuToEdit, err := Client.GetMenu(menuName, false, false) + if err != nil { + return err + } + err = updateMenuFromFlags(cmd, menuToEdit) + if err != nil { + return err + } + return Client.UpdateMenu(menuToEdit) }, } @@ -59,11 +161,9 @@ var menuFindCmd = &cobra.Command{ Use: "find", Short: "find menu", Long: `Finds a given menu.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return FindItemNames(cmd, args, "menu") }, } @@ -71,11 +171,14 @@ var menuListCmd = &cobra.Command{ Use: "list", Short: "list all menus", Long: `Lists all available menus.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + menuNames, err := Client.ListMenuNames() + if err != nil { + return err + } + listItems("menus", menuNames) + return nil }, } @@ -83,11 +186,9 @@ var menuRemoveCmd = &cobra.Command{ Use: "remove", Short: "remove menu", Long: `Removes a given menu.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return RemoveItemRecursive(cmd, args, "menu") }, } @@ -95,23 +196,69 @@ var menuRenameCmd = &cobra.Command{ Use: "rename", Short: "rename menu", Long: `Renames a given menu.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + menuName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + menuNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } - // TODO: call cobblerclient - notImplemented() + menuHandle, err := Client.GetMenuHandle(menuName) + if err != nil { + return err + } + err = Client.RenameMenu(menuHandle, menuNewName) + if err != nil { + return err + } + newMenu, err := Client.GetMenu(menuNewName, false, false) + if err != nil { + return err + } + err = updateMenuFromFlags(cmd, newMenu) + if err != nil { + return err + } + return Client.UpdateMenu(newMenu) }, } +func reportMenus(menuNames []string) error { + for _, itemName := range menuNames { + menu, err := Client.GetMenu(itemName, false, false) + if err != nil { + return err + } + printStructured(menu) + fmt.Println("") + } + return nil +} + var menuReportCmd = &cobra.Command{ Use: "report", Short: "list all menus in detail", Long: `Shows detailed information about all menus.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + name, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + itemNames := make([]string, 0) + if name == "" { + itemNames, err = Client.ListMenuNames() + if err != nil { + return err + } + } else { + itemNames = append(itemNames, name) + } + return reportMenus(itemNames) }, } @@ -127,63 +274,35 @@ func init() { menuCmd.AddCommand(menuReportCmd) // local flags for menu add - menuAddCmd.Flags().String("name", "", "the menu name") - menuAddCmd.Flags().String("ctime", "", "") - menuAddCmd.Flags().String("depth", "", "") - menuAddCmd.Flags().String("mtime", "", "") - menuAddCmd.Flags().String("uid", "", "") - menuAddCmd.Flags().String("comment", "", "free form text description") - menuAddCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - menuAddCmd.Flags().String("parent", "", "parent menu") - menuAddCmd.Flags().String("display-name", "", "display name") + addCommonArgs(menuAddCmd) + addStringFlags(menuAddCmd, menuStringFlagMetadata) // local flags for menu copy - menuCopyCmd.Flags().String("name", "", "the menu name") - menuCopyCmd.Flags().String("ctime", "", "") - menuCopyCmd.Flags().String("depth", "", "") - menuCopyCmd.Flags().String("mtime", "", "") - menuCopyCmd.Flags().String("uid", "", "") - menuCopyCmd.Flags().String("comment", "", "free form text description") + addCommonArgs(menuCopyCmd) + addStringFlags(menuCopyCmd, menuStringFlagMetadata) menuCopyCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - menuCopyCmd.Flags().String("parent", "", "parent menu") - menuCopyCmd.Flags().String("display-name", "", "display name") // local flags for menu edit - menuEditCmd.Flags().String("name", "", "the menu name") - menuEditCmd.Flags().String("ctime", "", "") - menuEditCmd.Flags().String("depth", "", "") - menuEditCmd.Flags().String("mtime", "", "") - menuEditCmd.Flags().String("uid", "", "") - menuEditCmd.Flags().String("comment", "", "free form text description") + addCommonArgs(menuEditCmd) + addStringFlags(menuEditCmd, menuStringFlagMetadata) menuEditCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - menuEditCmd.Flags().String("parent", "", "parent menu") - menuEditCmd.Flags().String("display-name", "", "display name") // local flags for menu find - menuFindCmd.Flags().String("name", "", "the menu name") + addCommonArgs(menuFindCmd) + addStringFlags(menuFindCmd, menuStringFlagMetadata) menuFindCmd.Flags().String("ctime", "", "") menuFindCmd.Flags().String("depth", "", "") menuFindCmd.Flags().String("mtime", "", "") menuFindCmd.Flags().String("uid", "", "") - menuFindCmd.Flags().String("comment", "", "free form text description") - menuFindCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - menuFindCmd.Flags().String("parent", "", "parent menu") - menuFindCmd.Flags().String("display-name", "", "display name") // local flags for menu remove menuRemoveCmd.Flags().String("name", "", "the menu name") menuRemoveCmd.Flags().Bool("recursive", false, "also delete child objects") // local flags for menu rename - menuRenameCmd.Flags().String("name", "", "the menu name") - menuRenameCmd.Flags().String("ctime", "", "") - menuRenameCmd.Flags().String("depth", "", "") - menuRenameCmd.Flags().String("mtime", "", "") - menuRenameCmd.Flags().String("uid", "", "") - menuRenameCmd.Flags().String("comment", "", "free form text description") + addCommonArgs(menuRenameCmd) + addStringFlags(menuRenameCmd, menuStringFlagMetadata) menuRenameCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - menuRenameCmd.Flags().String("parent", "", "parent menu") - menuRenameCmd.Flags().String("display-name", "", "display name") // local flags for menu report menuReportCmd.Flags().String("name", "", "the menu name") diff --git a/cmd/metadata.go b/cmd/metadata.go new file mode 100644 index 0000000..6ab554c --- /dev/null +++ b/cmd/metadata.go @@ -0,0 +1,948 @@ +package cmd + +type FlagMetadata[T any] struct { + Name string + DefaultValue T + Usage string + IsInheritable bool +} + +var commonStringFlagMetadata = map[string]FlagMetadata[string]{ + "name": { + Name: "name", + DefaultValue: "", + Usage: "the item name", + }, + "comment": { + Name: "comment", + DefaultValue: "", + Usage: "free form text description", + }, +} + +var commonStringSliceFlagMetadata = map[string]FlagMetadata[[]string]{ + "owners": { + Name: "owners", + DefaultValue: []string{}, + Usage: "owners list for authorization.ownership (comma delimited)", + IsInheritable: true, + }, +} + +var distroStringFlagMetadata = map[string]FlagMetadata[string]{ + "kernel": { + Name: "kernel", + DefaultValue: "", + Usage: "Kernel (absolute path on filesystem)", + }, + "initrd": { + Name: "initrd", + DefaultValue: "", + Usage: "Initrd (absolute path on filesystem)", + }, + "arch": { + Name: "arch", + DefaultValue: "x86_64", + Usage: "Architecture", + }, + "breed": { + Name: "breed", + DefaultValue: "", + Usage: "Breed (what is the type of the distribution?)", + }, + "os-version": { + Name: "os-version", + DefaultValue: "", + Usage: "OS version (needed for some virtualization optimizations)", + }, + "remote-boot-kernel": { + Name: "remote-boot-kernel", + DefaultValue: "", + Usage: "remote boot kernel (URL the bootloader directly retrieves and boots from)", + }, + "remote-boot-initrd": { + Name: "remote-boot-initrd", + DefaultValue: "", + Usage: "remote boot initrd (URL the bootloader directly retrieves and boots from)", + }, + "redhat-management-key": { + Name: "redhat-management-key", + DefaultValue: "", + Usage: "RedHat management key (registration key for RHN, Spacewalk, or Satellite)", + }, +} + +var distroStringSliceFlagMetadata = map[string]FlagMetadata[[]string]{ + "boot-loaders": { + Name: "boot-loaders", + DefaultValue: []string{}, + Usage: "boot loaders (network installation boot loaders)", + IsInheritable: true, + }, + "mgmt-classes": { + Name: "mgmt-classes", + DefaultValue: []string{}, + Usage: "management classes (for external config management)", + }, +} + +var distroMapFlagMetadata = map[string]FlagMetadata[map[string]string]{ + "autoinstall-meta": { + Name: "autoinstall-meta", + DefaultValue: map[string]string{}, + Usage: "automatic installation template metadata", + IsInheritable: true, + }, + "boot-files": { + Name: "boot-files", + DefaultValue: map[string]string{}, + Usage: "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)", + IsInheritable: true, + }, + "kernel-options": { + Name: "kernel-options", + DefaultValue: map[string]string{}, + Usage: "kernel options (e.g. selinux=permissive)", + IsInheritable: true, + }, + "kernel-options-post": { + Name: "kernel-options-post", + DefaultValue: map[string]string{}, + Usage: "post install kernel options (e.g. clocksource=pit noapic)", + IsInheritable: true, + }, + "fetchable-files": { + Name: "fetchable-files", + DefaultValue: map[string]string{}, + Usage: "fetchable files (templates for tftp, wget or curl)", + IsInheritable: true, + }, + "template-files": { + Name: "template-files", + DefaultValue: map[string]string{}, + Usage: "template files (file mappings for built-in config management)", + IsInheritable: true, + }, +} + +var profileStringFlagMetadata = map[string]FlagMetadata[string]{ + "autoinstall": { + Name: "autoinstall", + DefaultValue: "", + Usage: "path to automatic installation template", + }, + "distro": { + Name: "distro", + DefaultValue: "", + Usage: "the name of a previously defined Cobbler distribution. This value is required", + }, + "redhat-management-key": { + Name: "redhat-management-key", + DefaultValue: "", + Usage: "RedHat management key (registration key for RHN, Spacewalk, or Satellite)", + }, + "dhcp-tag": { + Name: "dhcp-tag", + DefaultValue: "", + Usage: "DHCP tag (see manpage or leave blank)", + }, + "next-server-v4": { + Name: "next-server-v4", + DefaultValue: "", + Usage: "next server (IPv4) override (see manpage or leave blank)", + }, + "next-server-v6": { + Name: "next-server-v6", + DefaultValue: "", + Usage: "next server (IPv6) override (see manpage or leave blank)", + }, + "filename": { + Name: "filename", + DefaultValue: "", + Usage: "DHCP filename override (used to boot non-default bootloaders)", + }, + "parent": { + Name: "parent", + DefaultValue: "", + Usage: "parent profile", + }, + "proxy": { + Name: "proxy", + DefaultValue: "", + Usage: "proxy server URL", + }, + "server": { + Name: "server", + DefaultValue: "", + Usage: "server override", + }, + "menu": { + Name: "menu", + DefaultValue: "", + Usage: "parent boot menu", + }, + "virt-bridge": { + Name: "virt-bridge", + DefaultValue: "", + Usage: "virt bridge", + }, + "virt-disk-driver": { + Name: "virt-disk-driver", + DefaultValue: "", + Usage: "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk", + }, + "virt-path": { + Name: "virt-path", + DefaultValue: "", + Usage: "virt Path (e.g. /directory or VolGroup00)", + }, + "virt-type": { + Name: "virt-type", + DefaultValue: "", + Usage: "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)", + }, +} + +var profileBoolFlagMetadata = map[string]FlagMetadata[bool]{ + "enable-ipxe": { + Name: "enable-ipxe", + DefaultValue: false, + Usage: "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)", + }, + "enable-menu": { + Name: "enable-menu", + DefaultValue: false, + Usage: "enable PXE Menu? (show this profile in the PXE menu?)", + }, + "virt-auto-boot": { + Name: "virt-auto-boot", + DefaultValue: false, + Usage: "auto boot this VM?", + }, +} + +var profileIntFlagMetadata = map[string]FlagMetadata[int]{ + "virt-cpus": { + Name: "virt-cpus", + DefaultValue: 0, + Usage: "virt CPUs", + }, + "virt-ram": { + Name: "virt-ram", + DefaultValue: 0, + Usage: "virt RAM size in MB", + }, +} + +var profileFloatFlagMetadata = map[string]FlagMetadata[float64]{ + "virt-file-size": { + Name: "virt-file-size", + DefaultValue: float64(0), + Usage: "virt file size in GB", + }, +} + +var profileStringSliceFlagMetadata = map[string]FlagMetadata[[]string]{ + "repos": { + Name: "repos", + DefaultValue: []string{}, + Usage: "repos to auto-assign to this profile", + }, + "name-servers": { + Name: "name-servers", + DefaultValue: []string{}, + Usage: "name servers (comma delimited)", + IsInheritable: true, + }, + "name-servers-search": { + Name: "name-servers-search", + DefaultValue: []string{}, + Usage: "name servers search path (comma delimited)", + IsInheritable: true, + }, +} + +var profileMapFlagMetadata = map[string]FlagMetadata[map[string]string]{ + "mgmt-parameters": { + Name: "mgmt-parameters", + DefaultValue: map[string]string{}, + Usage: "Parameters which will be handed to your management application (must be a valid YAML dictionary))", + }, +} + +var systemStringFlagMetadata = map[string]FlagMetadata[string]{ + "autoinstall": { + Name: "autoinstall", + DefaultValue: "", + Usage: "path to automatic installation template", + }, + "redhat-management-key": { + Name: "redhat-management-key", + DefaultValue: "<>", + Usage: "RedHat management key (registration key for RHN, Spacewalk, or Satellite)", + }, + "next-server-v4": { + Name: "next-server-v4", + DefaultValue: "", + Usage: "next server (IPv4) override (see manpage or leave blank)", + }, + "next-server-v6": { + Name: "next-server-v6", + DefaultValue: "", + Usage: "next server (IPv6) override (see manpage or leave blank)", + }, + "filename": { + Name: "filename", + DefaultValue: "", + Usage: "DHCP filename override (used to boot non-default bootloaders)", + }, + "parent": { + Name: "parent", + DefaultValue: "", + Usage: "parent profile", + }, + "proxy": { + Name: "proxy", + DefaultValue: "", + Usage: "proxy server URL", + }, + "server": { + Name: "server", + DefaultValue: "", + Usage: "server override", + }, + "menu": { + Name: "menu", + DefaultValue: "", + Usage: "parent boot menu", + }, + "virt-bridge": { + Name: "virt-bridge", + DefaultValue: "", + Usage: "virt bridge", + }, + "virt-disk-driver": { + Name: "virt-disk-driver", + DefaultValue: "", + Usage: "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk", + }, + "virt-path": { + Name: "virt-path", + DefaultValue: "", + Usage: "virt Path (e.g. /directory or VolGroup00)", + }, + "virt-type": { + Name: "virt-type", + DefaultValue: "", + Usage: "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)", + }, + "gateway": { + Name: "gateway", + DefaultValue: "", + Usage: "gateway", + }, + "hostname": { + Name: "hostname", + DefaultValue: "", + Usage: "hostname", + }, + "image": { + Name: "image", + DefaultValue: "", + Usage: "parent image (if not a profile)", + }, + "ipv6-default-device": { + Name: "ipv6-default-device", + DefaultValue: "", + Usage: "IPv6 default device", + }, + "profile": { + Name: "profile", + DefaultValue: "", + Usage: "Parent profile", + }, + "status": { + Name: "status", + DefaultValue: "", + Usage: "system status. Valid options: development,testing,acceptance,production", + }, +} + +var systemBoolFlagMetadata = map[string]FlagMetadata[bool]{ + "enable-ipxe": { + Name: "enable-ipxe", + DefaultValue: false, + Usage: "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)", + IsInheritable: true, + }, + "enable-menu": { + Name: "enable-menu", + DefaultValue: false, + Usage: "enable PXE Menu? (show this profile in the PXE menu?)", + IsInheritable: true, + }, + "virt-auto-boot": { + Name: "virt-auto-boot", + DefaultValue: false, + Usage: "auto boot this VM?", + IsInheritable: true, + }, + "netboot-enabled": { + Name: "netboot-enabled", + DefaultValue: false, + Usage: "PXE (re)install this machine at next boot?", + }, + "virt-pxe-boot": { + Name: "virt-pxe-boot", + DefaultValue: false, + Usage: "use PXE to build this VM?", + }, +} + +var systemIntFlagMetadata = map[string]FlagMetadata[int]{ + "virt-cpus": { + Name: "virt-cpus", + DefaultValue: 0, + Usage: "virt CPUs", + }, + "virt-ram": { + Name: "virt-ram", + DefaultValue: 0, + Usage: "virt RAM size in MB", + }, + "serial-device": { + Name: "serial-device", + DefaultValue: 0, + Usage: "serial device number", + }, + "serial-baud-rate": { + Name: "serial-baud-rate", + DefaultValue: 0, + Usage: "serial Baud Rate. Valid options: 2400,4800,9600,19200,38400,57600,115200", + }, +} + +var systemFloatFlagMetadata = map[string]FlagMetadata[float64]{ + "virt-file-size": { + Name: "virt-file-size", + DefaultValue: float64(0), + Usage: "virt file size in GB", + }, +} + +var systemStringSliceFlagMetadata = map[string]FlagMetadata[[]string]{ + "boot-loaders": { + Name: "boot-loaders", + DefaultValue: []string{}, + Usage: "boot loaders (network installation boot loaders)", + IsInheritable: true, + }, + "mgmt-classes": { + Name: "mgmt-classes", + DefaultValue: []string{}, + Usage: "management classes (for external config management)", + }, + "name-servers": { + Name: "name-servers", + DefaultValue: []string{}, + Usage: "name servers (comma delimited)", + }, + "name-servers-search": { + Name: "name-servers-search", + DefaultValue: []string{}, + Usage: "name servers search path (comma delimited)", + }, +} + +var systemMapFlagMetadata = map[string]FlagMetadata[map[string]string]{ + "autoinstall-meta": { + Name: "autoinstall-meta", + DefaultValue: map[string]string{}, + Usage: "automatic installation template metadata", + IsInheritable: true, + }, + "boot-files": { + Name: "boot-files", + DefaultValue: map[string]string{}, + Usage: "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)", + IsInheritable: true, + }, + "fetchable-files": { + Name: "fetchable-files", + DefaultValue: map[string]string{}, + Usage: "fetchable files (templates for tftp, wget or curl)", + IsInheritable: true, + }, + "kernel-options": { + Name: "kernel-options", + DefaultValue: map[string]string{}, + Usage: "kernel options (e.g. selinux=permissive)", + IsInheritable: true, + }, + "kernel-options-post": { + Name: "kernel-options-post", + DefaultValue: map[string]string{}, + Usage: "post install kernel options (e.g. clocksource=pit noapic)", + IsInheritable: true, + }, + "template-files": { + Name: "template-files", + DefaultValue: map[string]string{}, + Usage: "template files (file mappings for built-in config management)", + IsInheritable: true, + }, + "mgmt-parameters": { + Name: "mgmt-parameters", + DefaultValue: map[string]string{}, + Usage: "Parameters which will be handed to your management application (must be a valid YAML dictionary))", + }, +} + +var systemPowerStringFlagMetadata = map[string]FlagMetadata[string]{ + "power-address": { + Name: "power-address", + DefaultValue: "", + Usage: "power management address (e.g. power-device.example.org)", + }, + "power-id": { + Name: "power-id", + DefaultValue: "", + Usage: "power management ID (usually a plug number or blade name, if power type requires it)", + }, + "power-pass": { + Name: "power-pass", + DefaultValue: "", + Usage: "power management password", + }, + "power-type": { + Name: "power-type", + DefaultValue: "", + Usage: "power management script to use", + }, + "power-user": { + Name: "power-user", + DefaultValue: "", + Usage: "power management username", + }, + "power-options": { + Name: "power-options", + DefaultValue: "", + Usage: "additional options, to be passed to the fencing agent", + }, + "power-identity-file": { + Name: "power-identity-file", + DefaultValue: "", + Usage: "identity file to be passed to the fencing agent (SSH key)", + }, +} + +var interfaceStringFlagMetadata = map[string]FlagMetadata[string]{ + "bonding-opts": { + Name: "bonding-opts", + DefaultValue: "", + Usage: "bonding opts (should be used with --interface)", + }, + "bridge-opts": { + Name: "bridge-opts", + DefaultValue: "", + Usage: "bridge opts (should be used with --interface)", + }, + "dhcp-tag": { + Name: "dhcp-tag", + DefaultValue: "", + Usage: "DHCP tag (see manpage or leave blank)", + }, + "dns-name": { + Name: "dns-name", + DefaultValue: "", + Usage: "DNS name (should be used with --interface)", + }, + "if-gateway": { + Name: "if-gateway", + DefaultValue: "", + Usage: "per-Interface Gateway (should be used with --interface)", + }, + "interface-master": { + Name: "interface-master", + DefaultValue: "", + Usage: "master interface (Should be used with --interface)", + }, + "interface-type": { + Name: "interface-type", + DefaultValue: "", + Usage: `interface Type. Valid options: na,bond,bond_slave,bridge,bridge_slave,bonded_bridge_slave,bmc,infiniband. + (should be used with --interface)`, + }, + "ip-address": { + Name: "ip-address", + DefaultValue: "", + Usage: "IPv4 address (should be used with --interface)", + }, + "ipv6-address": { + Name: "ipv6-address", + DefaultValue: "", + Usage: "IPv6 address (should be used with --interface)", + }, + "ipv6-default-gateway": { + Name: "ipv6-default-gateway", + DefaultValue: "", + Usage: "IPv6 Default Gateway (should be used with --interface)", + }, + "ipv6-mtu": { + Name: "ipv6-mtu", + DefaultValue: "", + Usage: "IPv6 MTU", + }, + "ipv6-prefix": { + Name: "ipv6-prefix", + DefaultValue: "", + Usage: "IPv6 Prefix (should be used with --interface)", + }, + "mac-address": { + Name: "mac-address", + DefaultValue: "", + Usage: "MAC Address (place 'random' in this field for a random MAC Address.)", + }, + "mtu": { + Name: "mtu", + DefaultValue: "", + Usage: "MTU (should be used with --interface)", + }, + "netmask": { + Name: "netmask", + DefaultValue: "", + Usage: "Subnet mask (should be used with --interface)", + }, +} + +var interfaceBoolFlagMetadata = map[string]FlagMetadata[bool]{ + "connected-mode": { + Name: "connected-mode", + DefaultValue: false, + Usage: "InfiniBand connected mode (should be used with --interface)", + }, + "management": { + Name: "management", + DefaultValue: false, + Usage: "declares the interface as management interface (should be used with --interface)", + }, + "static": { + Name: "static", + DefaultValue: false, + Usage: "Is this interface static? (should be used with --interface)", + }, +} + +var interfaceStringSliceFlagMetadata = map[string]FlagMetadata[[]string]{ + "cnames": { + Name: "cnames", + DefaultValue: []string{}, + Usage: "Cannonical Name Records, should be used with --interface (comma delimited)", + }, + "ipv6-secondaries": { + Name: "ipv6-secondaries", + DefaultValue: []string{}, + Usage: "IPv6 Secondaries (should be used with --interface)", + }, + "ipv6-static-routes": { + Name: "ipv6-static-routes", + DefaultValue: []string{}, + Usage: "IPv6 Static Routes (should be used with --interface)", + }, + "static-routes": { + Name: "static-routes", + DefaultValue: []string{}, + Usage: "static routes (should be used with --interface)", + }, +} + +var imageStringFlagMetadata = map[string]FlagMetadata[string]{ + "arch": { + Name: "arch", + DefaultValue: "", + Usage: "Architecture", + }, + "breed": { + Name: "breed", + DefaultValue: "", + Usage: "Breed (valid options: none,rsync,rhn,yum,apt,wget)", + }, + "parent": { + Name: "parent", + DefaultValue: "", + Usage: "parent item", + }, + "file": { + Name: "file", + DefaultValue: "", + Usage: "path to local file or nfs://user@host:path", + }, + "image-type": { + Name: "image-type", + DefaultValue: "", + Usage: "image type. Valid options: iso,direct,memdisk,virt-image", + }, + "os-version": { + Name: "os-version", + DefaultValue: "", + Usage: "OS version (needed for some virtualization optimizations)", + }, + "menu": { + Name: "menu", + DefaultValue: "", + Usage: "parent boot menu", + }, + "virt-bridge": { + Name: "virt-bridge", + DefaultValue: "", + Usage: "virt bridge", + }, + "virt-disk-driver": { + Name: "virt-disk-driver", + DefaultValue: "<>", + Usage: "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk", + }, + "virt-path": { + Name: "virt-path", + DefaultValue: "", + Usage: "virt Path (e.g. /directory or VolGroup00)", + }, + "virt-type": { + Name: "virt-type", + DefaultValue: "", + Usage: "virtualization technology to use. Valid options: xenpv,xenfv,qemu,kvm,vmware", + }, +} + +var imageIntFlagMetadata = map[string]FlagMetadata[int]{ + "network-count": { + Name: "network-count", + DefaultValue: 0, + Usage: "Network Count", + }, + "virt-cpus": { + Name: "virt-cpus", + DefaultValue: 1, + Usage: "virt CPUs", + }, + "virt-ram": { + Name: "virt-ram", + DefaultValue: 0, + Usage: "virt RAM size in MB", + }, +} + +var imageFloatFlagMetadata = map[string]FlagMetadata[float64]{ + "virt-file-size": { + Name: "virt-file-size", + DefaultValue: float64(0), + Usage: "virt file size in GB", + }, +} + +var imageBoolFlagMetadata = map[string]FlagMetadata[bool]{ + "virt-auto-boot": { + Name: "virt-auto-boot", + DefaultValue: false, + Usage: "auto boot this VM?", + }, +} + +var imageStringSliceFlagMetadata = map[string]FlagMetadata[[]string]{ + "boot-loaders": { + Name: "boot-loaders", + DefaultValue: []string{}, + Usage: "boot loaders (network installation boot loaders)", + }, +} + +var menuStringFlagMetadata = map[string]FlagMetadata[string]{ + "parent": { + Name: "parent", + DefaultValue: "", + Usage: "parent menu", + }, + "display-name": { + Name: "display-name", + DefaultValue: "", + Usage: "display name", + }, +} + +var fileStringFlagMetadata = map[string]FlagMetadata[string]{ + "action": { + Name: "action", + DefaultValue: "", + Usage: "create or remove file resource", + }, + "mode": { + Name: "mode", + DefaultValue: "", + Usage: "file modes", + }, + "template": { + Name: "template", + DefaultValue: "", + Usage: "the template for the file", + }, + "path": { + Name: "path", + DefaultValue: "", + Usage: "the path of the file", + }, + "group": { + Name: "group", + DefaultValue: "", + Usage: "file owner group in file system", + }, + "owner": { + Name: "owner", + DefaultValue: "", + Usage: "file owner user in file system", + }, +} + +var fileBoolFlagMetadata = map[string]FlagMetadata[bool]{ + "is-dir": { + Name: "is-dir", + DefaultValue: false, + Usage: "treat file resource as a directory", + }, +} + +var packageStringFlagMetadata = map[string]FlagMetadata[string]{ + "action": { + Name: "action", + DefaultValue: "", + Usage: "install or remove package resource", + }, + "installer": { + Name: "installer", + DefaultValue: "", + Usage: "package manager", + }, + "version": { + Name: "version", + DefaultValue: "", + Usage: "package version", + }, +} + +var mgmtclassStringFlagMetadata = map[string]FlagMetadata[string]{ + "params": { + Name: "params", + DefaultValue: "", + Usage: "list of parameters/variables", + }, + "class-name": { + Name: "class-name", + DefaultValue: "", + Usage: "actual class name (leave blank to use the name field)", + }, +} + +var mgmtclassBoolFlagMetadata = map[string]FlagMetadata[bool]{ + "is-definition": { + Name: "is-definition", + DefaultValue: false, + Usage: "is Definition? Treat this class as a definition (puppet only)", + }, +} + +var mgmtclassStringSliceFlagMetadata = map[string]FlagMetadata[[]string]{ + "files": { + Name: "files", + DefaultValue: []string{}, + Usage: "file resources", + }, + "packages": { + Name: "packages", + DefaultValue: []string{}, + Usage: "package resources", + }, +} + +var repoStringFlagMetadata = map[string]FlagMetadata[string]{ + "arch": { + Name: "arch", + DefaultValue: "none", + Usage: "Architecture", + }, + "breed": { + Name: "breed", + DefaultValue: "none", + Usage: "Breed (valid options: none,rsync,rhn,yum,apt,wget)", + }, + "createrepo-flags": { + Name: "createrepo-flags", + DefaultValue: "", + Usage: "flags to use with createrepo", + }, + "mirror": { + Name: "mirror", + DefaultValue: "", + Usage: "address of yum or rsync repo to mirror", + }, + "mirror-type": { + Name: "mirror-type", + DefaultValue: "", + Usage: "mirror type. Valid options: metalink,mirrorlist,baseurl", + }, + "proxy": { + Name: "proxy", + DefaultValue: "", + Usage: "proxy URL (<> to use proxy_url_ext from settings, blank or <> for no proxy)", + }, +} + +var repoBoolFlagMetadata = map[string]FlagMetadata[bool]{ + "keep-updated": { + Name: "keep-updated", + DefaultValue: false, + Usage: "update this repo on next 'cobbler reposync'?", + }, +} + +var repoIntFlagMetadata = map[string]FlagMetadata[int]{ + "priority": { + Name: "priority", + DefaultValue: 0, + Usage: "value for yum priorities plugin, if installed", + }, +} + +var repoStringSliceFlagMetadata = map[string]FlagMetadata[[]string]{ + "apt-components": { + Name: "apt-components", + DefaultValue: []string{}, + Usage: "APT components (e.g. main restricted universe)", + }, + "apt-dists": { + Name: "apt-dists", + DefaultValue: []string{}, + Usage: "APT dist names (e.g. precise,bullseye,buster)", + }, +} + +var repoMapFlagMetadata = map[string]FlagMetadata[map[string]string]{ + "environment": { + Name: "environment", + DefaultValue: map[string]string{}, + Usage: "environment variables (use these environment variables during commands (key=value, comma delimited)", + }, + "yumopts": { + Name: "yumopts", + DefaultValue: map[string]string{}, + Usage: "options to write to yum config file", + }, + "rsyncopts": { + Name: "rsyncopts", + DefaultValue: map[string]string{}, + Usage: "options to use with rsync repo", + }, + "rpm-list": { + Name: "rpm-list", + DefaultValue: map[string]string{}, + Usage: "mirror just these RPMs (yum only)", + }, +} diff --git a/cmd/mgmtclass.go b/cmd/mgmtclass.go index 8debf6a..4356ec1 100644 --- a/cmd/mgmtclass.go +++ b/cmd/mgmtclass.go @@ -5,9 +5,93 @@ package cmd import ( + "fmt" + cobbler "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) +func updateMgmtClassFromFlags(cmd *cobra.Command, mgmtClass *cobbler.MgmtClass) error { + // TODO: in-place flag + // inPlace, err := cmd.Flags().GetBool("in-place") + _, err := cmd.Flags().GetBool("in-place") + if err != nil { + return err + } + cmd.Flags().Visit(func(flag *pflag.Flag) { + if err != nil { + // If one of the previous flags has had an error just directly return. + return + } + switch flag.Name { + // The rename & copy operations are special operations as such we cannot blindly set this inside here. + // Any rename & copy operation must be handled outside of this method. + case "comment": + var mgmtClassNewComment string + mgmtClassNewComment, err = cmd.Flags().GetString("comment") + if err != nil { + return + } + mgmtClass.Comment = mgmtClassNewComment + case "owners": + fallthrough + case "owners-inherit": + if cmd.Flags().Lookup("owners-inherit").Changed { + mgmtClass.Owners.Data = []string{} + mgmtClass.Owners.IsInherited, err = cmd.Flags().GetBool("owners-inherit") + if err != nil { + return + } + } else { + var mgmtClassNewOwners []string + mgmtClassNewOwners, err = cmd.Flags().GetStringSlice("owners") + if err != nil { + return + } + mgmtClass.Owners.IsInherited = false + mgmtClass.Owners.Data = mgmtClassNewOwners + } + case "files": + var mgmtClassNewFiles []string + mgmtClassNewFiles, err = cmd.Flags().GetStringSlice("files") + if err != nil { + return + } + mgmtClass.Files = mgmtClassNewFiles + case "packages": + var mgmtClassNewPackages []string + mgmtClassNewPackages, err = cmd.Flags().GetStringSlice("packages") + if err != nil { + return + } + mgmtClass.Packages = mgmtClassNewPackages + case "params": + var mgmtClassNewParams map[string]string + mgmtClassNewParams, err = cmd.Flags().GetStringToString("params") + if err != nil { + return + } + mgmtClass.Params = mgmtClassNewParams + case "class-name": + var mgmtClassNewClassName string + mgmtClassNewClassName, err = cmd.Flags().GetString("class-name") + if err != nil { + return + } + mgmtClass.ClassName = mgmtClassNewClassName + case "is-definition": + var mgmtClassNewIsDefinition bool + mgmtClassNewIsDefinition, err = cmd.Flags().GetBool("is-definition") + if err != nil { + return + } + mgmtClass.IsDefiniton = mgmtClassNewIsDefinition + } + }) + // Don't blindly return nil because maybe one of the flags had an issue retrieving an argument. + return err +} + // mgmtclassCmd represents the mgmtclass command var mgmtclassCmd = &cobra.Command{ Use: "mgmtclass", @@ -15,7 +99,7 @@ var mgmtclassCmd = &cobra.Command{ Long: `Let you manage mgmtclasses. See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-mgmtclass for more information.`, Run: func(cmd *cobra.Command, args []string) { - cmd.Help() + _ = cmd.Help() }, } @@ -23,11 +107,28 @@ var mgmtclassAddCmd = &cobra.Command{ Use: "add", Short: "add mgmtclass", Long: `Adds a mgmtclass.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + newMgmtClass := cobbler.NewMgmtClass() + var err error - // TODO: call cobblerclient - notImplemented() + // Get special name flag + newMgmtClass.Name, err = cmd.Flags().GetString("name") + if err != nil { + return err + } + // Update with the rest of the flags + err = updateMgmtClassFromFlags(cmd, &newMgmtClass) + if err != nil { + return err + } + // Now create the file via XML-RPC + mgmtClass, err := Client.CreateMgmtClass(newMgmtClass) + if err != nil { + return err + } + fmt.Printf("Mgmtclass %s created\n", mgmtClass.Name) + return nil }, } @@ -35,11 +136,41 @@ var mgmtclassCopyCmd = &cobra.Command{ Use: "copy", Short: "copy mgmtclass", Long: `Copies a mgmtclass.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get special name and newname flags + mgmtClassName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + mgmtClassNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + + // Get API handle + mgmtClassHandle, err := Client.GetMgmtClassHandle(mgmtClassName) + if err != nil { + return err + } + // Copy the mgmtclass server-side + err = Client.CopyMgmtClass(mgmtClassHandle, mgmtClassNewName) + if err != nil { + return err + } + // Get the copied mgmtclass + newMgmtClass, err := Client.GetMgmtClass(mgmtClassNewName, false, false) + if err != nil { + return err + } + // Update the mgmtclass in-memory + err = updateMgmtClassFromFlags(cmd, newMgmtClass) + if err != nil { + return err + } + // Update the mgmtclass via XML-RPC + return Client.UpdateMgmtClass(newMgmtClass) }, } @@ -47,11 +178,27 @@ var mgmtclassEditCmd = &cobra.Command{ Use: "edit", Short: "edit mgmtclass", Long: `Edits a mgmtclass.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Collect CLI flags + mgmtClassName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + + // Get mgmtclass from the API + mgmtClassToEdit, err := Client.GetMgmtClass(mgmtClassName, false, false) + if err != nil { + return err + } + // Update mgmtclass in-memory + err = updateMgmtClassFromFlags(cmd, mgmtClassToEdit) + if err != nil { + return err + } + // Update the mgmtclass via XML-RPC + return Client.UpdateMgmtClass(mgmtClassToEdit) }, } @@ -59,11 +206,9 @@ var mgmtclassFindCmd = &cobra.Command{ Use: "find", Short: "find mgmtclass", Long: `Finds a given mgmtclass.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return FindItemNames(cmd, args, "mgmtclass") }, } @@ -71,11 +216,14 @@ var mgmtclassListCmd = &cobra.Command{ Use: "list", Short: "list all mgmtclasses", Long: `Lists all available mgmtclasses.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + mgmtclassNames, err := Client.ListMgmtClassNames() + if err != nil { + return err + } + listItems("mgmtclasses", mgmtclassNames) + return nil }, } @@ -83,11 +231,9 @@ var mgmtclassRemoveCmd = &cobra.Command{ Use: "remove", Short: "remove mgmtclass", Long: `Removes a given mgmtclass.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return RemoveItemRecursive(cmd, args, "mgmtclass") }, } @@ -95,23 +241,76 @@ var mgmtclassRenameCmd = &cobra.Command{ Use: "rename", Short: "rename mgmtclass", Long: `Renames a given mgmtclass.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get the special name and newname flags + mgmtClassName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + mgmtClassNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + + // Get the mgmtclass handle + mgmtClassHandle, err := Client.GetMgmtClassHandle(mgmtClassName) + if err != nil { + return err + } + // Rename the mgmtclass server-side + err = Client.RenameMgmtClass(mgmtClassHandle, mgmtClassNewName) + if err != nil { + return err + } + // Get the renamed mgmtclass + renamedMgmtClass, err := Client.GetMgmtClass(mgmtClassNewName, false, false) + if err != nil { + return err + } + // Update mgmtclass in-memory + err = updateMgmtClassFromFlags(cmd, renamedMgmtClass) + if err != nil { + return err + } + // Update the mgmtclass via XML-RPC + return Client.UpdateMgmtClass(renamedMgmtClass) }, } +func reportMgmtClasses(mgmtClassNames []string) error { + for _, itemName := range mgmtClassNames { + system, err := Client.GetMgmtClass(itemName, false, false) + if err != nil { + return err + } + printStructured(system) + fmt.Println("") + } + return nil +} + var mgmtclassReportCmd = &cobra.Command{ Use: "report", Short: "list all mgmtclasses in detail", Long: `Shows detailed information about all mgmtclasses.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + name, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + itemNames := make([]string, 0) + if name == "" { + itemNames, err = Client.ListMgmtClassNames() + if err != nil { + return err + } + } else { + itemNames = append(itemNames, name) + } + return reportMgmtClasses(itemNames) }, } @@ -127,85 +326,45 @@ func init() { mgmtclassCmd.AddCommand(mgmtclassReportCmd) // local flags for mgmtclass add - mgmtclassAddCmd.Flags().String("name", "", "the mgmtclass name") - mgmtclassAddCmd.Flags().String("ctime", "", "") - mgmtclassAddCmd.Flags().String("depth", "", "") - mgmtclassAddCmd.Flags().String("mtime", "", "") - mgmtclassAddCmd.Flags().String("comment", "", "free form text description") - mgmtclassAddCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - mgmtclassAddCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - mgmtclassAddCmd.Flags().String("files", "", "file resources") - mgmtclassAddCmd.Flags().String("packages", "", "package resources") - mgmtclassAddCmd.Flags().String("params", "", "list of parameters/variables") - mgmtclassAddCmd.Flags().String("class-name", "", "actual class name (leave blank to use the name field)") - mgmtclassAddCmd.Flags().String("is-definition", "", "is Definition? Treat this class as a definition (puppet only)") - mgmtclassAddCmd.Flags().String("uid", "", "") + addCommonArgs(mgmtclassAddCmd) + addStringFlags(mgmtclassAddCmd, mgmtclassStringFlagMetadata) + addBoolFlags(mgmtclassAddCmd, mgmtclassBoolFlagMetadata) + addStringSliceFlags(mgmtclassAddCmd, mgmtclassStringSliceFlagMetadata) // local flags for mgmtclass copy - mgmtclassCopyCmd.Flags().String("name", "", "the mgmtclass name") + addCommonArgs(mgmtclassCopyCmd) + addStringFlags(mgmtclassCopyCmd, mgmtclassStringFlagMetadata) + addBoolFlags(mgmtclassCopyCmd, mgmtclassBoolFlagMetadata) + addStringSliceFlags(mgmtclassCopyCmd, mgmtclassStringSliceFlagMetadata) mgmtclassCopyCmd.Flags().String("newname", "", "the new mgmtclass name") - mgmtclassCopyCmd.Flags().String("ctime", "", "") - mgmtclassCopyCmd.Flags().String("depth", "", "") - mgmtclassCopyCmd.Flags().String("mtime", "", "") - mgmtclassCopyCmd.Flags().String("comment", "", "free form text description") - mgmtclassCopyCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") mgmtclassCopyCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - mgmtclassCopyCmd.Flags().String("files", "", "file resources") - mgmtclassCopyCmd.Flags().String("packages", "", "package resources") - mgmtclassCopyCmd.Flags().String("params", "", "list of parameters/variables") - mgmtclassCopyCmd.Flags().String("class-name", "", "actual class name (leave blank to use the name field)") - mgmtclassCopyCmd.Flags().String("is-definition", "", "is Definition? Treat this class as a definition (puppet only)") - mgmtclassCopyCmd.Flags().String("uid", "", "") // local flags for mgmtclass edit - mgmtclassEditCmd.Flags().String("name", "", "the mgmtclass name") - mgmtclassEditCmd.Flags().String("ctime", "", "") - mgmtclassEditCmd.Flags().String("depth", "", "") - mgmtclassEditCmd.Flags().String("mtime", "", "") - mgmtclassEditCmd.Flags().String("comment", "", "free form text description") - mgmtclassEditCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") + addCommonArgs(mgmtclassEditCmd) + addStringFlags(mgmtclassEditCmd, mgmtclassStringFlagMetadata) + addBoolFlags(mgmtclassEditCmd, mgmtclassBoolFlagMetadata) + addStringSliceFlags(mgmtclassEditCmd, mgmtclassStringSliceFlagMetadata) mgmtclassEditCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - mgmtclassEditCmd.Flags().String("files", "", "file resources") - mgmtclassEditCmd.Flags().String("packages", "", "package resources") - mgmtclassEditCmd.Flags().String("params", "", "list of parameters/variables") - mgmtclassEditCmd.Flags().String("class-name", "", "actual class name (leave blank to use the name field)") - mgmtclassEditCmd.Flags().String("is-definition", "", "is Definition? Treat this class as a definition (puppet only)") - mgmtclassEditCmd.Flags().String("uid", "", "") // local flags for mgmtclass find - mgmtclassFindCmd.Flags().String("name", "", "the mgmtclass name") - mgmtclassFindCmd.Flags().String("ctime", "", "") - mgmtclassFindCmd.Flags().String("depth", "", "") - mgmtclassFindCmd.Flags().String("mtime", "", "") - mgmtclassFindCmd.Flags().String("comment", "", "free form text description") - mgmtclassFindCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - mgmtclassFindCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - mgmtclassFindCmd.Flags().String("files", "", "file resources") - mgmtclassFindCmd.Flags().String("packages", "", "package resources") - mgmtclassFindCmd.Flags().String("params", "", "list of parameters/variables") - mgmtclassFindCmd.Flags().String("class-name", "", "actual class name (leave blank to use the name field)") - mgmtclassFindCmd.Flags().String("is-definition", "", "is Definition? Treat this class as a definition (puppet only)") + addCommonArgs(mgmtclassFindCmd) + addStringFlags(mgmtclassFindCmd, mgmtclassStringFlagMetadata) + addBoolFlags(mgmtclassFindCmd, mgmtclassBoolFlagMetadata) + addStringSliceFlags(mgmtclassFindCmd, mgmtclassStringSliceFlagMetadata) mgmtclassFindCmd.Flags().String("uid", "", "") + // TODO: ctime, mtime, depth // local flags for mgmtclass remove mgmtclassRemoveCmd.Flags().String("name", "", "the mgmtclass name") mgmtclassRemoveCmd.Flags().Bool("recursive", false, "also delete child objects") // local flags for mgmtclass rename - mgmtclassRenameCmd.Flags().String("name", "", "the mgmtclass name") + addCommonArgs(mgmtclassRenameCmd) + addStringFlags(mgmtclassRenameCmd, mgmtclassStringFlagMetadata) + addBoolFlags(mgmtclassRenameCmd, mgmtclassBoolFlagMetadata) + addStringSliceFlags(mgmtclassRenameCmd, mgmtclassStringSliceFlagMetadata) mgmtclassRenameCmd.Flags().String("newname", "", "the new mgmtclass name") - mgmtclassRenameCmd.Flags().String("ctime", "", "") - mgmtclassRenameCmd.Flags().String("depth", "", "") - mgmtclassRenameCmd.Flags().String("mtime", "", "") - mgmtclassRenameCmd.Flags().String("comment", "", "free form text description") - mgmtclassRenameCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") mgmtclassRenameCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - mgmtclassRenameCmd.Flags().String("files", "", "file resources") - mgmtclassRenameCmd.Flags().String("packages", "", "package resources") - mgmtclassRenameCmd.Flags().String("params", "", "list of parameters/variables") - mgmtclassRenameCmd.Flags().String("class-name", "", "actual class name (leave blank to use the name field)") - mgmtclassRenameCmd.Flags().String("is-definition", "", "is Definition? Treat this class as a definition (puppet only)") - mgmtclassRenameCmd.Flags().String("uid", "", "") // local flags for mgmtclass report mgmtclassReportCmd.Flags().String("name", "", "the mgmtclass name") diff --git a/cmd/mkloaders.go b/cmd/mkloaders.go index 2098990..362424c 100644 --- a/cmd/mkloaders.go +++ b/cmd/mkloaders.go @@ -5,6 +5,7 @@ package cmd import ( + "fmt" "github.com/spf13/cobra" ) @@ -16,9 +17,14 @@ var mkloadersCmd = &cobra.Command{ then this also generates bootloaders for different architectures then the one of the system. The options are configured in the Cobbler settings file.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient + eventId, err := Client.BackgroundMkLoaders() + if err != nil { + return err + } + fmt.Printf("Event ID: %s\n", eventId) + return nil }, } diff --git a/cmd/package.go b/cmd/package.go index aba2ff0..439b088 100644 --- a/cmd/package.go +++ b/cmd/package.go @@ -5,9 +5,76 @@ package cmd import ( + "fmt" + cobbler "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) +func updatePackageFromFlags(cmd *cobra.Command, p *cobbler.Package) error { + // TODO: in-place flag + // inPlace, err := cmd.Flags().GetBool("in-place") + _, err := cmd.Flags().GetBool("in-place") + if err != nil { + return err + } + cmd.Flags().Visit(func(flag *pflag.Flag) { + if err != nil { + // If one of the previous flags has had an error just directly return. + return + } + switch flag.Name { + case "comment": + var packageNewComment string + packageNewComment, err = cmd.Flags().GetString("comment") + if err != nil { + return + } + p.Comment = packageNewComment + case "owners": + fallthrough + case "owners-inherit": + if cmd.Flags().Lookup("owners-inherit").Changed { + p.Owners.Data = []string{} + p.Owners.IsInherited, err = cmd.Flags().GetBool("owners-inherit") + if err != nil { + return + } + } else { + var packageNewOwners []string + packageNewOwners, err = cmd.Flags().GetStringSlice("owners") + if err != nil { + return + } + p.Owners.IsInherited = false + p.Owners.Data = packageNewOwners + } + case "action": + var packageNewAction string + packageNewAction, err = cmd.Flags().GetString("action") + if err != nil { + return + } + p.Action = packageNewAction + case "installer": + var packageNewInstaller string + packageNewInstaller, err = cmd.Flags().GetString("installer") + if err != nil { + return + } + p.Action = packageNewInstaller + case "version": + var packageNewVersion string + packageNewVersion, err = cmd.Flags().GetString("version") + if err != nil { + return + } + p.Action = packageNewVersion + } + }) + return err +} + // packageCmd represents the package command var packageCmd = &cobra.Command{ Use: "package", @@ -15,7 +82,7 @@ var packageCmd = &cobra.Command{ Long: `Let you manage packages. See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-package for more information.`, Run: func(cmd *cobra.Command, args []string) { - cmd.Help() + _ = cmd.Help() }, } @@ -23,11 +90,28 @@ var packageAddCmd = &cobra.Command{ Use: "add", Short: "add package", Long: `Adds a package.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + newPackage := cobbler.NewPackage() + var err error - // TODO: call cobblerclient - notImplemented() + // internal fields (ctime, mtime, depth, uid) cannot be modified + newPackage.Name, err = cmd.Flags().GetString("name") + if err != nil { + return err + } + // Update package in-memory + err = updatePackageFromFlags(cmd, &newPackage) + if err != nil { + return err + } + // Create package via XML-RPC + linuxpackage, err := Client.CreatePackage(newPackage) + if err != nil { + return err + } + fmt.Printf("Package %s created\n", linuxpackage.Name) + return nil }, } @@ -35,11 +119,40 @@ var packageCopyCmd = &cobra.Command{ Use: "copy", Short: "copy package", Long: `Copies a package.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + // Collect CLI flags + packageName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + packageNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } - // TODO: call cobblerclient - notImplemented() + // Get package handle + packageHandle, err := Client.GetPackageHandle(packageName) + if err != nil { + return err + } + // Copy the package server-side + err = Client.CopyPackage(packageHandle, packageNewName) + if err != nil { + return err + } + // Get the copied package from the API + newPackage, err := Client.GetPackage(packageNewName, false, false) + if err != nil { + return err + } + // Update package in-memory + err = updatePackageFromFlags(cmd, newPackage) + if err != nil { + return err + } + // Update the package via XML-RPC + return Client.UpdatePackage(newPackage) }, } @@ -47,11 +160,25 @@ var packageEditCmd = &cobra.Command{ Use: "edit", Short: "edit package", Long: `Edits a package.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + packageName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } - // TODO: call cobblerclient - notImplemented() + // Get package from the API + packageToEdit, err := Client.GetPackage(packageName, false, false) + if err != nil { + return err + } + // Update package in-memory + err = updatePackageFromFlags(cmd, packageToEdit) + if err != nil { + return err + } + // Update package via XML-RPC + return Client.UpdatePackage(packageToEdit) }, } @@ -59,11 +186,9 @@ var packageFindCmd = &cobra.Command{ Use: "find", Short: "find package", Long: `Finds a given package.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return FindItemNames(cmd, args, "package") }, } @@ -73,9 +198,11 @@ var packageListCmd = &cobra.Command{ Long: `Lists all available packages.`, Run: func(cmd *cobra.Command, args []string) { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + packageNames, err := Client.ListPackageNames() + if err != nil { + fmt.Println(err) + } + listItems("packages", packageNames) }, } @@ -83,11 +210,9 @@ var packageRemoveCmd = &cobra.Command{ Use: "remove", Short: "remove package", Long: `Removes a given package.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return RemoveItemRecursive(cmd, args, "package") }, } @@ -95,23 +220,76 @@ var packageRenameCmd = &cobra.Command{ Use: "rename", Short: "rename package", Long: `Renames a given package.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // internal fields (ctime, mtime, depth, uid) cannot be modified + packageName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + packageNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + + // Get package API handle + packageHandle, err := Client.GetPackageHandle(packageName) + if err != nil { + return err + } + // Perform server-side package rename + err = Client.RenamePackage(packageHandle, packageNewName) + if err != nil { + return err + } + // Get the renamed package from the API + newPackage, err := Client.GetPackage(packageNewName, false, false) + if err != nil { + return err + } + // Update package in-memory + err = updatePackageFromFlags(cmd, newPackage) + if err != nil { + return err + } + // Update package via XML-RPC + return Client.UpdatePackage(newPackage) }, } +func reportPackages(packageNames []string) error { + for _, itemName := range packageNames { + repo, err := Client.GetRepo(itemName, false, false) + if err != nil { + return err + } + printStructured(repo) + fmt.Println("") + } + return nil +} + var packageReportCmd = &cobra.Command{ Use: "report", Short: "list all packages in detail", Long: `Shows detailed information about all packages.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + name, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + itemNames := make([]string, 0) + if name == "" { + itemNames, err = Client.ListRepoNames() + if err != nil { + return err + } + } else { + itemNames = append(itemNames, name) + } + return reportPackages(itemNames) }, } @@ -127,75 +305,35 @@ func init() { packageCmd.AddCommand(packageReportCmd) // local flags for package add - packageAddCmd.Flags().String("name", "", "the package name") - packageAddCmd.Flags().String("ctime", "", "") - packageAddCmd.Flags().String("depth", "", "") - packageAddCmd.Flags().String("mtime", "", "") - packageAddCmd.Flags().String("uid", "", "") - packageAddCmd.Flags().String("comment", "", "free form text description") - packageAddCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - packageAddCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - packageAddCmd.Flags().String("action", "", "install or remove package resourc") - packageAddCmd.Flags().String("installer", "", "package manager") - packageAddCmd.Flags().String("version", "", "package version") + addCommonArgs(packageAddCmd) + addStringFlags(packageAddCmd, packageStringFlagMetadata) // local flags for package copy - packageCopyCmd.Flags().String("name", "", "the package name") + addCommonArgs(packageCopyCmd) + addStringFlags(packageCopyCmd, packageStringFlagMetadata) packageCopyCmd.Flags().String("newname", "", "the new package name") - packageCopyCmd.Flags().String("ctime", "", "") - packageCopyCmd.Flags().String("depth", "", "") - packageCopyCmd.Flags().String("mtime", "", "") - packageCopyCmd.Flags().String("uid", "", "") - packageCopyCmd.Flags().String("comment", "", "free form text description") - packageCopyCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited)") packageCopyCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - packageCopyCmd.Flags().String("action", "", "install or remove package resourc") - packageCopyCmd.Flags().String("installer", "", "package manager") - packageCopyCmd.Flags().String("version", "", "package version") // local flags for package edit - packageEditCmd.Flags().String("name", "", "the package name") - packageEditCmd.Flags().String("ctime", "", "") - packageEditCmd.Flags().String("depth", "", "") - packageEditCmd.Flags().String("mtime", "", "") - packageEditCmd.Flags().String("uid", "", "") - packageEditCmd.Flags().String("comment", "", "free form text description") - packageEditCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") + addCommonArgs(packageEditCmd) + addStringFlags(packageEditCmd, packageStringFlagMetadata) packageEditCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - packageEditCmd.Flags().String("action", "", "install or remove package resourc") - packageEditCmd.Flags().String("installer", "", "package manager") - packageEditCmd.Flags().String("version", "", "package version") // local flags for package find - packageFindCmd.Flags().String("name", "", "the package name") - packageFindCmd.Flags().String("ctime", "", "") - packageFindCmd.Flags().String("depth", "", "") - packageFindCmd.Flags().String("mtime", "", "") - packageFindCmd.Flags().String("uid", "", "") - packageFindCmd.Flags().String("comment", "", "free form text description") - packageFindCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") + addCommonArgs(packageFindCmd) + addStringFlags(packageFindCmd, packageStringFlagMetadata) + // TODO: ctime, mtime, depth, uid packageFindCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - packageFindCmd.Flags().String("action", "", "install or remove package resourc") - packageFindCmd.Flags().String("installer", "", "package manager") - packageFindCmd.Flags().String("version", "", "package version") // local flags for package remove packageRemoveCmd.Flags().String("name", "", "the package name") packageRemoveCmd.Flags().Bool("recursive", false, "also delete child objects") // local flags for package rename - packageRenameCmd.Flags().String("name", "", "the package name") + addCommonArgs(packageRenameCmd) + addStringFlags(packageRenameCmd, packageStringFlagMetadata) packageRenameCmd.Flags().String("newname", "", "the new package name") - packageRenameCmd.Flags().String("ctime", "", "") - packageRenameCmd.Flags().String("depth", "", "") - packageRenameCmd.Flags().String("mtime", "", "") - packageRenameCmd.Flags().String("uid", "", "") - packageRenameCmd.Flags().String("comment", "", "free form text description") - packageRenameCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") packageRenameCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - packageRenameCmd.Flags().String("action", "", "install or remove package resourc") - packageRenameCmd.Flags().String("installer", "", "package manager") - packageRenameCmd.Flags().String("version", "", "package version") // local flags for package report packageReportCmd.Flags().String("name", "", "the package name") diff --git a/cmd/profile.go b/cmd/profile.go index 82f6c3e..9359b42 100644 --- a/cmd/profile.go +++ b/cmd/profile.go @@ -5,15 +5,450 @@ package cmd import ( + "encoding/json" "fmt" - "os" - cobbler "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" + "github.com/spf13/pflag" + "os" + "reflect" ) -var profile *cobbler.Profile //nolint:golint,unused -var profiles []*cobbler.Distro +func updateProfileFromFlags(cmd *cobra.Command, profile *cobbler.Profile) error { + // TODO: in-place flag + // inPlace, err := cmd.Flags().GetBool("in-place") + _, err := cmd.Flags().GetBool("in-place") + if err != nil { + return err + } + cmd.Flags().Visit(func(flag *pflag.Flag) { + if err != nil { + // If one of the previous flags has had an error just directly return. + return + } + switch flag.Name { + case "name": + return + case "newname": + return + case "repos": + var profileNewRepos []string + profileNewRepos, err = cmd.Flags().GetStringSlice("repos") + if err != nil { + return + } + profile.Repos = profileNewRepos + case "autoinstall": + var profileNewAutoinstall string + profileNewAutoinstall, err = cmd.Flags().GetString("autoinstall") + if err != nil { + return + } + profile.Autoinstall = profileNewAutoinstall + case "autoinstall-meta": + fallthrough + case "autoinstall-meta-inherit": + if cmd.Flags().Lookup("boot-loaders-inherit").Changed { + profile.AutoinstallMeta.Data = make(map[string]interface{}) + profile.AutoinstallMeta.IsInherited, err = cmd.Flags().GetBool("boot-loaders-inherit") + if err != nil { + return + } + } else { + var profileNewAutoinstallMeta map[string]string + profileNewAutoinstallMeta, err = cmd.Flags().GetStringToString("autoinstall-meta") + if err != nil { + return + } + profile.AutoinstallMeta.IsInherited = false + profile.AutoinstallMeta.Data = convertMapStringToMapInterface(profileNewAutoinstallMeta) + } + case "boot-files": + fallthrough + case "boot-files-inherit": + if cmd.Flags().Lookup("boot-files-inherit").Changed { + profile.BootFiles.Data = make(map[string]interface{}) + profile.BootFiles.IsInherited, err = cmd.Flags().GetBool("boot-files-inherit") + if err != nil { + return + } + } else { + var profileNewBootFiles map[string]string + profileNewBootFiles, err = cmd.Flags().GetStringToString("boot-files") + if err != nil { + return + } + profile.BootFiles.IsInherited = false + profile.BootFiles.Data = convertMapStringToMapInterface(profileNewBootFiles) + } + case "boot-loaders": + fallthrough + case "boot-loaders-inherit": + if cmd.Flags().Lookup("boot-loaders-inherit").Changed { + profile.BootLoaders.Data = []string{} + profile.BootLoaders.IsInherited, err = cmd.Flags().GetBool("boot-loaders-inherit") + if err != nil { + return + } + } else { + var profileNewBootLoaders []string + profileNewBootLoaders, err = cmd.Flags().GetStringSlice("boot-loaders") + if err != nil { + return + } + profile.BootLoaders.IsInherited = false + profile.BootLoaders.Data = profileNewBootLoaders + } + case "distro": + var profileNewDistro string + profileNewDistro, err = cmd.Flags().GetString("distro") + if err != nil { + return + } + profile.Distro = profileNewDistro + case "comment": + var profileNewComment string + profileNewComment, err = cmd.Flags().GetString("comment") + if err != nil { + return + } + profile.Comment = profileNewComment + case "fetchable-files": + fallthrough + case "fetchable-files-inherit": + if cmd.Flags().Lookup("fetchable-files-inherit").Changed { + profile.FetchableFiles.Data = make(map[string]interface{}) + profile.FetchableFiles.IsInherited, err = cmd.Flags().GetBool("fetchable-files-inherit") + if err != nil { + return + } + } else { + var profileNewFetchableFiles map[string]string + profileNewFetchableFiles, err = cmd.Flags().GetStringToString("fetchable-files") + if err != nil { + return + } + profile.FetchableFiles.IsInherited = false + profile.FetchableFiles.Data = convertMapStringToMapInterface(profileNewFetchableFiles) + } + case "kernel-options": + fallthrough + case "kernel-options-inherit": + if cmd.Flags().Lookup("kernel-options-inherit").Changed { + profile.KernelOptions.Data = make(map[string]interface{}) + profile.KernelOptions.IsInherited, err = cmd.Flags().GetBool("kernel-options-inherit") + if err != nil { + return + } + } else { + var profileNewKernelOptions map[string]string + profileNewKernelOptions, err = cmd.Flags().GetStringToString("kernel-options") + if err != nil { + return + } + profile.KernelOptions.IsInherited = false + profile.KernelOptions.Data = convertMapStringToMapInterface(profileNewKernelOptions) + } + case "kernel-options-post": + fallthrough + case "kernel-options-post-inherit": + if cmd.Flags().Lookup("kernel-options-post-inherit").Changed { + profile.KernelOptionsPost.Data = make(map[string]interface{}) + profile.KernelOptionsPost.IsInherited, err = cmd.Flags().GetBool("kernel-options-post-inherit") + if err != nil { + return + } + } else { + var profileNewKernelOptionsPost map[string]string + profileNewKernelOptionsPost, err = cmd.Flags().GetStringToString("kernel-options-post") + if err != nil { + return + } + profile.KernelOptionsPost.IsInherited = false + profile.KernelOptionsPost.Data = convertMapStringToMapInterface(profileNewKernelOptionsPost) + } + case "mgmt-classes": + fallthrough + case "mgmt-classes-inherit": + if cmd.Flags().Lookup("mgmt-classes-inherit").Changed { + profile.MgmtClasses.Data = []string{} + profile.MgmtClasses.IsInherited, err = cmd.Flags().GetBool("mgmt-classes-inherit") + if err != nil { + return + } + } else { + var profileNewMgmtClasses []string + profileNewMgmtClasses, err = cmd.Flags().GetStringSlice("mgmt-classes") + if err != nil { + return + } + profile.MgmtClasses.IsInherited = false + profile.MgmtClasses.Data = profileNewMgmtClasses + } + case "owners": + fallthrough + case "owners-inherit": + if cmd.Flags().Lookup("owners-inherit").Changed { + profile.Owners.Data = []string{} + profile.Owners.IsInherited, err = cmd.Flags().GetBool("owners-inherit") + if err != nil { + return + } + } else { + var profileNewOwners []string + profileNewOwners, err = cmd.Flags().GetStringSlice("owners") + if err != nil { + return + } + profile.Owners.IsInherited = false + profile.Owners.Data = profileNewOwners + } + case "redhat-management-key": + var profileNewRedhatManagementKey string + profileNewRedhatManagementKey, err = cmd.Flags().GetString("redhat-management-key") + if err != nil { + return + } + profile.RedhatManagementKey = profileNewRedhatManagementKey + case "template-files-post": + fallthrough + case "template-files-inherit": + if cmd.Flags().Lookup("template-files-inherit").Changed { + profile.TemplateFiles.Data = make(map[string]interface{}) + profile.TemplateFiles.IsInherited, err = cmd.Flags().GetBool("template-files-inherit") + if err != nil { + return + } + } else { + var profileNewTemplateFiles map[string]string + profileNewTemplateFiles, err = cmd.Flags().GetStringToString("template-files") + if err != nil { + return + } + profile.TemplateFiles.IsInherited = false + profile.TemplateFiles.Data = convertMapStringToMapInterface(profileNewTemplateFiles) + } + case "dhcp-tag": + var profileNewDhcpTag string + profileNewDhcpTag, err = cmd.Flags().GetString("dhcp-tag") + if err != nil { + return + } + profile.DHCPTag = profileNewDhcpTag + case "enable-ipxe": + fallthrough + case "enable-ipxe-inherit": + if cmd.Flags().Lookup("enable-ipxe-inherit").Changed { + profile.EnableIPXE.Data = false + profile.EnableIPXE.IsInherited, err = cmd.Flags().GetBool("enable-ipxe-inherit") + if err != nil { + return + } + } else { + var profileNewEnableIpxe bool + profileNewEnableIpxe, err = cmd.Flags().GetBool("enable-ipxe") + if err != nil { + return + } + profile.EnableIPXE.IsInherited = false + profile.EnableIPXE.Data = profileNewEnableIpxe + } + case "enable-menu": + fallthrough + case "enable-menu-inherit": + if cmd.Flags().Lookup("enable-menu-inherit").Changed { + profile.EnableMenu.Data = false + profile.EnableMenu.IsInherited, err = cmd.Flags().GetBool("enable-menu-inherit") + if err != nil { + return + } + } else { + var profileNewEnableMenu bool + profileNewEnableMenu, err = cmd.Flags().GetBool("enable-menu") + if err != nil { + return + } + profile.EnableMenu.IsInherited = false + profile.EnableMenu.Data = profileNewEnableMenu + } + case "mgmt-parameters": + fallthrough + case "mgmt-parameters-inherit": + if cmd.Flags().Lookup("mgmt-parameters-inherit").Changed { + profile.MgmtParameters.Data = make(map[string]interface{}) + profile.MgmtParameters.IsInherited, err = cmd.Flags().GetBool("mgmt-parameters-inherit") + if err != nil { + return + } + } else { + var profileNewMgmtParameters map[string]string + profileNewMgmtParameters, err = cmd.Flags().GetStringToString("mgmt-parameters") + if err != nil { + return + } + profile.MgmtParameters.IsInherited = false + profile.MgmtParameters.Data = convertMapStringToMapInterface(profileNewMgmtParameters) + } + case "name-servers": + fallthrough + case "name-servers-inherit": + if cmd.Flags().Lookup("name-servers-inherit").Changed { + profile.NameServers.Data = make([]string, 0) + profile.NameServers.IsInherited = true + } else { + var profileNewNameServers []string + profileNewNameServers, err = cmd.Flags().GetStringSlice("name-servers") + if err != nil { + return + } + profile.NameServers.Data = profileNewNameServers + profile.NameServers.IsInherited = false + } + case "name-servers-search": + fallthrough + case "name-servers-search-inherit": + if cmd.Flags().Lookup("name-servers-search-inherit").Changed { + profile.NameServersSearch.Data = make([]string, 0) + profile.NameServersSearch.IsInherited = true + } else { + var profileNewNameServersSearch []string + profileNewNameServersSearch, err = cmd.Flags().GetStringSlice("name-servers-search") + if err != nil { + return + } + profile.NameServersSearch.Data = profileNewNameServersSearch + profile.NameServersSearch.IsInherited = false + } + case "next-server-v4": + var profileNewNextServerV4 string + profileNewNextServerV4, err = cmd.Flags().GetString("next-server-v4") + if err != nil { + return + } + profile.NextServerv4 = profileNewNextServerV4 + case "next-server-v6": + var profileNewNextServerV6 string + profileNewNextServerV6, err = cmd.Flags().GetString("next-server-v6") + if err != nil { + return + } + profile.NextServerv6 = profileNewNextServerV6 + case "filename": + var profileNewFilename string + profileNewFilename, err = cmd.Flags().GetString("filename") + if err != nil { + return + } + profile.Filename = profileNewFilename + case "parent": + var profileNewParent string + profileNewParent, err = cmd.Flags().GetString("parent") + if err != nil { + return + } + profile.Parent = profileNewParent + case "proxy": + var profileNewProxy string + profileNewProxy, err = cmd.Flags().GetString("proxy") + if err != nil { + return + } + profile.Proxy = profileNewProxy + case "server": + var profileNewServer string + profileNewServer, err = cmd.Flags().GetString("server") + if err != nil { + return + } + profile.Server = profileNewServer + case "menu": + var profileNewMenu string + profileNewMenu, err = cmd.Flags().GetString("menu") + if err != nil { + return + } + profile.Menu = profileNewMenu + case "virt-auto-boot": + fallthrough + case "virt-auto-boot-inherit": + if cmd.Flags().Lookup("virt-auto-boot-inherit").Changed { + profile.VirtAutoBoot.IsInherited = true + } else { + var profileNewVirtAutoBoot bool + profileNewVirtAutoBoot, err = cmd.Flags().GetBool("virt-auto-boot") + if err != nil { + return + } + profile.VirtAutoBoot.Data = profileNewVirtAutoBoot + profile.VirtAutoBoot.IsInherited = false + } + case "virt-bridge": + var profileNewVirtBridge string + profileNewVirtBridge, err = cmd.Flags().GetString("virt-bridge") + if err != nil { + return + } + profile.VirtBridge = profileNewVirtBridge + case "virt-cpus": + var profileNewVirtCpus int + profileNewVirtCpus, err = cmd.Flags().GetInt("virt-cpus") + if err != nil { + return + } + profile.VirtCPUs = profileNewVirtCpus + case "virt-disk-driver": + var profileNewVirtDiskDriver string + profileNewVirtDiskDriver, err = cmd.Flags().GetString("virt-disk-driver") + if err != nil { + return + } + profile.VirtDiskDriver = profileNewVirtDiskDriver + case "virt-file-size": + fallthrough + case "virt-file-size-inherit": + if cmd.Flags().Lookup("virt-auto-boot-inherit").Changed { + profile.VirtAutoBoot.IsInherited = true + } else { + var profileNewVirtFileSize float64 + profileNewVirtFileSize, err = cmd.Flags().GetFloat64("virt-file-size") + if err != nil { + return + } + profile.VirtFileSize.Data = profileNewVirtFileSize + profile.VirtFileSize.IsInherited = false + } + case "virt-path": + var profileNewVirtPath string + profileNewVirtPath, err = cmd.Flags().GetString("virt-path") + if err != nil { + return + } + profile.VirtPath = profileNewVirtPath + case "virt-ram": + fallthrough + case "virt-ram-inherit": + if cmd.Flags().Lookup("virt-auto-boot-inherit").Changed { + profile.VirtRAM.IsInherited = true + } else { + var profileNewVirtRam int + profileNewVirtRam, err = cmd.Flags().GetInt("virt-ram") + if err != nil { + return + } + profile.VirtRAM.Data = profileNewVirtRam + profile.VirtRAM.IsInherited = false + } + case "virt-type": + var profileNewVirtType string + profileNewVirtType, err = cmd.Flags().GetString("virt-type") + if err != nil { + return + } + profile.VirtType = profileNewVirtType + } + }) + return nil +} // profileCmd represents the profile command var profileCmd = &cobra.Command{ @@ -22,7 +457,7 @@ var profileCmd = &cobra.Command{ Long: `Let you manage profiles. See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-profile for more information.`, Run: func(cmd *cobra.Command, args []string) { - cmd.Help() + _ = cmd.Help() }, } @@ -30,50 +465,26 @@ var profileAddCmd = &cobra.Command{ Use: "add", Short: "add profile", Long: `Adds a profile.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - var newProfile cobbler.Profile + newProfile := cobbler.NewProfile() + var err error // internal fields (ctime, mtime, uid, depth, repos-enabled) cannot be modified - newProfile.Autoinstall, _ = cmd.Flags().GetString("autoinstall") - newProfile.AutoinstallMeta, _ = cmd.Flags().GetStringArray("autoinstall-meta") - newProfile.BootFiles, _ = cmd.Flags().GetStringArray("bootfiles") - newProfile.Comment, _ = cmd.Flags().GetString("comment") - newProfile.DHCPTag, _ = cmd.Flags().GetString("dhcp-tag") - newProfile.Distro, _ = cmd.Flags().GetString("distro") - newProfile.EnableGPXE, _ = cmd.Flags().GetBool("enable-ipxe") - newProfile.EnableMenu, _ = cmd.Flags().GetBool("enable-menu") - newProfile.FetchableFiles, _ = cmd.Flags().GetStringArray("fetchable-files") - newProfile.KernelOptions, _ = cmd.Flags().GetStringArray("kernel-options") - newProfile.KernelOptionsPost, _ = cmd.Flags().GetStringArray("kernel-options-post") - newProfile.MGMTClasses, _ = cmd.Flags().GetStringArray("mgmt-classes") - newProfile.MGMTParameters, _ = cmd.Flags().GetString("mgmt-parameters") - newProfile.Name, _ = cmd.Flags().GetString("name") - newProfile.NameServers, _ = cmd.Flags().GetStringArray("name-servers") - newProfile.NameServersSearch, _ = cmd.Flags().GetStringArray("name-servers-search") - newProfile.NextServerv4, _ = cmd.Flags().GetString("next-server-v4") - newProfile.Owners, _ = cmd.Flags().GetStringArray("owners") - newProfile.Proxy, _ = cmd.Flags().GetString("proxy") - // newProfile.RedHatManagementKey, _ = cmd.Flags().GetString("redhat-management-key") - newProfile.Repos, _ = cmd.Flags().GetStringArray("repos") - newProfile.Server, _ = cmd.Flags().GetString("server") - newProfile.TemplateFiles, _ = cmd.Flags().GetStringArray("template-files") - newProfile.VirtAutoBoot, _ = cmd.Flags().GetString("virt-auto-boot") - newProfile.VirtBridge, _ = cmd.Flags().GetString("virt-bridge") - newProfile.VirtCPUs, _ = cmd.Flags().GetString("virt-cpus") - newProfile.VirtDiskDriver, _ = cmd.Flags().GetString("virt-disk-driver") - newProfile.VirtFileSize, _ = cmd.Flags().GetString("virt-file-size") - newProfile.VirtPath, _ = cmd.Flags().GetString("virt-path") - newProfile.VirtRAM, _ = cmd.Flags().GetString("virt-ram") - newProfile.VirtType, _ = cmd.Flags().GetString("virt-type") - - profile, err = Client.CreateProfile(newProfile) - - if checkError(err) == nil { - fmt.Printf("Profile %s created", newProfile.Name) - } else { - fmt.Fprintln(os.Stderr, err.Error()) + newProfile.Name, err = cmd.Flags().GetString("name") + if err != nil { + return err + } + err = updateProfileFromFlags(cmd, &newProfile) + if err != nil { + return err } + profile, err := Client.CreateProfile(newProfile) + if err != nil { + return err + } + fmt.Printf("Profile %s created\n", profile.Name) + return nil }, } @@ -81,11 +492,34 @@ var profileCopyCmd = &cobra.Command{ Use: "copy", Short: "copy profile", Long: `Copies a profile.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + profileName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + profileNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } - // TODO: call cobblerclient - notImplemented() + profileHandle, err := Client.GetProfileHandle(profileName) + if err != nil { + return err + } + err = Client.CopyDistro(profileHandle, profileNewName) + if err != nil { + return err + } + newProfile, err := Client.GetProfile(profileNewName, false, false) + if err != nil { + return err + } + err = updateProfileFromFlags(cmd, newProfile) + if err != nil { + return err + } + return Client.UpdateProfile(newProfile) }, } @@ -93,11 +527,64 @@ var profileDumpVarsCmd = &cobra.Command{ Use: "dumpvars", Short: "dump profile variables", Long: `Prints all profile variables to stdout.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get CLI flags + profileName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + + // Now retrieve data + blendedData, err := Client.GetBlendedData(profileName, "") + if err != nil { + return err + } + // Print data + // TODO: Deduplicate with system + for key, value := range blendedData { + if value == nil { + fmt.Printf("%s:\n", key) + continue + } + valueType := reflect.TypeOf(value).Kind() + switch valueType { + case reflect.Bool: + fmt.Printf("%s: %t\n", key, value.(bool)) + case reflect.Int64: + fmt.Printf("%s: %d\n", key, value.(int64)) + case reflect.Int32: + fmt.Printf("%s: %d\n", key, value.(int32)) + case reflect.Int16: + fmt.Printf("%s: %d\n", key, value.(int16)) + case reflect.Int8: + fmt.Printf("%s: %d\n", key, value.(int8)) + case reflect.Int: + fmt.Printf("%s: %d\n", key, value.(int)) + case reflect.Float32: + fmt.Printf("%s: %f\n", key, value.(float32)) + case reflect.Float64: + fmt.Printf("%s: %f\n", key, value.(float64)) + case reflect.Slice, reflect.Array: + arr := reflect.ValueOf(value) + fmt.Printf("%s: [", key) + for i := 0; i < arr.Len(); i++ { + if i+1 != arr.Len() { + fmt.Printf("'%v', ", arr.Index(i).Interface()) + } else { + fmt.Printf("'%v'", arr.Index(i).Interface()) + } + } + fmt.Printf("]\n") + case reflect.Map: + res2B, _ := json.Marshal(value) + fmt.Printf("%s: %s\n", key, string(res2B)) + default: + fmt.Printf("%s: %s\n", key, value) + } + } + return err }, } @@ -105,155 +592,25 @@ var profileEditCmd = &cobra.Command{ Use: "edit", Short: "edit profile", Long: `Edits a profile.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() // find profile through its name - pname, _ := cmd.Flags().GetString("name") - var updateProfile, err = Client.GetProfile(pname) - - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + pname, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + updateProfile, err := Client.GetProfile(pname, false, false) + if err != nil { + return err } // internal fields (ctime, mtime, uid, depth, repos-enabled) cannot be modified - var tmpArgs, _ = cmd.Flags().GetString("autoinstall") - if tmpArgs != "" { - updateProfile.Autoinstall, _ = cmd.Flags().GetString("autoinstall") - } - var tmpArgsArray, _ = cmd.Flags().GetStringArray("autoinstall-meta") - if len(tmpArgsArray) > 0 { - updateProfile.AutoinstallMeta, _ = cmd.Flags().GetStringArray("autoinstall-meta") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("bootfiles") - if len(tmpArgsArray) > 0 { - updateProfile.BootFiles, _ = cmd.Flags().GetStringArray("bootfiles") - } - tmpArgs, _ = cmd.Flags().GetString("comment") - if tmpArgs != "" { - updateProfile.Comment, _ = cmd.Flags().GetString("comment") - } - tmpArgs, _ = cmd.Flags().GetString("dhcp-tag") - if tmpArgs != "" { - updateProfile.DHCPTag, _ = cmd.Flags().GetString("dhcp-tag") - } - tmpArgs, _ = cmd.Flags().GetString("distro") - if tmpArgs != "" { - updateProfile.Distro, _ = cmd.Flags().GetString("distro") - } - // TODO - /* var tmpArgsbool, _ = cmd.Flags().GetBool("enable-ipxe") - if tmpArgsbool != "" { - updateProfile.EnableGPXE, _ = cmd.Flags().GetBool("enable-ipxe") - } - tmpArgsbool, _ = cmd.Flags().GetBool("enable-menu") - if tmpArgsbool != "" { - updateProfile.EnableMenu, _ = cmd.Flags().GetBool("enable-menu") - } - */ - tmpArgsArray, _ = cmd.Flags().GetStringArray("fetchable-files") - if len(tmpArgsArray) > 0 { - updateProfile.FetchableFiles, _ = cmd.Flags().GetStringArray("fetchable-files") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("kernel-options") - if len(tmpArgsArray) > 0 { - updateProfile.KernelOptions, _ = cmd.Flags().GetStringArray("kernel-options") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("kernel-options-post") - if len(tmpArgsArray) > 0 { - updateProfile.KernelOptionsPost, _ = cmd.Flags().GetStringArray("kernel-options-post") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("mgmt-classes") - if len(tmpArgsArray) > 0 { - updateProfile.MGMTClasses, _ = cmd.Flags().GetStringArray("mgmt-classes") - } - tmpArgs, _ = cmd.Flags().GetString("mgmt-parameters") - if tmpArgs != "" { - updateProfile.MGMTParameters, _ = cmd.Flags().GetString("mgmt-parameters") - } - tmpArgs, _ = cmd.Flags().GetString("name") - if tmpArgs != "" { - updateProfile.Name, _ = cmd.Flags().GetString("name") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("name-servers") - if len(tmpArgsArray) > 0 { - updateProfile.NameServers, _ = cmd.Flags().GetStringArray("name-servers") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("name-servers-search") - if len(tmpArgsArray) > 0 { - updateProfile.NameServersSearch, _ = cmd.Flags().GetStringArray("name-servers-search") - } - tmpArgs, _ = cmd.Flags().GetString("next-server-v4") - if tmpArgs != "" { - updateProfile.NextServerv4, _ = cmd.Flags().GetString("next-server-v4") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("owners") - if len(tmpArgsArray) > 0 { - updateProfile.Owners, _ = cmd.Flags().GetStringArray("owners") - } - tmpArgs, _ = cmd.Flags().GetString("proxy") - if tmpArgs != "" { - updateProfile.Proxy, _ = cmd.Flags().GetString("proxy") - } - /* - * tmpArgs, _ = cmd.Flags().GetString("redhat-management-key") - * if tmpArgs != "" { - * updateProfile.RedHatManagementKey, _ = cmd.Flags().GetString("redhat-management-key") - * } - */ - tmpArgsArray, _ = cmd.Flags().GetStringArray("repos") - if len(tmpArgsArray) > 0 { - updateProfile.Repos, _ = cmd.Flags().GetStringArray("repos") - } - tmpArgs, _ = cmd.Flags().GetString("server") - if tmpArgs != "" { - updateProfile.Server, _ = cmd.Flags().GetString("server") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("template-files") - if len(tmpArgsArray) > 0 { - updateProfile.TemplateFiles, _ = cmd.Flags().GetStringArray("template-files") - } - tmpArgs, _ = cmd.Flags().GetString("virt-auto-boot") - if tmpArgs != "" { - updateProfile.VirtAutoBoot, _ = cmd.Flags().GetString("virt-auto-boot") - } - tmpArgs, _ = cmd.Flags().GetString("virt-bridge") - if tmpArgs != "" { - updateProfile.VirtBridge, _ = cmd.Flags().GetString("virt-bridge") - } - tmpArgs, _ = cmd.Flags().GetString("virt-cpus") - if tmpArgs != "" { - updateProfile.VirtCPUs, _ = cmd.Flags().GetString("virt-cpus") - } - tmpArgs, _ = cmd.Flags().GetString("virt-disk-driver") - if tmpArgs != "" { - updateProfile.VirtDiskDriver, _ = cmd.Flags().GetString("virt-disk-driver") - } - tmpArgs, _ = cmd.Flags().GetString("virt-file-size") - if tmpArgs != "" { - updateProfile.VirtFileSize, _ = cmd.Flags().GetString("virt-file-size") - } - tmpArgs, _ = cmd.Flags().GetString("virt-path") - if tmpArgs != "" { - updateProfile.VirtPath, _ = cmd.Flags().GetString("virt-path") - } - tmpArgs, _ = cmd.Flags().GetString("virt-ram") - if tmpArgs != "" { - updateProfile.VirtRAM, _ = cmd.Flags().GetString("virt-ram") - } - tmpArgs, _ = cmd.Flags().GetString("virt-type") - if tmpArgs != "" { - updateProfile.VirtType, _ = cmd.Flags().GetString("virt-type") - } - - err = Client.UpdateProfile(updateProfile) - - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + err = updateProfileFromFlags(cmd, updateProfile) + if err != nil { + return err } - + return Client.UpdateProfile(updateProfile) }, } @@ -261,11 +618,9 @@ var profileFindCmd = &cobra.Command{ Use: "find", Short: "find profile", Long: `Finds a given profile.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return FindItemNames(cmd, args, "profile") }, } @@ -273,11 +628,26 @@ var profileGetAutoinstallCmd = &cobra.Command{ Use: "get-autoinstall", Short: "dump autoinstall XML", Long: `Prints the autoinstall XML file of the given profile to stdout.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + profileName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + profileExists, err := Client.HasItem("profile", profileName) + if err != nil { + return err + } + if !profileExists { + fmt.Println("Profile does not exist!") + os.Exit(1) + } + autoinstallRendered, err := Client.GenerateAutoinstall(profileName, "") + if err != nil { + return err + } + fmt.Println(autoinstallRendered) + return nil }, } @@ -285,17 +655,14 @@ var profileListCmd = &cobra.Command{ Use: "list", Short: "list all profiles", Long: `Lists all available profiles.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - profiles, err = Client.GetDistros() - - if checkError(err) == nil { - fmt.Println(profiles) - } else { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + profileNames, err := Client.ListProfileNames() + if err != nil { + return err } + listItems("profiles", profileNames) + return nil }, } @@ -303,15 +670,18 @@ var profileRemoveCmd = &cobra.Command{ Use: "remove", Short: "remove profile", Long: `Removes a given profile.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - pname, _ := cmd.Flags().GetString("name") - err := Client.DeleteProfile(pname) - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + pname, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + recursiveDelete, err := cmd.Flags().GetBool("recursive") + if err != nil { + return err } + return Client.DeleteProfileRecursive(pname, recursiveDelete) }, } @@ -319,23 +689,70 @@ var profileRenameCmd = &cobra.Command{ Use: "rename", Short: "rename profile", Long: `Renames a given profile.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + profileName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + profileNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } - // TODO: call cobblerclient - notImplemented() + // Now do the real edit + profileHandle, err := Client.GetProfileHandle(profileName) + if err != nil { + return err + } + err = Client.RenameProfile(profileHandle, profileNewName) + if err != nil { + return err + } + newProfile, err := Client.GetProfile(profileNewName, false, false) + if err != nil { + return err + } + err = updateProfileFromFlags(cmd, newProfile) + if err != nil { + return err + } + return Client.UpdateProfile(newProfile) }, } +func reportProfiles(profileNames []string) error { + for _, itemName := range profileNames { + profile, err := Client.GetProfile(itemName, false, false) + if err != nil { + return err + } + printStructured(profile) + fmt.Println("") + } + return nil +} + var profileReportCmd = &cobra.Command{ Use: "report", Short: "list all profiles in detail", Long: `Shows detailed information about all profiles.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + name, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + itemNames := make([]string, 0) + if name == "" { + itemNames, err = Client.ListProfileNames() + if err != nil { + return err + } + } else { + itemNames = append(itemNames, name) + } + return reportProfiles(itemNames) }, } @@ -353,167 +770,59 @@ func init() { profileCmd.AddCommand(profileReportCmd) // local flags for profile add - profileAddCmd.Flags().String("name", "", "the profile name") - profileAddCmd.Flags().String("repos", "", "(repos to auto-assign to this profile") - profileAddCmd.Flags().String("autoinstall", "", "path to automatic installation template") - profileAddCmd.Flags().String("autoinstall-meta", "", "automatic installation metadata") - profileAddCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - profileAddCmd.Flags().String("boot-loaders", "", "boot loader space delimited list (network installation boot loaders). Valid options for list items are: <>,grub,pxe,ipxe") - profileAddCmd.Flags().String("distro", "", "the name of a previously defined Cobbler distribution. This value is required") - profileAddCmd.Flags().String("comment", "", "free form text description") - profileAddCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - profileAddCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - profileAddCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - profileAddCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - profileAddCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - profileAddCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - profileAddCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") + addCommonArgs(profileAddCmd) + addStringFlags(profileAddCmd, profileStringFlagMetadata) + addBoolFlags(profileAddCmd, profileBoolFlagMetadata) + addIntFlags(profileAddCmd, profileIntFlagMetadata) + addFloatFlags(profileAddCmd, profileFloatFlagMetadata) + addStringSliceFlags(profileAddCmd, distroStringSliceFlagMetadata) + addStringSliceFlags(profileAddCmd, profileStringSliceFlagMetadata) + addMapFlags(profileAddCmd, distroMapFlagMetadata) + addMapFlags(profileAddCmd, profileMapFlagMetadata) profileAddCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - profileAddCmd.Flags().String("dhcp-tag", "", "DHCP tag (see manpage or leave blank)") - profileAddCmd.Flags().Bool("enable-ipxe", false, "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)") - profileAddCmd.Flags().Bool("enable-menu", false, "enable PXE Menu? (show this profile in the PXE menu?)") - profileAddCmd.Flags().String("mgmt-parameters", "", "Parameters which will be handed to your management application (must be a valid YAML dictionary))") - profileAddCmd.Flags().String("name-servers", "", "name servers (space delimited)") - profileAddCmd.Flags().String("name-servers-search", "", "name servers search path (space delimited)") - profileAddCmd.Flags().String("next-server-v4", "", "next server (IPv4) override (see manpage or leave blank)") - profileAddCmd.Flags().String("next-server-v6", "", "next server (IPv6) override (see manpage or leave blank)") - profileAddCmd.Flags().String("filename", "", "DHCP filename override (used to boot non-default bootloaders)") - profileAddCmd.Flags().String("parent", "", "parent profile") - profileAddCmd.Flags().String("proxy", "", "proxy server URL") - profileAddCmd.Flags().String("server", "", "server override") - profileAddCmd.Flags().String("menu", "", "parent boot menu") - profileAddCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - profileAddCmd.Flags().String("virt-bridge", "", "virt bridge") - profileAddCmd.Flags().String("virt-cpus", "", "virt CPUs") - profileAddCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - profileAddCmd.Flags().String("virt-file-size", "", "virt file size in GB") - profileAddCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - profileAddCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - profileAddCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)") // local flags for profile copy - profileCopyCmd.Flags().String("name", "", "the profile name") + addCommonArgs(profileCopyCmd) + addStringFlags(profileCopyCmd, profileStringFlagMetadata) + addBoolFlags(profileCopyCmd, profileBoolFlagMetadata) + addIntFlags(profileCopyCmd, profileIntFlagMetadata) + addFloatFlags(profileCopyCmd, profileFloatFlagMetadata) + addStringSliceFlags(profileCopyCmd, distroStringSliceFlagMetadata) + addStringSliceFlags(profileCopyCmd, profileStringSliceFlagMetadata) + addMapFlags(profileCopyCmd, distroMapFlagMetadata) + addMapFlags(profileCopyCmd, profileMapFlagMetadata) profileCopyCmd.Flags().String("newname", "", "the new profile name") - profileCopyCmd.Flags().String("repos", "", "(repos to auto-assign to this profile") - profileCopyCmd.Flags().String("autoinstall", "", "path to automatic installation template") - profileCopyCmd.Flags().String("autoinstall-meta", "", "automatic installation metadata") - profileCopyCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - profileCopyCmd.Flags().String("boot-loaders", "", "boot loader space delimited list (network installation boot loaders). Valid options for list items are: <>,grub,pxe,ipxe") - profileCopyCmd.Flags().String("distro", "", "the name of a previously defined Cobbler distribution. This value is required") - profileCopyCmd.Flags().String("comment", "", "free form text description") - profileCopyCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - profileCopyCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - profileCopyCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - profileCopyCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - profileCopyCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - profileCopyCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - profileCopyCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") profileCopyCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - profileCopyCmd.Flags().String("dhcp-tag", "", "DHCP tag (see manpage or leave blank)") - profileCopyCmd.Flags().Bool("enable-ipxe", false, "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)") - profileCopyCmd.Flags().Bool("enable-menu", false, "enable PXE Menu? (show this profile in the PXE menu?)") - profileCopyCmd.Flags().String("mgmt-parameters", "", "Parameters which will be handed to your management application (must be a valid YAML dictionary))") - profileCopyCmd.Flags().String("name-servers", "", "name servers (space delimited)") - profileCopyCmd.Flags().String("name-servers-search", "", "name servers search path (space delimited)") - profileCopyCmd.Flags().String("next-server-v4", "", "next server (IPv4) override (see manpage or leave blank)") - profileCopyCmd.Flags().String("next-server-v6", "", "next server (IPv6) override (see manpage or leave blank)") - profileCopyCmd.Flags().String("filename", "", "DHCP filename override (used to boot non-default bootloaders)") - profileCopyCmd.Flags().String("parent", "", "parent profile") - profileCopyCmd.Flags().String("proxy", "", "proxy server URL") - profileCopyCmd.Flags().String("server", "", "server override") - profileCopyCmd.Flags().String("menu", "", "parent boot menu") - profileCopyCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - profileCopyCmd.Flags().String("virt-bridge", "", "virt bridge") - profileCopyCmd.Flags().String("virt-cpus", "", "virt CPUs") - profileCopyCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - profileCopyCmd.Flags().String("virt-file-size", "", "virt file size in GB") - profileCopyCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - profileCopyCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - profileCopyCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)") // local flags for profile dumpvars profileDumpVarsCmd.Flags().String("name", "", "the profile name") // local flags for profile edit - profileEditCmd.Flags().String("name", "", "the profile name") - profileEditCmd.Flags().String("repos", "", "(repos to auto-assign to this profile") - profileEditCmd.Flags().String("autoinstall", "", "path to automatic installation template") - profileEditCmd.Flags().String("autoinstall-meta", "", "automatic installation metadata") - profileEditCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - profileEditCmd.Flags().String("boot-loaders", "", "boot loader space delimited list (network installation boot loaders). Valid options for list items are: <>,grub,pxe,ipxe") - profileEditCmd.Flags().String("distro", "", "the name of a previously defined Cobbler distribution. This value is required") - profileEditCmd.Flags().String("comment", "", "free form text description") - profileEditCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - profileEditCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - profileEditCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - profileEditCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - profileEditCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - profileEditCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - profileEditCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") + addCommonArgs(profileEditCmd) + addStringFlags(profileEditCmd, profileStringFlagMetadata) + addBoolFlags(profileEditCmd, profileBoolFlagMetadata) + addIntFlags(profileEditCmd, profileIntFlagMetadata) + addFloatFlags(profileEditCmd, profileFloatFlagMetadata) + addStringSliceFlags(profileEditCmd, distroStringSliceFlagMetadata) + addStringSliceFlags(profileEditCmd, profileStringSliceFlagMetadata) + addMapFlags(profileEditCmd, distroMapFlagMetadata) + addMapFlags(profileEditCmd, profileMapFlagMetadata) profileEditCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - profileEditCmd.Flags().String("dhcp-tag", "", "DHCP tag (see manpage or leave blank)") - profileEditCmd.Flags().Bool("enable-ipxe", false, "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)") - profileEditCmd.Flags().Bool("enable-menu", false, "enable PXE Menu? (show this profile in the PXE menu?)") - profileEditCmd.Flags().String("mgmt-parameters", "", "Parameters which will be handed to your management application (must be a valid YAML dictionary))") - profileEditCmd.Flags().String("name-servers", "", "name servers (space delimited)") - profileEditCmd.Flags().String("name-servers-search", "", "name servers search path (space delimited)") - profileEditCmd.Flags().String("next-server-v4", "", "next server (IPv4) override (see manpage or leave blank)") - profileEditCmd.Flags().String("next-server-v6", "", "next server (IPv6) override (see manpage or leave blank)") - profileEditCmd.Flags().String("filename", "", "DHCP filename override (used to boot non-default bootloaders)") - profileEditCmd.Flags().String("parent", "", "parent profile") - profileEditCmd.Flags().String("proxy", "", "proxy server URL") - profileEditCmd.Flags().String("server", "", "server override") - profileEditCmd.Flags().String("menu", "", "parent boot menu") - profileEditCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - profileEditCmd.Flags().String("virt-bridge", "", "virt bridge") - profileEditCmd.Flags().String("virt-cpus", "", "virt CPUs") - profileEditCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - profileEditCmd.Flags().String("virt-file-size", "", "virt file size in GB") - profileEditCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - profileEditCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - profileEditCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)") // local flags for profile find - profileFindCmd.Flags().String("name", "", "the profile name") + addCommonArgs(profileFindCmd) + addStringFlags(profileFindCmd, profileStringFlagMetadata) + addBoolFlags(profileFindCmd, profileBoolFlagMetadata) + addIntFlags(profileFindCmd, profileIntFlagMetadata) + addFloatFlags(profileFindCmd, profileFloatFlagMetadata) + addStringSliceFlags(profileFindCmd, distroStringSliceFlagMetadata) + addStringSliceFlags(profileFindCmd, profileStringSliceFlagMetadata) + addMapFlags(profileFindCmd, distroMapFlagMetadata) + addMapFlags(profileFindCmd, profileMapFlagMetadata) profileFindCmd.Flags().String("ctime", "", "") profileFindCmd.Flags().String("depth", "", "") profileFindCmd.Flags().String("mtime", "", "") - profileFindCmd.Flags().String("repos", "", "(repos to auto-assign to this profile") profileFindCmd.Flags().String("uid", "", "UID") - profileFindCmd.Flags().String("autoinstall", "", "path to automatic installation template") - profileFindCmd.Flags().String("autoinstall-meta", "", "automatic installation metadata") - profileFindCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - profileFindCmd.Flags().String("boot-loaders", "", "boot loader space delimited list (network installation boot loaders). Valid options for list items are: <>,grub,pxe,ipxe") - profileFindCmd.Flags().String("distro", "", "the name of a previously defined Cobbler distribution. This value is required") - profileFindCmd.Flags().String("comment", "", "free form text description") - profileFindCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - profileFindCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - profileFindCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - profileFindCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - profileFindCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - profileFindCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - profileFindCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") - profileFindCmd.Flags().String("dhcp-tag", "", "DHCP tag (see manpage or leave blank)") - profileFindCmd.Flags().Bool("enable-ipxe", false, "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)") - profileFindCmd.Flags().Bool("enable-menu", false, "enable PXE Menu? (show this profile in the PXE menu?)") - profileFindCmd.Flags().String("mgmt-parameters", "", "Parameters which will be handed to your management application (must be a valid YAML dictionary))") - profileFindCmd.Flags().String("name-servers", "", "name servers (space delimited)") - profileFindCmd.Flags().String("name-servers-search", "", "name servers search path (space delimited)") - profileFindCmd.Flags().String("next-server-v4", "", "next server (IPv4) override (see manpage or leave blank)") - profileFindCmd.Flags().String("next-server-v6", "", "next server (IPv6) override (see manpage or leave blank)") - profileFindCmd.Flags().String("filename", "", "DHCP filename override (used to boot non-default bootloaders)") - profileFindCmd.Flags().String("parent", "", "parent profile") - profileFindCmd.Flags().String("proxy", "", "proxy server URL") - profileFindCmd.Flags().String("server", "", "server override") - profileFindCmd.Flags().String("menu", "", "parent boot menu") - profileFindCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - profileFindCmd.Flags().String("virt-bridge", "", "virt bridge") - profileFindCmd.Flags().String("virt-cpus", "", "virt CPUs") - profileFindCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - profileFindCmd.Flags().String("virt-file-size", "", "virt file size in GB") - profileFindCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - profileFindCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - profileFindCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)") // local flags for profile get-autoinstall profileGetAutoinstallCmd.Flags().String("name", "", "the profile name") @@ -523,44 +832,17 @@ func init() { profileRemoveCmd.Flags().Bool("recursive", false, "also delete child objects") // local flags for profile rename - profileRenameCmd.Flags().String("name", "", "the profile name") + addCommonArgs(profileRenameCmd) + addStringFlags(profileRenameCmd, profileStringFlagMetadata) + addBoolFlags(profileRenameCmd, profileBoolFlagMetadata) + addIntFlags(profileRenameCmd, profileIntFlagMetadata) + addFloatFlags(profileRenameCmd, profileFloatFlagMetadata) + addStringSliceFlags(profileRenameCmd, distroStringSliceFlagMetadata) + addStringSliceFlags(profileRenameCmd, profileStringSliceFlagMetadata) + addMapFlags(profileRenameCmd, distroMapFlagMetadata) + addMapFlags(profileRenameCmd, profileMapFlagMetadata) profileRenameCmd.Flags().String("newname", "", "the new profile name") - profileRenameCmd.Flags().String("repos", "", "(repos to auto-assign to this profile") - profileRenameCmd.Flags().String("autoinstall", "", "path to automatic installation template") - profileRenameCmd.Flags().String("autoinstall-meta", "", "automatic installation metadata") - profileRenameCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - profileRenameCmd.Flags().String("boot-loaders", "", "boot loader space delimited list (network installation boot loaders). Valid options for list items are: <>,grub,pxe,ipxe") - profileRenameCmd.Flags().String("distro", "", "the name of a previously defined Cobbler distribution. This value is required") - profileRenameCmd.Flags().String("comment", "", "free form text description") - profileRenameCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - profileRenameCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - profileRenameCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - profileRenameCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - profileRenameCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - profileRenameCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - profileRenameCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") profileRenameCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - profileRenameCmd.Flags().String("dhcp-tag", "", "DHCP tag (see manpage or leave blank)") - profileRenameCmd.Flags().Bool("enable-ipxe", false, "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)") - profileRenameCmd.Flags().Bool("enable-menu", false, "enable PXE Menu? (show this profile in the PXE menu?)") - profileRenameCmd.Flags().String("mgmt-parameters", "", "Parameters which will be handed to your management application (must be a valid YAML dictionary))") - profileRenameCmd.Flags().String("name-servers", "", "name servers (space delimited)") - profileRenameCmd.Flags().String("name-servers-search", "", "name servers search path (space delimited)") - profileRenameCmd.Flags().String("next-server-v4", "", "next server (IPv4) override (see manpage or leave blank)") - profileRenameCmd.Flags().String("next-server-v6", "", "next server (IPv6) override (see manpage or leave blank)") - profileRenameCmd.Flags().String("filename", "", "DHCP filename override (used to boot non-default bootloaders)") - profileRenameCmd.Flags().String("parent", "", "parent profile") - profileRenameCmd.Flags().String("proxy", "", "proxy server URL") - profileRenameCmd.Flags().String("server", "", "server override") - profileRenameCmd.Flags().String("menu", "", "parent boot menu") - profileRenameCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - profileRenameCmd.Flags().String("virt-bridge", "", "virt bridge") - profileRenameCmd.Flags().String("virt-cpus", "", "virt CPUs") - profileRenameCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - profileRenameCmd.Flags().String("virt-file-size", "", "virt file size in GB") - profileRenameCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - profileRenameCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - profileRenameCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)") // local flags for profile report profileReportCmd.Flags().String("name", "", "the profile name") diff --git a/cmd/replicate.go b/cmd/replicate.go index 88e4764..b19b28e 100644 --- a/cmd/replicate.go +++ b/cmd/replicate.go @@ -5,6 +5,8 @@ package cmd import ( + "fmt" + "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" ) @@ -18,9 +20,86 @@ relevant cobbler.conf and modules.conf, as these files are not synced. See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-replicate for more information.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient + distrosOption, err := cmd.Flags().GetString("distros") + if err != nil { + return err + } + profilesOption, err := cmd.Flags().GetString("profiles") + if err != nil { + return err + } + systemsOption, err := cmd.Flags().GetString("systems") + if err != nil { + return err + } + reposOption, err := cmd.Flags().GetString("repos") + if err != nil { + return err + } + imagesOption, err := cmd.Flags().GetString("image") + if err != nil { + return err + } + mgmtClassesOption, err := cmd.Flags().GetString("mgmtclasses") + if err != nil { + return err + } + packagesOption, err := cmd.Flags().GetString("packages") + if err != nil { + return err + } + filesOption, err := cmd.Flags().GetString("files") + if err != nil { + return err + } + portOption, err := cmd.Flags().GetString("port") + if err != nil { + return err + } + masterOption, err := cmd.Flags().GetString("master") + if err != nil { + return err + } + pruneOption, err := cmd.Flags().GetBool("prune") + if err != nil { + return err + } + omitDataOption, err := cmd.Flags().GetBool("omit-data") + if err != nil { + return err + } + syncAllOption, err := cmd.Flags().GetBool("sync-all") + if err != nil { + return err + } + useSslOption, err := cmd.Flags().GetBool("use-ssl") + if err != nil { + return err + } + replicateOptions := cobblerclient.ReplicateOptions{ + Master: masterOption, + Port: portOption, + DistroPatterns: distrosOption, + ProfilePatterns: profilesOption, + SystemPatterns: systemsOption, + RepoPatterns: reposOption, + Imagepatterns: imagesOption, + MgmtclassPatterns: mgmtClassesOption, + PackagePatterns: packagesOption, + FilePatterns: filesOption, + Prune: pruneOption, + OmitData: omitDataOption, + SyncAll: syncAllOption, + UseSsl: useSslOption, + } + eventId, err := Client.BackgroundReplicate(replicateOptions) + if err != nil { + return err + } + fmt.Printf("EventID: %s\n", eventId) + return nil }, } diff --git a/cmd/repo.go b/cmd/repo.go index 7df4e2d..4683629 100644 --- a/cmd/repo.go +++ b/cmd/repo.go @@ -6,15 +6,173 @@ package cmd import ( "fmt" - "os" - - "github.com/spf13/cobra" - cobbler "github.com/cobbler/cobblerclient" + "github.com/spf13/cobra" + "github.com/spf13/pflag" ) -var repo *cobbler.Repo //nolint:golint,unused -var repos []*cobbler.Repo +func updateRepoFromFlags(cmd *cobra.Command, repo *cobbler.Repo) error { + // TODO: in-place flag + // inPlace, err := cmd.Flags().GetBool("in-place") + _, err := cmd.Flags().GetBool("in-place") + if err != nil { + return err + } + cmd.Flags().Visit(func(flag *pflag.Flag) { + if err != nil { + // If one of the previous flags has had an error just directly return. + return + } + switch flag.Name { + case "comment": + var repoNewComment string + repoNewComment, err = cmd.Flags().GetString("comment") + if err != nil { + return + } + repo.Comment = repoNewComment + case "arch": + var repoNewArch string + repoNewArch, err = cmd.Flags().GetString("arch") + if err != nil { + return + } + repo.Arch = repoNewArch + case "breed": + var repoNewBreed string + repoNewBreed, err = cmd.Flags().GetString("breed") + if err != nil { + return + } + repo.Breed = repoNewBreed + case "owners": + fallthrough + case "owners-inherit": + if cmd.Flags().Lookup("owners-inherit").Changed { + repo.Owners.Data = []string{} + repo.Owners.IsInherited, err = cmd.Flags().GetBool("owners-inherit") + if err != nil { + return + } + } else { + var repoNewOwners []string + repoNewOwners, err = cmd.Flags().GetStringSlice("owners") + if err != nil { + return + } + repo.Owners.IsInherited = false + repo.Owners.Data = repoNewOwners + } + case "apt-components": + var repoNewAptComponents []string + repoNewAptComponents, err = cmd.Flags().GetStringSlice("apt-components") + if err != nil { + return + } + repo.AptComponents = repoNewAptComponents + case "apt-dists": + var repoNewAptDists []string + repoNewAptDists, err = cmd.Flags().GetStringSlice("apt-dists") + if err != nil { + return + } + repo.AptDists = repoNewAptDists + case "createrepo-flags": + fallthrough + case "createrepo-flags-inherit": + if cmd.Flags().Lookup("createrepo-flags-inherit").Changed { + repo.CreateRepoFlags.Data = "" + repo.CreateRepoFlags.IsInherited, err = cmd.Flags().GetBool("createrepo-flags-inherit") + if err != nil { + return + } + } else { + var repoNewCreatrepoFlags string + repoNewCreatrepoFlags, err = cmd.Flags().GetString("createrepo-flags") + if err != nil { + return + } + repo.CreateRepoFlags.IsInherited = false + repo.CreateRepoFlags.Data = repoNewCreatrepoFlags + } + case "environment": + var repoNewEnvironment map[string]string + repoNewEnvironment, err = cmd.Flags().GetStringToString("environment") + if err != nil { + return + } + repo.Environment = repoNewEnvironment + case "keep-updated": + var repoNewKeepUpdated bool + repoNewKeepUpdated, err = cmd.Flags().GetBool("keep-updated") + if err != nil { + return + } + repo.KeepUpdated = repoNewKeepUpdated + case "mirror": + var repoNewMirror string + repoNewMirror, err = cmd.Flags().GetString("mirror") + if err != nil { + return + } + repo.Mirror = repoNewMirror + case "mirror-type": + var repoNewMirrorType string + repoNewMirrorType, err = cmd.Flags().GetString("mirror-type") + if err != nil { + return + } + repo.MirrorType = repoNewMirrorType + case "priority": + var repoNewPriority int + repoNewPriority, err = cmd.Flags().GetInt("priority") + if err != nil { + return + } + repo.Priority = repoNewPriority + case "proxy": + fallthrough + case "proxy-inherit": + if cmd.Flags().Lookup("proxy-inherit").Changed { + repo.Proxy.Data = "" + repo.Proxy.IsInherited, err = cmd.Flags().GetBool("proxy-inherit") + if err != nil { + return + } + } else { + var repoNewProxy string + repoNewProxy, err = cmd.Flags().GetString("proxy") + if err != nil { + return + } + repo.Proxy.IsInherited = false + repo.Proxy.Data = repoNewProxy + } + case "rpm-list": + var repoNewRpmList []string + repoNewRpmList, err = cmd.Flags().GetStringSlice("rpm-list") + if err != nil { + return + } + repo.RpmList = repoNewRpmList + case "yumopts": + var repoNewYumOpts map[string]string + repoNewYumOpts, err = cmd.Flags().GetStringToString("yumopts") + if err != nil { + return + } + repo.YumOpts = repoNewYumOpts + case "rsyncopts": + var repoNewRsyncOpts map[string]string + repoNewRsyncOpts, err = cmd.Flags().GetStringToString("rsyncopts") + if err != nil { + return + } + repo.RsyncOpts = repoNewRsyncOpts + } + }) + return err +} // repoCmd represents the repo command var repoCmd = &cobra.Command{ @@ -23,7 +181,7 @@ var repoCmd = &cobra.Command{ Long: `Let you manage repositories. See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-repo for more information.`, Run: func(cmd *cobra.Command, args []string) { - cmd.Help() + _ = cmd.Help() }, } @@ -31,36 +189,28 @@ var repoAddCmd = &cobra.Command{ Use: "add", Short: "add repository", Long: `Adds a repository.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - var newRepo cobbler.Repo + newRepo := cobbler.NewRepo() + var err error // internal fields (ctime, mtime, depth, uid, parent, tree-build-time) cannot be modified - newRepo.AptComponents, _ = cmd.Flags().GetStringArray("apt-components") - newRepo.AptDists, _ = cmd.Flags().GetStringArray("apt-dists") - newRepo.Arch, _ = cmd.Flags().GetString("arch") - newRepo.Breed, _ = cmd.Flags().GetString("breed") - newRepo.Comment, _ = cmd.Flags().GetString("comment") - newRepo.CreateRepoFlags, _ = cmd.Flags().GetString("createrepo-flags") - newRepo.Environment, _ = cmd.Flags().GetStringArray("environment") - newRepo.KeepUpdated, _ = cmd.Flags().GetBool("keep-updated") - newRepo.Mirror, _ = cmd.Flags().GetString("mirror") - // not implemented in Cobbler yet - // newRepo.MirrorLocally, _ = cmd.Flags().GetBool("mirror-locally") - newRepo.Name, _ = cmd.Flags().GetString("name") - newRepo.Owners, _ = cmd.Flags().GetStringArray("owners") - newRepo.Proxy, _ = cmd.Flags().GetString("proxy") - newRepo.RpmList, _ = cmd.Flags().GetStringArray("rpm-list") - - repo, err = Client.CreateRepo(newRepo) - - if checkError(err) == nil { - fmt.Printf("Repo %s created", newRepo.Name) - } else { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + newRepo.Name, err = cmd.Flags().GetString("name") + if err != nil { + return err + } + // Update repo in-memory + err = updateRepoFromFlags(cmd, &newRepo) + if err != nil { + return err } + // Now create via XML-RPC + repo, err := Client.CreateRepo(newRepo) + if err != nil { + return err + } + fmt.Printf("Repo %s created\n", repo.Name) + return nil }, } @@ -68,11 +218,9 @@ var repoAutoAddCmd = &cobra.Command{ Use: "autoadd", Short: "add repository automatically", Long: `Automatically adds a repository.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return Client.AutoAddRepos() }, } @@ -80,11 +228,35 @@ var repoCopyCmd = &cobra.Command{ Use: "copy", Short: "copy repository", Long: `Copies a repository.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + repoName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + repoNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + + repoHandle, err := Client.GetRepoHandle(repoName) + if err != nil { + return err + } + err = Client.CopyRepo(repoHandle, repoNewName) + if err != nil { + return err + } + copiedRepo, err := Client.GetRepo(repoNewName, false, false) + if err != nil { + return err + } + err = updateRepoFromFlags(cmd, copiedRepo) + if err != nil { + return err + } + return Client.UpdateRepo(copiedRepo) }, } @@ -92,86 +264,26 @@ var repoEditCmd = &cobra.Command{ Use: "edit", Short: "edit repository", Long: `Edits a repository.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() // find repo through its name - rname, _ := cmd.Flags().GetString("name") - var updateRepo, err = Client.GetRepo(rname) - - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) - } - - // internal fields (ctime, mtime, depth, uid, parent, tree-build-time) cannot be modified - var tmpArgs, _ = cmd.Flags().GetString("apt-components") - if tmpArgs != "" { - updateRepo.AptDists, _ = cmd.Flags().GetStringArray("apt-components") - } - tmpArgs, _ = cmd.Flags().GetString("apt-dists") - if tmpArgs != "" { - updateRepo.AptDists, _ = cmd.Flags().GetStringArray("apt-dists") - } - tmpArgs, _ = cmd.Flags().GetString("arch") - if tmpArgs != "" { - updateRepo.Arch, _ = cmd.Flags().GetString("arch") - } - tmpArgs, _ = cmd.Flags().GetString("breed") - if tmpArgs != "" { - updateRepo.Breed, _ = cmd.Flags().GetString("breed") - } - tmpArgs, _ = cmd.Flags().GetString("comment") - if tmpArgs != "" { - updateRepo.Comment, _ = cmd.Flags().GetString("comment") + rname, err := cmd.Flags().GetString("name") + if err != nil { + return err } - tmpArgs, _ = cmd.Flags().GetString("createrepo-flags") - if tmpArgs != "" { - updateRepo.CreateRepoFlags, _ = cmd.Flags().GetString("createrepo-flags") + // Get repo from API + updateRepo, err := Client.GetRepo(rname, false, false) + if err != nil { + return err } - var tmpArgsArray, _ = cmd.Flags().GetStringArray("environment") - if len(tmpArgsArray) > 0 { - updateRepo.Environment, _ = cmd.Flags().GetStringArray("environment") - } - // TODO - /* tmpArgs, _ = cmd.Flags().GetBool("keep-updated") - if tmpArgs != "" { - updateRepo.KeepUpdated, _ = cmd.Flags().GetBool("keep-updated") - } - */ - tmpArgs, _ = cmd.Flags().GetString("mirror") - if tmpArgs != "" { - updateRepo.Mirror, _ = cmd.Flags().GetString("mirror") - } - // not implemented in Cobbler yet - /* tmpArgs, _ = cmd.Flags().GetBool("mirror-locally") - if tmpArgs != "" { - updateRepo.KeepUpdated, _ = cmd.Flags().GetBool("mirror-locally") - } - */ - tmpArgs, _ = cmd.Flags().GetString("name") - if tmpArgs != "" { - updateRepo.Name, _ = cmd.Flags().GetString("name") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("owners") - if len(tmpArgsArray) > 0 { - updateRepo.Owners, _ = cmd.Flags().GetStringArray("owners") - } - tmpArgs, _ = cmd.Flags().GetString("proxy") - if tmpArgs != "" { - updateRepo.Proxy, _ = cmd.Flags().GetString("proxy") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("rpm-list") - if len(tmpArgsArray) > 0 { - updateRepo.RpmList, _ = cmd.Flags().GetStringArray("rpm-list") - } - - err = Client.UpdateRepo(updateRepo) - - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + // Update repo in-memory + err = updateRepoFromFlags(cmd, updateRepo) + if err != nil { + return err } + // Update repo via XML-RPC + return Client.UpdateRepo(updateRepo) }, } @@ -179,11 +291,9 @@ var repoFindCmd = &cobra.Command{ Use: "find", Short: "find repository", Long: `Finds a given repository.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return FindItemNames(cmd, args, "repo") }, } @@ -191,16 +301,14 @@ var repoListCmd = &cobra.Command{ Use: "list", Short: "list all repositorys", Long: `Lists all available repositories.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - repos, err = Client.GetRepos() - - if checkError(err) == nil { - fmt.Println(repos) - } else { - fmt.Fprintln(os.Stderr, err.Error()) + repoNames, err := Client.ListRepoNames() + if err != nil { + return err } + listItems("repos", repoNames) + return nil }, } @@ -208,14 +316,9 @@ var repoRemoveCmd = &cobra.Command{ Use: "remove", Short: "remove repository", Long: `Removes a given repository.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - rname, _ := cmd.Flags().GetString("name") - err := Client.DeleteRepo(rname) - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - } + return RemoveItemRecursive(cmd, args, "repo") }, } @@ -223,23 +326,75 @@ var repoRenameCmd = &cobra.Command{ Use: "rename", Short: "rename repository", Long: `Renames a given repository.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get special name and newname flags + repoName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + repoNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + // Get repo handle from the API + repoHandle, err := Client.GetMenuHandle(repoName) + if err != nil { + return err + } + // Rename the repo server side + err = Client.RenameRepo(repoHandle, repoNewName) + if err != nil { + return err + } + // Get the renamed repository from the API + newRepository, err := Client.GetRepo(repoNewName, false, false) + if err != nil { + return err + } + // Update the repo in-memory + err = updateRepoFromFlags(cmd, newRepository) + if err != nil { + return err + } + // Update the repo via XML-RPC + return Client.UpdateRepo(newRepository) }, } +func reportRepos(repoNames []string) error { + for _, itemName := range repoNames { + repo, err := Client.GetRepo(itemName, false, false) + if err != nil { + return err + } + printStructured(repo) + fmt.Println("") + } + return nil +} + var repoReportCmd = &cobra.Command{ Use: "report", Short: "list all repositorys in detail", Long: `Shows detailed information about all repositories.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + name, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + itemNames := make([]string, 0) + if name == "" { + itemNames, err = Client.ListRepoNames() + if err != nil { + return err + } + } else { + itemNames = append(itemNames, name) + } + return reportRepos(itemNames) }, } @@ -256,135 +411,62 @@ func init() { repoCmd.AddCommand(repoReportCmd) // local flags for repo add - repoAddCmd.Flags().String("name", "", "the repo name") - repoAddCmd.Flags().String("arch", "", "Architecture") - repoAddCmd.Flags().String("breed", "", "Breed (valid options: none,rsync,rhn,yum,apt,wget)") - repoAddCmd.Flags().String("comment", "", "free form text description") - repoAddCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - repoAddCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - repoAddCmd.Flags().String("apt-components", "", "APT components (e.g. main restricted universe)") - repoAddCmd.Flags().String("apt-dists", "", "APT dist names (e.g. precise,bullseye,buster)") - repoAddCmd.Flags().String("createrepo-flags", "", "flags to use with createrepo") - repoAddCmd.Flags().String("environment", "", "environment variables (use these environment variables during commands (key=value, space delimited)") - repoAddCmd.Flags().Bool("keep-updated", false, "update this repo on next 'cobbler reposync'?") - repoAddCmd.Flags().String("mirror", "", "address of yum or rsync repo to mirror") - repoAddCmd.Flags().String("mirror-type", "", "mirror type. Valid options: metalink,mirrorlist,baseurl)") - repoAddCmd.Flags().String("priority", "", "value for yum priorities plugin, if installed") - repoAddCmd.Flags().String("proxy", "", "proxy URL (<> to use proxy_url_ext from settings, blank or <> for no proxy)") - repoAddCmd.Flags().String("rpm-list", "", "mirror just these RPMs (yum only)") - repoAddCmd.Flags().String("yumopts", "", "options to write to yum config file") - repoAddCmd.Flags().String("rsyncopts", "", "options to use with rsync repo") + addCommonArgs(repoAddCmd) + addStringFlags(repoAddCmd, repoStringFlagMetadata) + addBoolFlags(repoAddCmd, repoBoolFlagMetadata) + addIntFlags(repoAddCmd, repoIntFlagMetadata) + addStringSliceFlags(repoAddCmd, repoStringSliceFlagMetadata) + addMapFlags(repoAddCmd, repoMapFlagMetadata) // local flags for repo autoadd - repoAutoAddCmd.Flags().String("name", "", "the repo name") - repoAutoAddCmd.Flags().String("arch", "", "Architecture") - repoAutoAddCmd.Flags().String("breed", "", "Breed (valid options: none,rsync,rhn,yum,apt,wget)") - repoAutoAddCmd.Flags().String("comment", "", "free form text description") - repoAutoAddCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - repoAutoAddCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - repoAutoAddCmd.Flags().String("apt-components", "", "APT components (e.g. main restricted universe)") - repoAutoAddCmd.Flags().String("apt-dists", "", "APT dist names (e.g. precise,bullseye,buster)") - repoAutoAddCmd.Flags().String("createrepo-flags", "", "flags to use with createrepo") - repoAutoAddCmd.Flags().String("environment", "", "environment variables (use these environment variables during commands (key=value, space delimited)") - repoAutoAddCmd.Flags().Bool("keep-updated", false, "update this repo on next 'cobbler reposync'?") - repoAutoAddCmd.Flags().String("mirror", "", "address of yum or rsync repo to mirror") - repoAutoAddCmd.Flags().String("mirror-type", "", "mirror type. Valid options: metalink,mirrorlist,baseurl)") - repoAutoAddCmd.Flags().String("priority", "", "value for yum priorities plugin, if installed") - repoAutoAddCmd.Flags().String("proxy", "", "proxy URL (<> to use proxy_url_ext from settings, blank or <> for no proxy)") - repoAutoAddCmd.Flags().String("rpm-list", "", "mirror just these RPMs (yum only)") - repoAutoAddCmd.Flags().String("yumopts", "", "options to write to yum config file") - repoAutoAddCmd.Flags().String("rsyncopts", "", "options to use with rsync repo") + // no flags // local flags for repo copy - repoCopyCmd.Flags().String("name", "", "the repo name") + addCommonArgs(repoCopyCmd) + addStringFlags(repoCopyCmd, repoStringFlagMetadata) + addBoolFlags(repoCopyCmd, repoBoolFlagMetadata) + addIntFlags(repoCopyCmd, repoIntFlagMetadata) + addStringSliceFlags(repoCopyCmd, repoStringSliceFlagMetadata) + addMapFlags(repoCopyCmd, repoMapFlagMetadata) repoCopyCmd.Flags().String("newname", "", "the new repo name") - repoCopyCmd.Flags().String("arch", "", "Architecture") - repoCopyCmd.Flags().String("breed", "", "Breed (valid options: none,rsync,rhn,yum,apt,wget)") - repoCopyCmd.Flags().String("comment", "", "free form text description") - repoCopyCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") repoCopyCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - repoCopyCmd.Flags().String("apt-components", "", "APT components (e.g. main restricted universe)") - repoCopyCmd.Flags().String("apt-dists", "", "APT dist names (e.g. precise,bullseye,buster)") - repoCopyCmd.Flags().String("createrepo-flags", "", "flags to use with createrepo") - repoCopyCmd.Flags().String("environment", "", "environment variables (use these environment variables during commands (key=value, space delimited)") - repoCopyCmd.Flags().Bool("keep-updated", false, "update this repo on next 'cobbler reposync'?") - repoCopyCmd.Flags().String("mirror", "", "address of yum or rsync repo to mirror") - repoCopyCmd.Flags().String("mirror-type", "", "mirror type. Valid options: metalink,mirrorlist,baseurl)") - repoCopyCmd.Flags().String("priority", "", "value for yum priorities plugin, if installed") - repoCopyCmd.Flags().String("proxy", "", "proxy URL (<> to use proxy_url_ext from settings, blank or <> for no proxy)") - repoCopyCmd.Flags().String("rpm-list", "", "mirror just these RPMs (yum only)") - repoCopyCmd.Flags().String("yumopts", "", "options to write to yum config file") - repoCopyCmd.Flags().String("rsyncopts", "", "options to use with rsync repo") // local flags for repo edit - repoEditCmd.Flags().String("name", "", "the repo name") - repoEditCmd.Flags().String("arch", "", "Architecture") - repoEditCmd.Flags().String("breed", "", "Breed (valid options: none,rsync,rhn,yum,apt,wget)") - repoEditCmd.Flags().String("comment", "", "free form text description") - repoEditCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") + addCommonArgs(repoEditCmd) + addStringFlags(repoEditCmd, repoStringFlagMetadata) + addBoolFlags(repoEditCmd, repoBoolFlagMetadata) + addIntFlags(repoEditCmd, repoIntFlagMetadata) + addStringSliceFlags(repoEditCmd, repoStringSliceFlagMetadata) + addMapFlags(repoEditCmd, repoMapFlagMetadata) repoEditCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - repoEditCmd.Flags().String("apt-components", "", "APT components (e.g. main restricted universe)") - repoEditCmd.Flags().String("apt-dists", "", "APT dist names (e.g. precise,bullseye,buster)") - repoEditCmd.Flags().String("createrepo-flags", "", "flags to use with createrepo") - repoEditCmd.Flags().String("environment", "", "environment variables (use these environment variables during commands (key=value, space delimited)") - repoEditCmd.Flags().Bool("keep-updated", false, "update this repo on next 'cobbler reposync'?") - repoEditCmd.Flags().String("mirror", "", "address of yum or rsync repo to mirror") - repoEditCmd.Flags().String("mirror-type", "", "mirror type. Valid options: metalink,mirrorlist,baseurl)") - repoEditCmd.Flags().String("priority", "", "value for yum priorities plugin, if installed") - repoEditCmd.Flags().String("proxy", "", "proxy URL (<> to use proxy_url_ext from settings, blank or <> for no proxy)") - repoEditCmd.Flags().String("rpm-list", "", "mirror just these RPMs (yum only)") - repoEditCmd.Flags().String("yumopts", "", "options to write to yum config file") - repoEditCmd.Flags().String("rsyncopts", "", "options to use with rsync repo") // local flags for repo find - repoFindCmd.Flags().String("name", "", "the repo name") + addCommonArgs(repoFindCmd) + addStringFlags(repoFindCmd, repoStringFlagMetadata) + addBoolFlags(repoFindCmd, repoBoolFlagMetadata) + addIntFlags(repoFindCmd, repoIntFlagMetadata) + addStringSliceFlags(repoFindCmd, repoStringSliceFlagMetadata) + addMapFlags(repoFindCmd, repoMapFlagMetadata) repoFindCmd.Flags().String("ctime", "", "") repoFindCmd.Flags().String("depth", "", "") repoFindCmd.Flags().String("mtime", "", "") repoFindCmd.Flags().String("uid", "", "UID") - repoFindCmd.Flags().String("arch", "", "Architecture") - repoFindCmd.Flags().String("breed", "", "Breed (valid options: none,rsync,rhn,yum,apt,wget)") - repoFindCmd.Flags().String("comment", "", "free form text description") - repoFindCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") repoFindCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") repoFindCmd.Flags().String("parent", "", "") - repoFindCmd.Flags().String("apt-components", "", "APT components (e.g. main restricted universe)") - repoFindCmd.Flags().String("apt-dists", "", "APT dist names (e.g. precise,bullseye,buster)") - repoFindCmd.Flags().String("createrepo-flags", "", "flags to use with createrepo") - repoFindCmd.Flags().String("environment", "", "environment variables (use these environment variables during commands (key=value, space delimited)") - repoFindCmd.Flags().Bool("keep-updated", false, "update this repo on next 'cobbler reposync'?") - repoFindCmd.Flags().String("mirror", "", "address of yum or rsync repo to mirror") - repoFindCmd.Flags().String("mirror-type", "", "mirror type. Valid options: metalink,mirrorlist,baseurl)") - repoFindCmd.Flags().String("priority", "", "value for yum priorities plugin, if installed") - repoFindCmd.Flags().String("proxy", "", "proxy URL (<> to use proxy_url_ext from settings, blank or <> for no proxy)") - repoFindCmd.Flags().String("rpm-list", "", "mirror just these RPMs (yum only)") - repoFindCmd.Flags().String("yumopts", "", "options to write to yum config file") - repoFindCmd.Flags().String("rsyncopts", "", "options to use with rsync repo") // local flags for repo remove repoRemoveCmd.Flags().String("name", "", "the repo name") repoRemoveCmd.Flags().Bool("recursive", false, "also delete child objects") // local flags for repo rename - repoRenameCmd.Flags().String("name", "", "the repo name") + addCommonArgs(repoRenameCmd) + addStringFlags(repoRenameCmd, repoStringFlagMetadata) + addBoolFlags(repoRenameCmd, repoBoolFlagMetadata) + addIntFlags(repoRenameCmd, repoIntFlagMetadata) + addStringSliceFlags(repoRenameCmd, repoStringSliceFlagMetadata) + addMapFlags(repoRenameCmd, repoMapFlagMetadata) repoRenameCmd.Flags().String("newname", "", "the new repo name") - repoRenameCmd.Flags().String("arch", "", "Architecture") - repoRenameCmd.Flags().String("breed", "", "Breed (valid options: none,rsync,rhn,yum,apt,wget)") - repoRenameCmd.Flags().String("comment", "", "free form text description") - repoRenameCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") repoRenameCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - repoRenameCmd.Flags().String("apt-components", "", "APT components (e.g. main restricted universe)") - repoRenameCmd.Flags().String("apt-dists", "", "APT dist names (e.g. precise,bullseye,buster)") - repoRenameCmd.Flags().String("createrepo-flags", "", "flags to use with createrepo") - repoRenameCmd.Flags().String("environment", "", "environment variables (use these environment variables during commands (key=value, space delimited)") - repoRenameCmd.Flags().Bool("keep-updated", false, "update this repo on next 'cobbler reposync'?") - repoRenameCmd.Flags().String("mirror", "", "address of yum or rsync repo to mirror") - repoRenameCmd.Flags().String("mirror-type", "", "mirror type. Valid options: metalink,mirrorlist,baseurl)") - repoRenameCmd.Flags().String("priority", "", "value for yum priorities plugin, if installed") - repoRenameCmd.Flags().String("proxy", "", "proxy URL (<> to use proxy_url_ext from settings, blank or <> for no proxy)") - repoRenameCmd.Flags().String("rpm-list", "", "mirror just these RPMs (yum only)") - repoRenameCmd.Flags().String("yumopts", "", "options to write to yum config file") - repoRenameCmd.Flags().String("rsyncopts", "", "options to use with rsync repo") // local flags for repo report repoReportCmd.Flags().String("name", "", "the repo name") diff --git a/cmd/report.go b/cmd/report.go index 42868b3..f404642 100644 --- a/cmd/report.go +++ b/cmd/report.go @@ -5,6 +5,7 @@ package cmd import ( + "fmt" "github.com/spf13/cobra" ) @@ -16,10 +17,125 @@ var reportCmd = &cobra.Command{ most of the other Cobbler commands (currently: distro, profile, system, repo, image, mgmtclass, package, file, menu). Identical to 'cobbler list'`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + // Distro + fmt.Println("distros:") + fmt.Println("==========") + distroNames, err := Client.ListDistroNames() + if err != nil { + return err + } + err = reportDistros(distroNames) + if err != nil { + return err + } + fmt.Println("") - // TODO: call cobblerclient + // Profile + fmt.Println("profiles:") + fmt.Println("==========") + profileNames, err := Client.ListProfileNames() + if err != nil { + return err + } + err = reportProfiles(profileNames) + if err != nil { + return err + } + fmt.Println("") + + // System + fmt.Println("systems:") + fmt.Println("==========") + systemNames, err := Client.ListSystemNames() + if err != nil { + return err + } + err = reportSystems(systemNames) + if err != nil { + return err + } + fmt.Println("") + + // Repository + fmt.Println("repos:") + fmt.Println("==========") + repoNames, err := Client.ListRepoNames() + if err != nil { + return err + } + err = reportRepos(repoNames) + if err != nil { + return err + } + fmt.Println("") + + // Image + fmt.Println("images:") + fmt.Println("==========") + imageNames, err := Client.ListImageNames() + if err != nil { + return err + } + err = reportImages(imageNames) + if err != nil { + return err + } + fmt.Println("") + + // Mgmtclass + fmt.Println("mgmtclasses:") + fmt.Println("==========") + mgmtClassNames, err := Client.ListMgmtClassNames() + if err != nil { + return err + } + err = reportMgmtClasses(mgmtClassNames) + if err != nil { + return err + } + fmt.Println("") + + // Package + fmt.Println("packages:") + fmt.Println("==========") + packageNames, err := Client.ListPackageNames() + if err != nil { + return err + } + err = reportPackages(packageNames) + if err != nil { + return err + } + fmt.Println("") + + // File + fmt.Println("files:") + fmt.Println("==========") + fileNames, err := Client.ListFileNames() + if err != nil { + return err + } + err = reportFiles(fileNames) + if err != nil { + return err + } + fmt.Println("") + + // Menu + fmt.Println("menus:") + fmt.Println("==========") + menuNames, err := Client.ListMenuNames() + if err != nil { + return err + } + err = reportMenus(menuNames) + if err != nil { + return err + } + fmt.Println("") + return nil }, } diff --git a/cmd/reposync.go b/cmd/reposync.go index 7a89c90..1cef32a 100644 --- a/cmd/reposync.go +++ b/cmd/reposync.go @@ -5,6 +5,8 @@ package cmd import ( + "fmt" + "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" ) @@ -16,10 +18,32 @@ var reposyncCmd = &cobra.Command{ See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-reposync for more information.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient + noFailOption, err := cmd.Flags().GetBool("no-fail") + if err != nil { + return err + } + onlyOption, err := cmd.Flags().GetString("only") + if err != nil { + return err + } + triesOption, err := cmd.Flags().GetInt("tries") + if err != nil { + return err + } + var reposyncOptions = cobblerclient.BackgroundReposyncOptions{ + Repos: make([]string, 0), + Only: onlyOption, + Nofail: noFailOption, + Tries: triesOption, + } + eventId, err := Client.BackgroundReposync(reposyncOptions) + if err != nil { + return err + } + fmt.Printf("Event ID: %s\n", eventId) + return nil }, } @@ -29,5 +53,5 @@ func init() { //local flags reposyncCmd.Flags().Bool("no-fail", false, "do not stop reposyncing if a failure occurs") reposyncCmd.Flags().String("only", "", "update only this repository name") - reposyncCmd.Flags().String("tries", "", "try each repo this many times") + reposyncCmd.Flags().Int("tries", 3, "try each repo this many times") } diff --git a/cmd/root.go b/cmd/root.go index 807e345..32d8166 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,13 +5,14 @@ package cmd import ( + "encoding/json" "fmt" - "net/http" - "os" - cobbler "github.com/cobbler/cobblerclient" - "github.com/spf13/cobra" + "net/http" + "os" + "reflect" + "strings" "github.com/spf13/viper" ) @@ -20,7 +21,7 @@ var cfgFile string var Client cobbler.Client var conf cobbler.ClientConfig var httpClient = &http.Client{} -var err error +var verbose bool // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ @@ -28,7 +29,7 @@ var rootCmd = &cobra.Command{ Short: "Cobbler CLI client", Long: "An independent CLI to manage a Cobbler server.", Run: func(cmd *cobra.Command, args []string) { - cmd.Help() + _ = cmd.Help() }, } @@ -43,10 +44,17 @@ func init() { // global flags rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobbler.yaml)") + rootCmd.Flags().BoolVar(&verbose, "verbose", false, "Whether or not to print debug messages from the CLI.") } // initConfig reads in config file and ENV variables if set. func initConfig() { + // Set defaults + viper.SetDefault("server_url", "http://127.0.0.1/cobbler_api") + viper.SetDefault("server_username", "cobbler") + viper.SetDefault("server_password", "cobbler") + + // Read config file if cfgFile != "" { // Use config file from the flag. viper.SetConfigFile(cfgFile) @@ -66,15 +74,9 @@ func initConfig() { // If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { // TODO: Do we need the output what config file is used? - fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) - } -} - -func checkError(err error) error { - if err != nil { - return err - } else { - return nil + if verbose { + _, _ = fmt.Fprintln(os.Stdout, "Using config file:", viper.ConfigFileUsed()) + } } } @@ -90,12 +92,122 @@ func generateCobblerClient() { login, err := Client.Login() if !login || err != nil { - fmt.Fprintln(os.Stderr, fmt.Errorf("error! Failed to login: %s", err)) + _, _ = fmt.Fprintln(os.Stderr, fmt.Errorf("error! Failed to login: %s", err)) } } // simply prints a message about functions not implemented in the cobblerclient library func notImplemented() { - fmt.Fprintln(os.Stderr, fmt.Errorf(`error! Not yet implemented in the cobblerclient library + _, _ = fmt.Fprintln(os.Stderr, fmt.Errorf(`error! Not yet implemented in the cobblerclient library See https://github.com/cobbler/cobblerclient/issues/4`)) } + +func printStructured(dataStruct interface{}) { + s := reflect.ValueOf(dataStruct).Elem() + typeOfT := s.Type() + + for i := 0; i < s.NumField(); i++ { + f := s.Field(i) + mapstructureTag := typeOfT.Field(i).Tag.Get("mapstructure") + fieldName := typeOfT.Field(i).Name + fieldStructName := typeOfT.Field(i).Type.String() + if strings.HasPrefix(fieldStructName, "cobblerclient.Value") { + printValueStructured(mapstructureTag, f) + continue + } + if fieldName == "Item" { + baseItem := f.Interface().(cobbler.Item) + printStructured(&baseItem) + continue + } + if fieldName == "Interfaces" { + baseItem := f.Interface().(map[string]interface{}) + printNetworkInterface(baseItem) + continue + } + if fieldName == "Client" { + continue + } + if fieldName == "Meta" { + continue + } + printField(f.Kind(), mapstructureTag, f.Interface()) + } +} + +func printValueStructured(name string, value reflect.Value) { + isInherited := value.FieldByName("IsInherited").Bool() + data := value.FieldByName("Data").Interface() + if isInherited { + printField(reflect.String, name, "<>") + } else { + dataType := value.FieldByName("Data").Kind() + printField(dataType, name, data) + } +} + +func printInterfaceStructured(data interface{}) { + v, ok := data.(map[string]interface{}) + if !ok { + panic("Cast of interface to map unsuccessful during structured fmt.Printf!") + } + for key, value := range v { + printField(reflect.ValueOf(value).Kind(), key, value) + } +} + +func printNetworkInterface(networkInterface map[string]interface{}) { + for interfaceName, interfaceStruct := range networkInterface { + fmt.Printf("%-40s: %s\n", "Interface =====", interfaceName) + printInterfaceStructured(interfaceStruct) + } +} + +func printField(valueType reflect.Kind, name string, value interface{}) { + if name == "ctime" || name == "mtime" { + time, err := covertFloatToUtcTime(value.(float64)) + if err == nil { + // If there is an error just show the float + fmt.Printf("%-40s: %s\n", name, time) + return + } + } + switch valueType { + case reflect.Bool: + fmt.Printf("%-40s: %t\n", name, value.(bool)) + case reflect.Int64: + fmt.Printf("%-40s: %d\n", name, value.(int64)) + case reflect.Int32: + fmt.Printf("%-40s: %d\n", name, value.(int32)) + case reflect.Int16: + fmt.Printf("%-40s: %d\n", name, value.(int16)) + case reflect.Int8: + fmt.Printf("%-40s: %d\n", name, value.(int8)) + case reflect.Int: + fmt.Printf("%-40s: %d\n", name, value.(int)) + case reflect.Float32: + fmt.Printf("%-40s: %f\n", name, value.(float32)) + case reflect.Float64: + fmt.Printf("%-40s: %f\n", name, value.(float64)) + case reflect.Map: + res2B, _ := json.Marshal(value) + fmt.Printf("%-40s: %s\n", name, string(res2B)) + case reflect.Array, reflect.Slice: + arr := reflect.ValueOf(value) + fmt.Printf("%-40s: [", name) + for i := 0; i < arr.Len(); i++ { + if i+1 != arr.Len() { + fmt.Printf("'%v', ", arr.Index(i).Interface()) + } else { + fmt.Printf("'%v'", arr.Index(i).Interface()) + } + } + fmt.Printf("]\n") + default: + if value == nil { + value = "" + } + fmt.Printf("%-40s: %s\n", name, value) + // fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface()) + } +} diff --git a/cmd/setting.go b/cmd/setting.go index 71ca8b9..5418b3e 100644 --- a/cmd/setting.go +++ b/cmd/setting.go @@ -5,7 +5,9 @@ package cmd import ( + "fmt" "github.com/spf13/cobra" + "os" ) // settingCmd represents the setting command @@ -14,7 +16,7 @@ var settingCmd = &cobra.Command{ Short: "Settings management", Long: `Let you manage settings.`, Run: func(cmd *cobra.Command, args []string) { - cmd.Help() + _ = cmd.Help() }, } @@ -22,11 +24,36 @@ var settingEditCmd = &cobra.Command{ Use: "edit", Short: "edit settings", Long: `Edits the settings.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + settings, err := Client.GetSettings() + if err != nil { + return err + } + if !settings.AllowDynamicSettings { + fmt.Println("Dynamic settings are turned off server-side!") + os.Exit(1) + } + + settingName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + settingValue, err := cmd.Flags().GetString("value") + if err != nil { + return err + } + result, err := Client.ModifySetting(settingName, settingValue) + if err != nil { + return err + } + if result == 0 { + fmt.Println("Successfully updated!") + } else { + fmt.Println("Updating settings failed!") + } + return nil }, } @@ -34,11 +61,15 @@ var settingReportCmd = &cobra.Command{ Use: "report", Short: "list settings", Long: `Prints settings to stdout.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + settings, err := Client.GetSettings() + if err != nil { + return err + } - // TODO: call cobblerclient - notImplemented() + printStructured(settings) + return nil }, } diff --git a/cmd/signature.go b/cmd/signature.go index a449267..31053c8 100644 --- a/cmd/signature.go +++ b/cmd/signature.go @@ -5,7 +5,9 @@ package cmd import ( + "fmt" "github.com/spf13/cobra" + "sort" ) // signatureCmd represents the signature command @@ -13,20 +15,87 @@ var signatureCmd = &cobra.Command{ Use: "signature", Short: "Signature management", Long: `Reloads, reports or updates the signatures of the distinct operating system versions.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Please use one of the sub commands!") + _ = cmd.Help() + }, +} + +var signatureReportCmd = &cobra.Command{ + Use: "report", + Short: "Report the loaded signatures", + Long: `Report the loaded signatures`, + RunE: func(cmd *cobra.Command, args []string) error { + generateCobblerClient() + + // Get signatures + signatures, err := Client.GetSignatures() + if err != nil { + return err + } + + if len(signatures.Breeds) > 0 { + // Counters + var totalOsVersions int + + // Print signatures + fmt.Println("Currently loaded signatures") + breedNameList := make([]string, 0, len(signatures.Breeds)) + for key := range signatures.Breeds { + breedNameList = append(breedNameList, key) + } + sort.Strings(breedNameList) + for _, breedName := range breedNameList { + fmt.Println(breedName) + totalOsVersions += len(signatures.Breeds[breedName]) + if len(signatures.Breeds[breedName]) > 0 { + osVersionNameList := make([]string, 0, len(signatures.Breeds[breedName])) + for key := range signatures.Breeds[breedName] { + osVersionNameList = append(osVersionNameList, key) + } + sort.Strings(osVersionNameList) + for _, versionName := range osVersionNameList { + fmt.Printf("\t%s\n", versionName) + } + } else { + fmt.Println("\t(none)") + } + + } + fmt.Printf("\n%d breeds with %d total OS versions loaded\n", len(signatures.Breeds), totalOsVersions) + } else { + fmt.Println("No breeds found in the signature, a signature update is recommended") + } + return nil + }, +} + +var signatureUpdateCmd = &cobra.Command{ + Use: "update", + Short: "Update the signatures JSON file", + Long: `Retrieve an up-to-date "distro_signatures.json" file from the server-side configured webservice.`, Run: func(cmd *cobra.Command, args []string) { generateCobblerClient() + eventId, _ := Client.BackgroundSignatureUpdate() + fmt.Printf("Event ID: %s\n", eventId) + }, +} - // cmd.Help() this should be used once the library is implemented - // TODO: call cobblerclient +var signatureReloadCmd = &cobra.Command{ + Use: "reload", + Short: "Reloads signatures", + Long: `Reloads signatures from the - on the server - local "distro_signatures.json" file.`, + Run: func(cmd *cobra.Command, args []string) { + generateCobblerClient() + + // FIXME: Dependant on https://github.com/cobbler/cobbler/issues/3791 notImplemented() }, } func init() { + signatureCmd.AddCommand(signatureReloadCmd) + signatureCmd.AddCommand(signatureReportCmd) + signatureCmd.AddCommand(signatureUpdateCmd) rootCmd.AddCommand(signatureCmd) - - // local flags - signatureCmd.Flags().Bool("reload", false, "reload the signatures file") - signatureCmd.Flags().Bool("report", false, "list the currently loaded signatures") - signatureCmd.Flags().Bool("update", false, "update the signatures file") } diff --git a/cmd/sync.go b/cmd/sync.go index 20d33b9..9f7ce54 100644 --- a/cmd/sync.go +++ b/cmd/sync.go @@ -5,6 +5,8 @@ package cmd import ( + "fmt" + "github.com/cobbler/cobblerclient" "github.com/spf13/cobra" ) @@ -17,11 +19,46 @@ services. It is used to repair or rebuild the contents of '/tftpboot' or '/var/w changed behind the scenes. It brings the filesystem up to date with the configuration. See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-sync for more information.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + dhcpOption, err := cmd.Flags().GetBool("dhcp") + if err != nil { + return err + } + dnsOption, err := cmd.Flags().GetBool("dns") + if err != nil { + return err + } + verboseOption, err := cmd.Flags().GetBool("verbose") + if err != nil { + return err + } + systemsOption, err := cmd.Flags().GetStringSlice("systems") + if err != nil { + return err + } - // not fully implemented in the cobblerclient library. You cannot use flags at the moment! - Client.Sync() + var eventId string + if len(systemsOption) > 0 { + backgroundSyncSystemsOptions := cobblerclient.BackgroundSyncSystemsOptions{ + Systems: systemsOption, + Verbose: verboseOption, + } + eventId, err = Client.BackgroundSyncSystems(backgroundSyncSystemsOptions) + } else { + backgroundSyncOptions := cobblerclient.BackgroundSyncOptions{ + Dhcp: dhcpOption, + Dns: dnsOption, + Verbose: verboseOption, + } + eventId, err = Client.BackgroundSync(backgroundSyncOptions) + } + + if err != nil { + return err + } + fmt.Printf("Event ID: %s\n", eventId) + return nil }, } @@ -31,6 +68,8 @@ func init() { //local flags syncCmd.Flags().Bool("dhcp", false, "write DHCP config files and restart service") syncCmd.Flags().Bool("dns", false, "write DNS config files and restart service") - syncCmd.Flags().String("systems", "", "run a sync only on specified systems") + syncCmd.Flags().StringSlice("systems", []string{}, "run a sync only on specified systems") syncCmd.Flags().Bool("verbose", false, "more verbose output") + syncCmd.MarkFlagsMutuallyExclusive("dhcp", "systems") + syncCmd.MarkFlagsMutuallyExclusive("dns", "systems") } diff --git a/cmd/system.go b/cmd/system.go index bbac061..75a3f54 100644 --- a/cmd/system.go +++ b/cmd/system.go @@ -5,17 +5,701 @@ package cmd import ( + "encoding/json" "fmt" - "os" - "github.com/spf13/cobra" + "github.com/spf13/pflag" + "os" + "reflect" cobbler "github.com/cobbler/cobblerclient" ) -var system *cobbler.System //nolint:golint,unused -var systems []*cobbler.System -var iface cobbler.Interface +func updateSystemFromFlags(cmd *cobra.Command, system *cobbler.System) error { + // TODO: Implementation for more interfaces + // See https://github.com/cobbler/cli/issues/38 + systemNewInterface, err := cmd.Flags().GetString("interface") + if err != nil { + return err + } + systemInterface, keyInMap := system.Interfaces[systemNewInterface] + if !keyInMap { + // Interface doesn't exist, so add a new one. + // We cannot call CreateInterface because the system might not exist. + system.Interfaces[systemNewInterface] = cobbler.Interface{} + systemInterface = system.Interfaces[systemNewInterface] + } + cmd.Flags().Visit(func(flag *pflag.Flag) { + if err != nil { + // If one of the previous flags has had an error just directly return. + return + } + switch flag.Name { + // The rename & copy operations are special operations as such we cannot blindly set this inside here. + // Any rename & copy operation must be handled outside of this method. + case "autoinstall": + var systemNewAutoinstall string + systemNewAutoinstall, err = cmd.Flags().GetString("autoinstall") + if err != nil { + return + } + system.Autoinstall = systemNewAutoinstall + case "autoinstall-meta": + fallthrough + case "autoinstall-meta-inherit": + if cmd.Flags().Lookup("autoinstall-meta-inherit").Changed { + system.AutoinstallMeta.Data = make(map[string]interface{}) + system.AutoinstallMeta.IsInherited, err = cmd.Flags().GetBool("autoinstall-meta-inherit") + if err != nil { + return + } + } else { + var systemNewAutoinstallMeta map[string]string + systemNewAutoinstallMeta, err = cmd.Flags().GetStringToString("autoinstall-meta") + if err != nil { + return + } + system.AutoinstallMeta.IsInherited = false + system.AutoinstallMeta.Data = convertMapStringToMapInterface(systemNewAutoinstallMeta) + } + case "boot-files": + fallthrough + case "boot-files-inherit": + if cmd.Flags().Lookup("boot-files-inherit").Changed { + system.BootFiles.Data = make(map[string]interface{}) + system.BootFiles.IsInherited, err = cmd.Flags().GetBool("boot-files-inherit") + if err != nil { + return + } + } else { + var systemNewBootFiles map[string]string + systemNewBootFiles, err = cmd.Flags().GetStringToString("boot-files") + if err != nil { + return + } + system.BootFiles.IsInherited = false + system.BootFiles.Data = convertMapStringToMapInterface(systemNewBootFiles) + } + case "boot-loaders": + fallthrough + case "boot-loaders-inherit": + if cmd.Flags().Lookup("boot-loaders-inherit").Changed { + system.BootLoaders.Data = []string{} + system.BootLoaders.IsInherited, err = cmd.Flags().GetBool("boot-loaders-inherit") + if err != nil { + return + } + } else { + var systemNewBootLoaders []string + systemNewBootLoaders, err = cmd.Flags().GetStringSlice("boot-loaders") + if err != nil { + return + } + system.BootLoaders.IsInherited = false + system.BootLoaders.Data = systemNewBootLoaders + } + case "comment": + var systemNewComment string + systemNewComment, err = cmd.Flags().GetString("comment") + if err != nil { + return + } + system.Comment = systemNewComment + case "fetchable-files": + fallthrough + case "fetchable-files-inherit": + if cmd.Flags().Lookup("fetchable-files-inherit").Changed { + system.FetchableFiles.Data = make(map[string]interface{}) + system.FetchableFiles.IsInherited, err = cmd.Flags().GetBool("fetchable-files-inherit") + if err != nil { + return + } + } else { + var systemNewFetchableFiles map[string]string + systemNewFetchableFiles, err = cmd.Flags().GetStringToString("fetchable-files") + if err != nil { + return + } + system.FetchableFiles.IsInherited = false + system.FetchableFiles.Data = convertMapStringToMapInterface(systemNewFetchableFiles) + } + case "kernel-options": + fallthrough + case "kernel-options-inherit": + if cmd.Flags().Lookup("kernel-options-inherit").Changed { + system.KernelOptions.Data = make(map[string]interface{}) + system.KernelOptions.IsInherited, err = cmd.Flags().GetBool("kernel-options-inherit") + if err != nil { + return + } + } else { + var systemNewKernelOptions map[string]string + systemNewKernelOptions, err = cmd.Flags().GetStringToString("kernel-options") + if err != nil { + return + } + system.KernelOptions.IsInherited = false + system.KernelOptions.Data = convertMapStringToMapInterface(systemNewKernelOptions) + } + case "kernel-options-post": + fallthrough + case "kernel-options-post-inherit": + if cmd.Flags().Lookup("kernel-options-post-inherit").Changed { + system.KernelOptionsPost.Data = make(map[string]interface{}) + system.KernelOptionsPost.IsInherited, err = cmd.Flags().GetBool("kernel-options-post-inherit") + if err != nil { + return + } + } else { + var systemNewKernelOptionsPost map[string]string + systemNewKernelOptionsPost, err = cmd.Flags().GetStringToString("kernel-options-post") + if err != nil { + return + } + system.KernelOptionsPost.IsInherited = false + system.KernelOptionsPost.Data = convertMapStringToMapInterface(systemNewKernelOptionsPost) + } + case "mgmt-classes": + fallthrough + case "mgmt-classes-inherit": + if cmd.Flags().Lookup("mgmt-classes-inherit").Changed { + system.MgmtClasses.Data = []string{} + system.MgmtClasses.IsInherited, err = cmd.Flags().GetBool("mgmt-classes-inherit") + if err != nil { + return + } + } else { + var systemNewMgmtClasses []string + systemNewMgmtClasses, err = cmd.Flags().GetStringSlice("mgmt-classes") + if err != nil { + return + } + system.MgmtClasses.IsInherited = false + system.MgmtClasses.Data = systemNewMgmtClasses + } + case "owners": + fallthrough + case "owners-inherit": + if cmd.Flags().Lookup("owners-inherit").Changed { + system.Owners.Data = []string{} + system.Owners.IsInherited, err = cmd.Flags().GetBool("owners-inherit") + if err != nil { + return + } + } else { + var systemNewOwners []string + systemNewOwners, err = cmd.Flags().GetStringSlice("owners") + if err != nil { + return + } + system.Owners.IsInherited = false + system.Owners.Data = systemNewOwners + } + case "redhat-management-key": + var systemNewRedhatManagementKey string + systemNewRedhatManagementKey, err = cmd.Flags().GetString("redhat-management-key") + if err != nil { + return + } + system.RedhatManagementKey = systemNewRedhatManagementKey + case "template-files-post": + fallthrough + case "template-files-inherit": + if cmd.Flags().Lookup("template-files-inherit").Changed { + system.TemplateFiles.Data = make(map[string]interface{}) + system.TemplateFiles.IsInherited, err = cmd.Flags().GetBool("template-files-inherit") + if err != nil { + return + } + } else { + var systemNewTemplateFiles map[string]string + systemNewTemplateFiles, err = cmd.Flags().GetStringToString("template-files") + if err != nil { + return + } + system.TemplateFiles.IsInherited = false + system.TemplateFiles.Data = convertMapStringToMapInterface(systemNewTemplateFiles) + } + case "dhcp-tag": + var systemNewDhcpTag string + systemNewDhcpTag, err = cmd.Flags().GetString("dhcp-tag") + if err != nil { + return + } + systemInterface.DHCPTag = systemNewDhcpTag + case "enable-ipxe": + fallthrough + case "enable-ipxe-inherit": + if cmd.Flags().Lookup("enable-ipxe-inherit").Changed { + system.EnableIPXE.Data = false + system.EnableIPXE.IsInherited, err = cmd.Flags().GetBool("enable-ipxe-inherit") + if err != nil { + return + } + } else { + var systemNewEnableIpxe bool + systemNewEnableIpxe, err = cmd.Flags().GetBool("enable-ipxe") + if err != nil { + return + } + system.EnableIPXE.IsInherited = false + system.EnableIPXE.Data = systemNewEnableIpxe + } + case "enable-menu": + fallthrough + case "enable-menu-inherit": + if cmd.Flags().Lookup("enable-menu-inherit").Changed { + system.EnableMenu.Data = false + system.EnableMenu.IsInherited, err = cmd.Flags().GetBool("enable-menu-inherit") + if err != nil { + return + } + } else { + var systemNewEnableMenu bool + systemNewEnableMenu, err = cmd.Flags().GetBool("enable-menu") + if err != nil { + return + } + system.EnableMenu.IsInherited = false + system.EnableMenu.Data = systemNewEnableMenu + } + case "mgmt-parameters": + fallthrough + case "mgmt-parameters-inherit": + if cmd.Flags().Lookup("mgmt-parameters-inherit").Changed { + system.MgmtParameters.Data = make(map[string]interface{}) + system.MgmtParameters.IsInherited, err = cmd.Flags().GetBool("mgmt-parameters-inherit") + if err != nil { + return + } + } else { + var systemNewMgmtParameters map[string]string + systemNewMgmtParameters, err = cmd.Flags().GetStringToString("mgmt-parameters") + if err != nil { + return + } + system.MgmtParameters.IsInherited = false + system.MgmtParameters.Data = convertMapStringToMapInterface(systemNewMgmtParameters) + } + case "name-servers": + var systemNewNameServers []string + systemNewNameServers, err = cmd.Flags().GetStringSlice("name-servers") + if err != nil { + return + } + system.NameServers = systemNewNameServers + case "name-servers-search": + var systemNewNameServersSearch []string + systemNewNameServersSearch, err = cmd.Flags().GetStringSlice("name-servers-search") + if err != nil { + return + } + system.NameServersSearch = systemNewNameServersSearch + case "next-server-v4": + var systemNewNextServerV4 string + systemNewNextServerV4, err = cmd.Flags().GetString("next-server-v4") + if err != nil { + return + } + system.NextServerv4 = systemNewNextServerV4 + case "next-server-v6": + var systemNewNextServerV6 string + systemNewNextServerV6, err = cmd.Flags().GetString("next-server-v6") + if err != nil { + return + } + system.NextServerv6 = systemNewNextServerV6 + case "filename": + var systemNewFilename string + systemNewFilename, err = cmd.Flags().GetString("filename") + if err != nil { + return + } + system.Filename = systemNewFilename + case "parent": + var systemNewParent string + systemNewParent, err = cmd.Flags().GetString("parent") + if err != nil { + return + } + system.Parent = systemNewParent + case "proxy": + var systemNewProxy string + systemNewProxy, err = cmd.Flags().GetString("proxy") + if err != nil { + return + } + system.Proxy = systemNewProxy + case "server": + var systemNewServer string + systemNewServer, err = cmd.Flags().GetString("server") + if err != nil { + return + } + system.Server = systemNewServer + case "menu": + var systemNewMenu string + systemNewMenu, err = cmd.Flags().GetString("menu") + if err != nil { + return + } + system.Menu = systemNewMenu + case "virt-auto-boot": + fallthrough + case "virt-auto-boot-inherit": + if cmd.Flags().Lookup("virt-auto-boot-inherit").Changed { + system.VirtAutoBoot.Data = false + system.VirtAutoBoot.IsInherited = true + } else { + var systemNewVirtAutoBoot bool + systemNewVirtAutoBoot, err = cmd.Flags().GetBool("virt-auto-boot") + if err != nil { + return + } + system.VirtAutoBoot.Data = systemNewVirtAutoBoot + system.VirtAutoBoot.IsInherited = false + } + case "virt-cpus": + fallthrough + case "virt-cpus-inherit": + if cmd.Flags().Lookup("virt-cpus-inherit").Changed { + system.VirtCPUs.IsInherited = true + } else { + var systemNewVirtCpus int + systemNewVirtCpus, err = cmd.Flags().GetInt("virt-cpus") + if err != nil { + return + } + system.VirtCPUs.Data = systemNewVirtCpus + system.VirtCPUs.IsInherited = false + } + case "virt-disk-driver": + var systemNewVirtDiskDriver string + systemNewVirtDiskDriver, err = cmd.Flags().GetString("virt-disk-driver") + if err != nil { + return + } + system.VirtDiskDriver = systemNewVirtDiskDriver + case "virt-file-size": + fallthrough + case "virt-file-size-inherit": + if cmd.Flags().Lookup("virt-file-size-inherit").Changed { + system.VirtFileSize.IsInherited = true + } else { + var systemNewVirtFileSize float64 + systemNewVirtFileSize, err = cmd.Flags().GetFloat64("virt-file-size") + if err != nil { + return + } + system.VirtFileSize.Data = systemNewVirtFileSize + system.VirtFileSize.IsInherited = false + } + case "virt-path": + var systemNewVirtPath string + systemNewVirtPath, err = cmd.Flags().GetString("virt-path") + if err != nil { + return + } + system.VirtPath = systemNewVirtPath + case "virt-ram": + fallthrough + case "virt-ram-inherit": + if cmd.Flags().Lookup("virt-ram-inherit").Changed { + system.VirtRAM.IsInherited = true + } else { + var systemNewVirtRam int + systemNewVirtRam, err = cmd.Flags().GetInt("virt-ram") + if err != nil { + return + } + system.VirtRAM.Data = systemNewVirtRam + system.VirtRAM.IsInherited = false + } + case "virt-type": + var systemNewVirtType string + systemNewVirtType, err = cmd.Flags().GetString("virt-type") + if err != nil { + return + } + system.VirtType = systemNewVirtType + case "gateway": + var systemNewGateway string + systemNewGateway, err = cmd.Flags().GetString("gateway") + if err != nil { + return + } + system.Gateway = systemNewGateway + case "hostname": + var systemNewHostname string + systemNewHostname, err := cmd.Flags().GetString("hostname") + if err != nil { + return + } + system.Hostname = systemNewHostname + case "image": + var systemNewImage string + systemNewImage, err = cmd.Flags().GetString("image") + if err != nil { + return + } + system.Image = systemNewImage + case "ipv6-default-device": + var systemNewIpv6DefaultDevice string + systemNewIpv6DefaultDevice, err = cmd.Flags().GetString("ipv6-default-device") + if err != nil { + return + } + system.IPv6DefaultDevice = systemNewIpv6DefaultDevice + case "netboot-enabled": + var systemNewNetbootEnabled bool + systemNewNetbootEnabled, err = cmd.Flags().GetBool("netboot-enabled") + if err != nil { + return + } + system.NetbootEnabled = systemNewNetbootEnabled + case "power-address": + var systemNewPowerAddress string + systemNewPowerAddress, err = cmd.Flags().GetString("power-address") + if err != nil { + return + } + system.PowerAddress = systemNewPowerAddress + case "power-id": + var systemNewPowerId string + systemNewPowerId, err = cmd.Flags().GetString("power-id") + if err != nil { + return + } + system.PowerID = systemNewPowerId + case "power-pass": + var systemNewPowerPass string + systemNewPowerPass, err = cmd.Flags().GetString("power-pass") + if err != nil { + return + } + system.PowerPass = systemNewPowerPass + case "power-type": + var systemNewPowerType string + systemNewPowerType, err = cmd.Flags().GetString("power-type") + if err != nil { + return + } + system.PowerType = systemNewPowerType + case "power-user": + var systemNewPowerUser string + systemNewPowerUser, err = cmd.Flags().GetString("power-user") + if err != nil { + return + } + system.PowerUser = systemNewPowerUser + case "power-options": + var systemNewPowerOptions string + systemNewPowerOptions, err = cmd.Flags().GetString("power-options") + if err != nil { + return + } + system.PowerOptions = systemNewPowerOptions + case "power-identity-file": + var systemNewPowerIdentityFile string + systemNewPowerIdentityFile, err = cmd.Flags().GetString("power-identity-file") + if err != nil { + return + } + system.PowerIdentityFile = systemNewPowerIdentityFile + case "profile": + var systemNewProfile string + systemNewProfile, err = cmd.Flags().GetString("profile") + if err != nil { + return + } + system.Profile = systemNewProfile + case "status": + var systemNewStatus string + systemNewStatus, err = cmd.Flags().GetString("status") + if err != nil { + return + } + system.Status = systemNewStatus + case "virt-pxe-boot": + var systemNewVirtPxeBoot bool + systemNewVirtPxeBoot, err = cmd.Flags().GetBool("virt-pxe-boot") + if err != nil { + return + } + system.VirtPXEBoot = systemNewVirtPxeBoot + case "serial-device": + var systemNewSerialDevice int + systemNewSerialDevice, err = cmd.Flags().GetInt("serial-device") + if err != nil { + return + } + system.SerialDevice = systemNewSerialDevice + case "serial-baud-rate": + var systemNewSerialBaudRate int + systemNewSerialBaudRate, err = cmd.Flags().GetInt("serial-baud-rate") + if err != nil { + return + } + system.SerialBaudRate = systemNewSerialBaudRate + case "bonding-opts": + var systemNewBondingOpts string + systemNewBondingOpts, err = cmd.Flags().GetString("bonding-opts") + if err != nil { + return + } + systemInterface.BondingOpts = systemNewBondingOpts + case "bridge-opts": + var systemNewBridgeOpts string + systemNewBridgeOpts, err = cmd.Flags().GetString("bridge-opts") + if err != nil { + return + } + systemInterface.BridgeOpts = systemNewBridgeOpts + case "cnames": + var systemNewCNames []string + systemNewCNames, err = cmd.Flags().GetStringSlice("cnames") + if err != nil { + return + } + systemInterface.CNAMEs = systemNewCNames + case "connected-mode": + var systemNewConnectedMode bool + systemNewConnectedMode, err = cmd.Flags().GetBool("connected-mode") + if err != nil { + return + } + systemInterface.ConnectedMode = systemNewConnectedMode + case "dns-name": + var systemNewDnsName string + systemNewDnsName, err = cmd.Flags().GetString("dns-name") + if err != nil { + return + } + systemInterface.DNSName = systemNewDnsName + case "if-gateway": + var systemNewIfGateway string + systemNewIfGateway, err = cmd.Flags().GetString("if-gateway") + if err != nil { + return + } + systemInterface.Gateway = systemNewIfGateway + case "interface-master": + var systemNewInterfaceMaster string + systemNewInterfaceMaster, err = cmd.Flags().GetString("interface-master") + if err != nil { + return + } + systemInterface.InterfaceMaster = systemNewInterfaceMaster + case "interface-type": + var systemNewInterfaceType string + systemNewInterfaceType, err = cmd.Flags().GetString("interface-type") + if err != nil { + return + } + systemInterface.InterfaceType = systemNewInterfaceType + case "ip-address": + var systemNewIpAddress string + systemNewIpAddress, err := cmd.Flags().GetString("ip-address") + if err != nil { + return + } + systemInterface.IPAddress = systemNewIpAddress + case "ipv6-address": + var systemNewIpv6Address string + systemNewIpv6Address, err = cmd.Flags().GetString("ipv6-address") + if err != nil { + return + } + systemInterface.IPv6Address = systemNewIpv6Address + case "ipv6-default-gateway": + var systemNewIpv6DefaultGateway string + systemNewIpv6DefaultGateway, err = cmd.Flags().GetString("ipv6-default-gateway") + if err != nil { + return + } + systemInterface.IPv6DefaultGateway = systemNewIpv6DefaultGateway + case "ipv6-mtu": + var systemNewIpv6Mtu string + systemNewIpv6Mtu, err = cmd.Flags().GetString("ipv6-mtu") + if err != nil { + return + } + systemInterface.IPv6MTU = systemNewIpv6Mtu + case "ipv6-prefix": + var systemNewIpv6Prefix string + systemNewIpv6Prefix, err = cmd.Flags().GetString("ipv6-prefix") + if err != nil { + return + } + systemInterface.IPv6Prefix = systemNewIpv6Prefix + case "ipv6-secondaries": + var systemNewIpv6Secondaries []string + systemNewIpv6Secondaries, err = cmd.Flags().GetStringSlice("ipv6-secondaries") + if err != nil { + return + } + systemInterface.IPv6Secondaries = systemNewIpv6Secondaries + case "ipv6-static-routes": + var systemNewIpv6StaticRoutes []string + systemNewIpv6StaticRoutes, err = cmd.Flags().GetStringSlice("ipv6-static-routes") + if err != nil { + return + } + systemInterface.IPv6StaticRoutes = systemNewIpv6StaticRoutes + case "mac-address": + var systemNewMacAddress string + systemNewMacAddress, err = cmd.Flags().GetString("mac-address") + if err != nil { + return + } + systemInterface.MACAddress = systemNewMacAddress + case "management": + var systemNewManagement bool + systemNewManagement, err = cmd.Flags().GetBool("management") + if err != nil { + return + } + systemInterface.Management = systemNewManagement + case "mtu": + var systemNewMtu string + systemNewMtu, err = cmd.Flags().GetString("mtu") + if err != nil { + return + } + systemInterface.MTU = systemNewMtu + case "netmask": + var systemNewNetmask string + systemNewNetmask, err = cmd.Flags().GetString("netmask") + if err != nil { + return + } + systemInterface.Netmask = systemNewNetmask + case "static": + var systemNewStatic bool + systemNewStatic, err = cmd.Flags().GetBool("static") + if err != nil { + return + } + systemInterface.Static = systemNewStatic + case "static-routes": + var systemNewStaticRoutes []string + systemNewStaticRoutes, err = cmd.Flags().GetStringSlice("static-routes") + if err != nil { + return + } + systemInterface.StaticRoutes = systemNewStaticRoutes + case "virt-bridge": + var systemNewVirtBridge string + systemNewVirtBridge, err = cmd.Flags().GetString("virt-bridge") + if err != nil { + return + } + systemInterface.VirtBridge = systemNewVirtBridge + } + }) + // Don't blindly return nil because maybe one of the flags had an issue retrieving an argument. + return err +} // systemCmd represents the system command var systemCmd = &cobra.Command{ @@ -24,7 +708,7 @@ var systemCmd = &cobra.Command{ Long: `Let you manage systems. See https://cobbler.readthedocs.io/en/latest/cobbler.html#cobbler-system for more information.`, Run: func(cmd *cobra.Command, args []string) { - cmd.Help() + _ = cmd.Help() }, } @@ -32,88 +716,30 @@ var systemAddCmd = &cobra.Command{ Use: "add", Short: "add system", Long: `Adds a system.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - var newSystem cobbler.System + newSystem := cobbler.NewSystem() + var err error // internal fields (ctime, mtime, depth, uid, repos-enabled, ipv6-autoconfiguration) cannot be modified - newSystem.Autoinstall, _ = cmd.Flags().GetString("autoinstall") - newSystem.AutoinstallMeta, _ = cmd.Flags().GetStringArray("autoinstall-meta") - newSystem.BootFiles, _ = cmd.Flags().GetString("bootfiles") - newSystem.Comment, _ = cmd.Flags().GetString("comment") - newSystem.EnableGPXE, _ = cmd.Flags().GetBool("enable-ipxe") - newSystem.FetchableFiles, _ = cmd.Flags().GetStringArray("fetchable-files") - newSystem.Gateway, _ = cmd.Flags().GetString("gateway") - newSystem.Hostname, _ = cmd.Flags().GetString("hostname") - newSystem.Image, _ = cmd.Flags().GetString("image") - newSystem.IPv6DefaultDevice, _ = cmd.Flags().GetString("ipv6-default-device") - newSystem.KernelOptions, _ = cmd.Flags().GetStringArray("kernel-options") - newSystem.KernelOptionsPost, _ = cmd.Flags().GetStringArray("kernel-options-post") - newSystem.MGMTClasses, _ = cmd.Flags().GetStringArray("mgmt-classes") - newSystem.MGMTParameters, _ = cmd.Flags().GetString("mgmt-parameters") - newSystem.Name, _ = cmd.Flags().GetString("name") - newSystem.NameServers, _ = cmd.Flags().GetStringArray("name-servers") - newSystem.NameServersSearch, _ = cmd.Flags().GetStringArray("name-servers-search") - newSystem.NetbootEnabled, _ = cmd.Flags().GetBool("netboot-enabled") - newSystem.NextServerv4, _ = cmd.Flags().GetString("next-server-v4") - newSystem.Owners, _ = cmd.Flags().GetStringArray("owners") - newSystem.PowerAddress, _ = cmd.Flags().GetString("power-address") - newSystem.PowerID, _ = cmd.Flags().GetString("power-id") - newSystem.PowerPass, _ = cmd.Flags().GetString("power-pass") - newSystem.PowerType, _ = cmd.Flags().GetString("power-type") - newSystem.PowerUser, _ = cmd.Flags().GetString("power-user") - newSystem.Profile, _ = cmd.Flags().GetString("profile") - newSystem.Proxy, _ = cmd.Flags().GetString("proxy") - // newSystem.RedHatManagementKey, _ = cmd.Flags().GetString("redhat-management-key") - newSystem.Status, _ = cmd.Flags().GetString("status") - newSystem.TemplateFiles, _ = cmd.Flags().GetStringArray("template-files") - newSystem.VirtAutoBoot, _ = cmd.Flags().GetString("virt-auto-boot") - newSystem.VirtCPUs, _ = cmd.Flags().GetString("virt-cpus") - newSystem.VirtDiskDriver, _ = cmd.Flags().GetString("virt-disk-driver") - newSystem.VirtFileSize, _ = cmd.Flags().GetString("virt-file-size") - newSystem.VirtPath, _ = cmd.Flags().GetString("virt-path") - newSystem.VirtPXEBoot, _ = cmd.Flags().GetInt("virt-pxe-boot") - newSystem.VirtRAM, _ = cmd.Flags().GetString("virt-ram") - newSystem.VirtType, _ = cmd.Flags().GetString("virt-type") - - // interface type - iface.CNAMEs, _ = cmd.Flags().GetStringArray("cnames") - iface.DHCPTag, _ = cmd.Flags().GetString("dhcp-tag") - iface.DNSName, _ = cmd.Flags().GetString("dns-name") - iface.BondingOpts, _ = cmd.Flags().GetString("bonding-opts") - //iface.BridgeOpts, _ = cmd.Flags().GetString("bridge-opts") - iface.Gateway, _ = cmd.Flags().GetString("gateway") - iface.InterfaceType, _ = cmd.Flags().GetString("interface-type") - iface.InterfaceMaster, _ = cmd.Flags().GetString("interface-master") - iface.IPAddress, _ = cmd.Flags().GetString("ip-address") - iface.IPv6Address, _ = cmd.Flags().GetString("ipv6-address") - iface.IPv6Secondaries, _ = cmd.Flags().GetStringArray("ipv6-secondaries") - iface.IPv6MTU, _ = cmd.Flags().GetString("ipv6-mtu") - iface.IPv6StaticRoutes, _ = cmd.Flags().GetStringArray("ipv6-static-routes") - iface.IPv6DefaultGateway, _ = cmd.Flags().GetString("ipv6-default-gateway") - iface.MACAddress, _ = cmd.Flags().GetString("mac-address") - iface.Management, _ = cmd.Flags().GetBool("management") - iface.Netmask, _ = cmd.Flags().GetString("netmask") - iface.Static, _ = cmd.Flags().GetBool("static") - iface.StaticRoutes, _ = cmd.Flags().GetStringArray("static-routes") - iface.VirtBridge, _ = cmd.Flags().GetString("virt-bridge") - - // TODO: Implementation for more interfaces - // See https://github.com/cobbler/cli/issues/38 - err = newSystem.CreateInterface("default", iface) - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + newSystem.Name, err = cmd.Flags().GetString("name") + if err != nil { + return err } - - system, err = Client.CreateSystem(newSystem) - if checkError(err) == nil { - fmt.Printf("System %s created", newSystem.Name) - } else { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + // Update system in-memory + err = updateSystemFromFlags(cmd, &newSystem) + if err != nil { + return err } + // No create the system via XML-RPC + // FIXME: Call modify_interface in the client when getting to the interfaces objects + system, err := Client.CreateSystem(newSystem) + if err != nil { + return err + } + fmt.Printf("System %s created\n", system.Name) + return nil }, } @@ -121,11 +747,37 @@ var systemCopyCmd = &cobra.Command{ Use: "copy", Short: "copy system", Long: `Copies a system.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() + systemName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + systemNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } - // TODO: call cobblerclient - notImplemented() + systemHandle, err := Client.GetSystemHandle(systemName) + if err != nil { + return err + } + err = Client.CopySystem(systemHandle, systemNewName) + if err != nil { + return err + } + newSystem, err := Client.GetSystem(systemNewName, false, false) + if err != nil { + return err + } + // Update the system in-memory + err = updateSystemFromFlags(cmd, newSystem) + if err != nil { + return err + } + // Update the system via XML-RPC + // FIXME: Call modify_interface in the client when getting to the interfaces objects + return Client.UpdateSystem(newSystem) }, } @@ -133,11 +785,64 @@ var systemDumpVarsCmd = &cobra.Command{ Use: "dumpvars", Short: "dump system variables", Long: `Prints all system variables to stdout.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get CLI flags + systemName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + + // Now retrieve data + blendedData, err := Client.GetBlendedData("", systemName) + if err != nil { + return err + } + // Print data + // TODO: Deduplicate with profile + for key, value := range blendedData { + if value == nil { + fmt.Printf("%s:\n", key) + continue + } + valueType := reflect.TypeOf(value).Kind() + switch valueType { + case reflect.Bool: + fmt.Printf("%s: %t\n", key, value.(bool)) + case reflect.Int64: + fmt.Printf("%s: %d\n", key, value.(int64)) + case reflect.Int32: + fmt.Printf("%s: %d\n", key, value.(int32)) + case reflect.Int16: + fmt.Printf("%s: %d\n", key, value.(int16)) + case reflect.Int8: + fmt.Printf("%s: %d\n", key, value.(int8)) + case reflect.Int: + fmt.Printf("%s: %d\n", key, value.(int)) + case reflect.Float32: + fmt.Printf("%s: %f\n", key, value.(float32)) + case reflect.Float64: + fmt.Printf("%s: %f\n", key, value.(float64)) + case reflect.Slice, reflect.Array: + arr := reflect.ValueOf(value) + fmt.Printf("%s: [", key) + for i := 0; i < arr.Len(); i++ { + if i+1 != arr.Len() { + fmt.Printf("'%v', ", arr.Index(i).Interface()) + } else { + fmt.Printf("'%v'", arr.Index(i).Interface()) + } + } + fmt.Printf("]\n") + case reflect.Map: + res2B, _ := json.Marshal(value) + fmt.Printf("%s: %s\n", key, string(res2B)) + default: + fmt.Printf("%s: %s\n", key, value) + } + } + return err }, } @@ -145,188 +850,27 @@ var systemEditCmd = &cobra.Command{ Use: "edit", Short: "edit system", Long: `Edits a system.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() // find profile through its name - pname, _ := cmd.Flags().GetString("name") - var updateSystem, err = Client.GetSystem(pname) - - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) - } - - // internal fields (ctime, mtime, depth, uid, repos-enabled, ipv6-autoconfiguration) cannot be modified - var tmpArgs, _ = cmd.Flags().GetString("autoinstall") - if tmpArgs != "" { - updateSystem.Autoinstall, _ = cmd.Flags().GetString("autoinstall") - } - var tmpArgsArray, _ = cmd.Flags().GetStringArray("autoinstall-meta") - if len(tmpArgsArray) > 0 { - updateSystem.AutoinstallMeta, _ = cmd.Flags().GetStringArray("autoinstall-meta") - } - tmpArgs, _ = cmd.Flags().GetString("bootfiles") - if tmpArgs != "" { - updateSystem.BootFiles, _ = cmd.Flags().GetString("bootfiles") - } - tmpArgs, _ = cmd.Flags().GetString("comment") - if tmpArgs != "" { - updateSystem.Comment, _ = cmd.Flags().GetString("comment") - } - // TODO - /* - var tmpArgsBool, _ = cmd.Flags().GetBool("enable-ipxe") - if tmpArgsBool != "" { - updateSystem.EnableGPXE, _ = cmd.Flags().GetBool("enable-ipxe") - } - */ - tmpArgsArray, _ = cmd.Flags().GetStringArray("fetchable-files") - if len(tmpArgsArray) > 0 { - updateSystem.FetchableFiles, _ = cmd.Flags().GetStringArray("fetchable-files") - } - tmpArgs, _ = cmd.Flags().GetString("gateway") - if tmpArgs != "" { - updateSystem.Gateway, _ = cmd.Flags().GetString("gateway") - } - tmpArgs, _ = cmd.Flags().GetString("hostname") - if tmpArgs != "" { - updateSystem.Hostname, _ = cmd.Flags().GetString("hostname") - } - tmpArgs, _ = cmd.Flags().GetString("image") - if tmpArgs != "" { - updateSystem.Image, _ = cmd.Flags().GetString("image") - } - tmpArgs, _ = cmd.Flags().GetString("ipv6-default-device") - if tmpArgs != "" { - updateSystem.IPv6DefaultDevice, _ = cmd.Flags().GetString("ipv6-default-device") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("kernel-options") - if len(tmpArgsArray) > 0 { - updateSystem.KernelOptions, _ = cmd.Flags().GetStringArray("kernel-options") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("kernel-options-post") - if len(tmpArgsArray) > 0 { - updateSystem.KernelOptionsPost, _ = cmd.Flags().GetStringArray("kernel-options-post") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("mgmt-classes") - if len(tmpArgsArray) > 0 { - updateSystem.MGMTClasses, _ = cmd.Flags().GetStringArray("mgmt-classes") - } - tmpArgs, _ = cmd.Flags().GetString("mgmt-parameters") - if tmpArgs != "" { - updateSystem.MGMTParameters, _ = cmd.Flags().GetString("mgmt-parameters") - } - tmpArgs, _ = cmd.Flags().GetString("name") - if tmpArgs != "" { - updateSystem.Name, _ = cmd.Flags().GetString("name") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("name-servers") - if len(tmpArgsArray) > 0 { - updateSystem.NameServers, _ = cmd.Flags().GetStringArray("name-servers") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("name-servers-search") - if len(tmpArgsArray) > 0 { - updateSystem.NameServersSearch, _ = cmd.Flags().GetStringArray("name-servers-search") - } - // TODO - /* - tmpArgsBool, _ = cmd.Flags().GetBool("netboot-enabled") - if tmpArgsBool != "" { - updateSystem.NetbootEnabled, _ = cmd.Flags().GetBool("netboot-enabled") - } - */ - tmpArgs, _ = cmd.Flags().GetString("next-servers") - if tmpArgs != "" { - updateSystem.NextServerv4, _ = cmd.Flags().GetString("next-server-v4") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("owners") - if len(tmpArgsArray) > 0 { - updateSystem.Owners, _ = cmd.Flags().GetStringArray("owners") - } - tmpArgs, _ = cmd.Flags().GetString("power-address") - if tmpArgs != "" { - updateSystem.PowerAddress, _ = cmd.Flags().GetString("power-address") - } - tmpArgs, _ = cmd.Flags().GetString("power-id") - if tmpArgs != "" { - updateSystem.PowerID, _ = cmd.Flags().GetString("power-id") - } - tmpArgs, _ = cmd.Flags().GetString("power-pass") - if tmpArgs != "" { - updateSystem.PowerPass, _ = cmd.Flags().GetString("power-pass") - } - tmpArgs, _ = cmd.Flags().GetString("power-type") - if tmpArgs != "" { - updateSystem.PowerType, _ = cmd.Flags().GetString("power-type") - } - tmpArgs, _ = cmd.Flags().GetString("power-user") - if tmpArgs != "" { - updateSystem.PowerUser, _ = cmd.Flags().GetString("power-user") + systemName, err := cmd.Flags().GetString("name") + if err != nil { + return err } - tmpArgs, _ = cmd.Flags().GetString("profile") - if tmpArgs != "" { - updateSystem.Profile, _ = cmd.Flags().GetString("profile") + updateSystem, err := Client.GetSystem(systemName, false, false) + if err != nil { + return err } - tmpArgs, _ = cmd.Flags().GetString("proxy") - if tmpArgs != "" { - updateSystem.Proxy, _ = cmd.Flags().GetString("proxy") - } - /* - tmpArgs, _ = cmd.Flags().GetString("redhat-management-key") - if tmpArgs != "" { - updateSystem.RedHatManagementKey, _ = cmd.Flags().GetString("redhat-management-key") - } - */ - tmpArgs, _ = cmd.Flags().GetString("status") - if tmpArgs != "" { - updateSystem.Status, _ = cmd.Flags().GetString("status") - } - tmpArgsArray, _ = cmd.Flags().GetStringArray("template-files") - if len(tmpArgsArray) > 0 { - updateSystem.TemplateFiles, _ = cmd.Flags().GetStringArray("template-files") - } - tmpArgs, _ = cmd.Flags().GetString("virt-auto-boot") - if tmpArgs != "" { - updateSystem.VirtAutoBoot, _ = cmd.Flags().GetString("virt-auto-boot") - } - tmpArgs, _ = cmd.Flags().GetString("virt-cpus") - if tmpArgs != "" { - updateSystem.VirtCPUs, _ = cmd.Flags().GetString("virt-cpus") - } - tmpArgs, _ = cmd.Flags().GetString("virt-disk-driver") - if tmpArgs != "" { - updateSystem.VirtDiskDriver, _ = cmd.Flags().GetString("virt-disk-driver") - } - tmpArgs, _ = cmd.Flags().GetString("virt-file-size") - if tmpArgs != "" { - updateSystem.VirtFileSize, _ = cmd.Flags().GetString("virt-file-size") - } - tmpArgs, _ = cmd.Flags().GetString("virt-path") - if tmpArgs != "" { - updateSystem.VirtPath, _ = cmd.Flags().GetString("virt-path") - } - - // FIXME: what happens when the int value is acutally 0 instead of 1? - var tmpArgsInt, _ = cmd.Flags().GetInt("virt-pxe-boot") - if tmpArgsInt != 0 { - updateSystem.VirtPXEBoot, _ = cmd.Flags().GetInt("virt-pxe-boot") - } - tmpArgs, _ = cmd.Flags().GetString("virt-ram") - if tmpArgs != "" { - updateSystem.VirtRAM, _ = cmd.Flags().GetString("virt-ram") - } - tmpArgs, _ = cmd.Flags().GetString("virt-type") - if tmpArgs != "" { - updateSystem.VirtType, _ = cmd.Flags().GetString("virt-type") - } - - err = Client.UpdateSystem(updateSystem) - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + // Update the system in-memory + err = updateSystemFromFlags(cmd, updateSystem) + if err != nil { + return err } + // Update the system via XML-RPC + // FIXME: Call modify_interface in the client when getting to the interfaces objects + return Client.UpdateSystem(updateSystem) }, } @@ -334,11 +878,9 @@ var systemFindCmd = &cobra.Command{ Use: "find", Short: "find system", Long: `Finds a given system.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + return FindItemNames(cmd, args, "system") }, } @@ -346,11 +888,26 @@ var systemGetAutoinstallCmd = &cobra.Command{ Use: "get-autoinstall", Short: "dump autoinstall XML", Long: `Prints the autoinstall XML file of the given system to stdout.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + systemName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + systemExists, err := Client.HasItem("system", systemName) + if err != nil { + return err + } + if !systemExists { + fmt.Println("System does not exist!") + os.Exit(1) + } + autoinstallRendered, err := Client.GenerateAutoinstall("", systemName) + if err != nil { + return err + } + fmt.Println(autoinstallRendered) + return nil }, } @@ -358,17 +915,14 @@ var systemListCmd = &cobra.Command{ Use: "list", Short: "list all systems", Long: `Lists all available systems.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - systems, err = Client.GetSystems() - - if checkError(err) == nil { - fmt.Println(systems) - } else { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) + systemNames, err := Client.ListSystemNames() + if err != nil { + return err } + listItems("systems", systemNames) + return nil }, } @@ -376,11 +930,22 @@ var systemPowerOffCmd = &cobra.Command{ Use: "poweroff", Short: "power off system", Long: `Powers off the selected system.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get flags + systemName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + + // Perform action + systemHandle, err := Client.GetSystemHandle(systemName) + if err != nil { + return err + } + _, err = Client.PowerSystem(systemHandle, "off") + return err }, } @@ -388,11 +953,22 @@ var systemPowerOnCmd = &cobra.Command{ Use: "poweron", Short: "power on system", Long: `Powers on the selected system.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get flags + systemName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + + // Perform action + systemHandle, err := Client.GetSystemHandle(systemName) + if err != nil { + return err + } + _, err = Client.PowerSystem(systemHandle, "on") + return err }, } @@ -400,11 +976,22 @@ var systemPowerStatusCmd = &cobra.Command{ Use: "powerstatus", Short: "Power status of the system", Long: `Querys the power status of the selected system.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get flags + systemName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + + // Perform action + systemHandle, err := Client.GetSystemHandle(systemName) + if err != nil { + return err + } + _, err = Client.PowerSystem(systemHandle, "status") + return err }, } @@ -412,11 +999,22 @@ var systemRebootCmd = &cobra.Command{ Use: "reboot", Short: "reboot system", Long: `Reboots the selected system.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get flags + systemName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + + // Perform action + systemHandle, err := Client.GetSystemHandle(systemName) + if err != nil { + return err + } + _, err = Client.PowerSystem(systemHandle, "reboot") + return err }, } @@ -424,15 +1022,9 @@ var systemRemoveCmd = &cobra.Command{ Use: "remove", Short: "remove system", Long: `Removes a given system.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - sname, _ := cmd.Flags().GetString("name") - err := Client.DeleteSystem(sname) - if checkError(err) != nil { - fmt.Fprintln(os.Stderr, err.Error()) - os.Exit(1) - } + return RemoveItemRecursive(cmd, args, "system") }, } @@ -440,23 +1032,72 @@ var systemRenameCmd = &cobra.Command{ Use: "rename", Short: "rename system", Long: `Renames a given system.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient - notImplemented() + // Get flags + systemName, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + systemNewName, err := cmd.Flags().GetString("newname") + if err != nil { + return err + } + + // Perform action + systemHandle, err := Client.GetSystemHandle(systemName) + if err != nil { + return err + } + err = Client.RenameSystem(systemHandle, systemNewName) + if err != nil { + return err + } + newSystem, err := Client.GetSystem(systemNewName, false, false) + if err != nil { + return err + } + err = updateSystemFromFlags(cmd, newSystem) + if err != nil { + return err + } + return Client.UpdateSystem(newSystem) }, } +func reportSystems(systemNames []string) error { + for _, itemName := range systemNames { + system, err := Client.GetSystem(itemName, false, false) + if err != nil { + return err + } + printStructured(system) + fmt.Println("") + } + return nil +} + var systemReportCmd = &cobra.Command{ Use: "report", Short: "list all systems in detail", Long: `Shows detailed information about all systems.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - - // TODO: call cobblerclient - notImplemented() + name, err := cmd.Flags().GetString("name") + if err != nil { + return err + } + itemNames := make([]string, 0) + if name == "" { + itemNames, err = Client.ListSystemNames() + if err != nil { + return err + } + } else { + itemNames = append(itemNames, name) + } + return reportSystems(itemNames) }, } @@ -478,159 +1119,39 @@ func init() { systemCmd.AddCommand(systemReportCmd) // local flags for system add - systemAddCmd.Flags().String("name", "", "the system name") - systemAddCmd.Flags().String("autoinstall", "", "path to automatic installation template") - systemAddCmd.Flags().String("autoinstall-meta", "", "automatic installation metadata") - systemAddCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - systemAddCmd.Flags().String("boot-loaders", "", "boot loader space delimited list (network installation boot loaders). Valid options for list items are: <>,grub,pxe,ipxe") - systemAddCmd.Flags().String("comment", "", "free form text description") - systemAddCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - systemAddCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - systemAddCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - systemAddCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - systemAddCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - systemAddCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - systemAddCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") + addCommonArgs(systemAddCmd) + addStringFlags(systemAddCmd, systemStringFlagMetadata) + addStringFlags(systemAddCmd, systemPowerStringFlagMetadata) + addBoolFlags(systemAddCmd, systemBoolFlagMetadata) + addIntFlags(systemAddCmd, systemIntFlagMetadata) + addFloatFlags(systemAddCmd, systemFloatFlagMetadata) + addStringSliceFlags(systemAddCmd, systemStringSliceFlagMetadata) + addMapFlags(systemAddCmd, systemMapFlagMetadata) + // Network interface flags + addStringFlags(systemAddCmd, interfaceStringFlagMetadata) + addBoolFlags(systemAddCmd, interfaceBoolFlagMetadata) + addStringSliceFlags(systemAddCmd, interfaceStringSliceFlagMetadata) + // Other systemAddCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - systemAddCmd.Flags().String("dhcp-tag", "", "DHCP tag (see manpage or leave blank)") - systemAddCmd.Flags().Bool("enable-ipxe", false, "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)") - systemAddCmd.Flags().Bool("enable-menu", false, "enable PXE Menu? (show this system in the PXE menu?)") - systemAddCmd.Flags().String("mgmt-parameters", "", "Parameters which will be handed to your management application (must be a valid YAML dictionary))") - systemAddCmd.Flags().String("name-servers", "", "name servers (space delimited)") - systemAddCmd.Flags().String("name-servers-search", "", "name servers search path (space delimited)") - systemAddCmd.Flags().String("next-server-v4", "", "next server (IPv4) override (see manpage or leave blank)") - systemAddCmd.Flags().String("next-server-v6", "", "next server (IPv6) override (see manpage or leave blank)") - systemAddCmd.Flags().String("filename", "", "DHCP filename override (used to boot non-default bootloaders)") - systemAddCmd.Flags().String("parent", "", "parent system") - systemAddCmd.Flags().String("proxy", "", "proxy server URL") - systemAddCmd.Flags().String("server", "", "server override") - systemAddCmd.Flags().String("menu", "", "parent boot menu") - systemAddCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - systemAddCmd.Flags().String("virt-bridge", "", "virt bridge") - systemAddCmd.Flags().String("virt-cpus", "", "virt CPUs") - systemAddCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - systemAddCmd.Flags().String("virt-file-size", "", "virt file size in GB") - systemAddCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - systemAddCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - systemAddCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)") - systemAddCmd.Flags().String("gateway", "", "gateway") - systemAddCmd.Flags().String("hostname", "", "hostname") - systemAddCmd.Flags().String("image", "", "parent image (if not a profile)") - systemAddCmd.Flags().String("ipv6-default-device", "", "IPv6 default device") - systemAddCmd.Flags().Bool("netboot-enabled", false, "PXE (re)install this machine at next boot?") - systemAddCmd.Flags().String("power-address", "", "power management address (e.g. power-device.example.org)") - systemAddCmd.Flags().String("power-id", "", "power management ID (usually a plug number or blade name, if power type requires it)") - systemAddCmd.Flags().String("power-pass", "", "power management password") - systemAddCmd.Flags().String("power-type", "", "power management script to use") - systemAddCmd.Flags().String("power-user", "", "power management username") - systemAddCmd.Flags().String("power-options", "", "additional options, to be passed to the fencing agent") - systemAddCmd.Flags().String("power-identity-file", "", "identity file to be passed to the fencing agent (SSH key)") - systemAddCmd.Flags().String("profile", "", "Parent profile") - systemAddCmd.Flags().String("status", "", "system status. Valid options: development,testing,acceptance,production") - systemAddCmd.Flags().Bool("virt-pxe-boot", false, "use PXE to build this VM?") - systemAddCmd.Flags().String("serial-device", "", "serial device number") - systemAddCmd.Flags().String("serial-baud-rate", "", "serial Baud Rate. Valid options: 2400,4800,9600,19200,38400,57600,115200") - systemAddCmd.Flags().String("bonding-opts", "", "bonding opts (should be used with --interface)") - systemAddCmd.Flags().String("ridge-opts", "", "bridge opts (should be used with --interface") - systemAddCmd.Flags().String("cnames", "", "Cannonical Name Records, should be used with --interface (in quotes, space delimited)") - systemAddCmd.Flags().String("connected-mode", "", "InfiniBand connected mode (should be used with --interface)") - systemAddCmd.Flags().String("dns-name", "", "DNS name (should be used with --interface)") - systemAddCmd.Flags().String("if-gateway", "", "per-Interface Gateway (should be used with --interface)") - systemAddCmd.Flags().String("interface-master", "", "master interface (Should be used with --interface)") - systemAddCmd.Flags().String("interface-type", "", `interface Type. Valid options: na,bond,bond_slave,bridge,bridge_slave,bonded_bridge_slave,bmc,infiniband. - (should be used with --interface)`) - systemAddCmd.Flags().String("ip-address", "", "IPv4 address (should be used with --interface)") - systemAddCmd.Flags().String("ipv6-address", "", "IPv6 address (should be used with --interface)") - systemAddCmd.Flags().String("ipv6-default-gateway", "", "IPv6 Default Gateway (should be used with --interface)") - systemAddCmd.Flags().String("ipv6-mtu", "", "IPv6 MTU") - systemAddCmd.Flags().String("ipv6-prefix", "", "IPv6 Prefix (should be used with --interface)") - systemAddCmd.Flags().String("ipv6-secondaries", "", "IPv6 Secondaries (should be used with --interface)") - systemAddCmd.Flags().String("ipv6-static-routes", "", "IPv6 Static Routes (should be used with --interface)") - systemAddCmd.Flags().String("mac-address", "", "MAC Address (place 'random' in this field for a random MAC Address.)") - systemAddCmd.Flags().Bool("management", false, "declares the interface as management interface (should be used with --interface) ") - systemAddCmd.Flags().String("mtu", "", "MTU") - systemAddCmd.Flags().String("netmask", "", "subnet mask (should be used with --interface)") - systemAddCmd.Flags().Bool("static", false, "Is this interface static? (should be used with --interface)") - systemAddCmd.Flags().String("static-routes", "", "static routes (should be used with --interface)") systemAddCmd.Flags().String("interface", "", "the interface to operate on") systemAddCmd.Flags().Bool("delete-interface", false, "delete the given interface (should be used with --interface)") systemAddCmd.Flags().String("rename-interface", "", "rename the given interface (should be used with --interface)") // local flags for system copy - systemCopyCmd.Flags().String("name", "", "the system name") - systemCopyCmd.Flags().String("autoinstall", "", "path to automatic installation template") - systemCopyCmd.Flags().String("autoinstall-meta", "", "automatic installation metadata") - systemCopyCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - systemCopyCmd.Flags().String("boot-loaders", "", "boot loader space delimited list (network installation boot loaders). Valid options for list items are: <>,grub,pxe,ipxe") - systemCopyCmd.Flags().String("comment", "", "free form text description") - systemCopyCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - systemCopyCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - systemCopyCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - systemCopyCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - systemCopyCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - systemCopyCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - systemCopyCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") + addCommonArgs(systemCopyCmd) + addStringFlags(systemCopyCmd, systemStringFlagMetadata) + addStringFlags(systemCopyCmd, systemPowerStringFlagMetadata) + addBoolFlags(systemCopyCmd, systemBoolFlagMetadata) + addIntFlags(systemCopyCmd, systemIntFlagMetadata) + addFloatFlags(systemCopyCmd, systemFloatFlagMetadata) + addStringSliceFlags(systemCopyCmd, systemStringSliceFlagMetadata) + addMapFlags(systemCopyCmd, systemMapFlagMetadata) + // Network interface flags + addStringFlags(systemCopyCmd, interfaceStringFlagMetadata) + addBoolFlags(systemCopyCmd, interfaceBoolFlagMetadata) + addStringSliceFlags(systemCopyCmd, interfaceStringSliceFlagMetadata) + // Other systemCopyCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - systemCopyCmd.Flags().String("dhcp-tag", "", "DHCP tag (see manpage or leave blank)") - systemCopyCmd.Flags().Bool("enable-ipxe", false, "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)") - systemCopyCmd.Flags().Bool("enable-menu", false, "enable PXE Menu? (show this system in the PXE menu?)") - systemCopyCmd.Flags().String("mgmt-parameters", "", "Parameters which will be handed to your management application (must be a valid YAML dictionary))") - systemCopyCmd.Flags().String("name-servers", "", "name servers (space delimited)") - systemCopyCmd.Flags().String("name-servers-search", "", "name servers search path (space delimited)") - systemCopyCmd.Flags().String("next-server-v4", "", "next server (IPv4) override (see manpage or leave blank)") - systemCopyCmd.Flags().String("next-server-v6", "", "next server (IPv6) override (see manpage or leave blank)") - systemCopyCmd.Flags().String("filename", "", "DHCP filename override (used to boot non-default bootloaders)") - systemCopyCmd.Flags().String("parent", "", "parent system") - systemCopyCmd.Flags().String("proxy", "", "proxy server URL") - systemCopyCmd.Flags().String("server", "", "server override") - systemCopyCmd.Flags().String("menu", "", "parent boot menu") - systemCopyCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - systemCopyCmd.Flags().String("virt-bridge", "", "virt bridge") - systemCopyCmd.Flags().String("virt-cpus", "", "virt CPUs") - systemCopyCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - systemCopyCmd.Flags().String("virt-file-size", "", "virt file size in GB") - systemCopyCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - systemCopyCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - systemCopyCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)") - systemCopyCmd.Flags().String("gateway", "", "gateway") - systemCopyCmd.Flags().String("hostname", "", "hostname") - systemCopyCmd.Flags().String("image", "", "parent image (if not a profile)") - systemCopyCmd.Flags().String("ipv6-default-device", "", "IPv6 default device") - systemCopyCmd.Flags().Bool("netboot-enabled", false, "PXE (re)install this machine at next boot?") - systemCopyCmd.Flags().String("power-address", "", "power management address (e.g. power-device.example.org)") - systemCopyCmd.Flags().String("power-id", "", "power management ID (usually a plug number or blade name, if power type requires it)") - systemCopyCmd.Flags().String("power-pass", "", "power management password") - systemCopyCmd.Flags().String("power-type", "", "power management script to use") - systemCopyCmd.Flags().String("power-user", "", "power management username") - systemCopyCmd.Flags().String("power-options", "", "additional options, to be passed to the fencing agent") - systemCopyCmd.Flags().String("power-identity-file", "", "identity file to be passed to the fencing agent (SSH key)") - systemCopyCmd.Flags().String("profile", "", "Parent profile") - systemCopyCmd.Flags().String("status", "", "system status. Valid options: development,testing,acceptance,production") - systemCopyCmd.Flags().Bool("virt-pxe-boot", false, "use PXE to build this VM?") - systemCopyCmd.Flags().String("serial-device", "", "serial device number") - systemCopyCmd.Flags().String("serial-baud-rate", "", "serial Baud Rate. Valid options: 2400,4800,9600,19200,38400,57600,115200") - systemCopyCmd.Flags().String("bonding-opts", "", "bonding opts (should be used with --interface)") - systemCopyCmd.Flags().String("ridge-opts", "", "bridge opts (should be used with --interface") - systemCopyCmd.Flags().String("cnames", "", "Cannonical Name Records, should be used with --interface (in quotes, space delimited)") - systemCopyCmd.Flags().String("connected-mode", "", "InfiniBand connected mode (should be used with --interface)") - systemCopyCmd.Flags().String("dns-name", "", "DNS name (should be used with --interface)") - systemCopyCmd.Flags().String("if-gateway", "", "per-Interface Gateway (should be used with --interface)") - systemCopyCmd.Flags().String("interface-master", "", "master interface (Should be used with --interface)") - systemCopyCmd.Flags().String("interface-type", "", `interface Type. Valid options: na,bond,bond_slave,bridge,bridge_slave,bonded_bridge_slave,bmc,infiniband. - (should be used with --interface)`) - systemCopyCmd.Flags().String("ip-address", "", "IPv4 address (should be used with --interface)") - systemCopyCmd.Flags().String("ipv6-address", "", "IPv6 address (should be used with --interface)") - systemCopyCmd.Flags().String("ipv6-default-gateway", "", "IPv6 Default Gateway (should be used with --interface)") - systemCopyCmd.Flags().String("ipv6-mtu", "", "IPv6 MTU") - systemCopyCmd.Flags().String("ipv6-prefix", "", "IPv6 Prefix (should be used with --interface)") - systemCopyCmd.Flags().String("ipv6-secondaries", "", "IPv6 Secondaries (should be used with --interface)") - systemCopyCmd.Flags().String("ipv6-static-routes", "", "IPv6 Static Routes (should be used with --interface)") - systemCopyCmd.Flags().String("mac-address", "", "MAC Address (place 'random' in this field for a random MAC Address.)") - systemCopyCmd.Flags().Bool("management", false, "declares the interface as management interface (should be used with --interface) ") - systemCopyCmd.Flags().String("mtu", "", "MTU") - systemCopyCmd.Flags().String("netmask", "", "subnet mask (should be used with --interface)") - systemCopyCmd.Flags().Bool("static", false, "Is this interface static? (should be used with --interface)") - systemCopyCmd.Flags().String("static-routes", "", "static routes (should be used with --interface)") systemCopyCmd.Flags().String("interface", "", "the interface to operate on") systemCopyCmd.Flags().Bool("delete-interface", false, "delete the given interface (should be used with --interface)") systemCopyCmd.Flags().String("rename-interface", "", "rename the given interface (should be used with --interface)") @@ -639,165 +1160,41 @@ func init() { systemDumpVarsCmd.Flags().String("name", "", "the system name") // local flags for system edit - systemEditCmd.Flags().String("name", "", "the system name") - systemEditCmd.Flags().String("autoinstall", "", "path to automatic installation template") - systemEditCmd.Flags().String("autoinstall-meta", "", "automatic installation metadata") - systemEditCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - systemEditCmd.Flags().String("boot-loaders", "", "boot loader space delimited list (network installation boot loaders). Valid options for list items are: <>,grub,pxe,ipxe") - systemEditCmd.Flags().String("comment", "", "free form text description") - systemEditCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - systemEditCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - systemEditCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - systemEditCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - systemEditCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - systemEditCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - systemEditCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") + addCommonArgs(systemEditCmd) + addStringFlags(systemEditCmd, systemStringFlagMetadata) + addStringFlags(systemEditCmd, systemPowerStringFlagMetadata) + addBoolFlags(systemEditCmd, systemBoolFlagMetadata) + addIntFlags(systemEditCmd, systemIntFlagMetadata) + addFloatFlags(systemEditCmd, systemFloatFlagMetadata) + addStringSliceFlags(systemEditCmd, systemStringSliceFlagMetadata) + addMapFlags(systemEditCmd, systemMapFlagMetadata) + // Network interface flags + addStringFlags(systemEditCmd, interfaceStringFlagMetadata) + addBoolFlags(systemEditCmd, interfaceBoolFlagMetadata) + addStringSliceFlags(systemEditCmd, interfaceStringSliceFlagMetadata) + // Other systemEditCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - systemEditCmd.Flags().String("dhcp-tag", "", "DHCP tag (see manpage or leave blank)") - systemEditCmd.Flags().Bool("enable-ipxe", false, "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)") - systemEditCmd.Flags().Bool("enable-menu", false, "enable PXE Menu? (show this system in the PXE menu?)") - systemEditCmd.Flags().String("mgmt-parameters", "", "Parameters which will be handed to your management application (must be a valid YAML dictionary))") - systemEditCmd.Flags().String("name-servers", "", "name servers (space delimited)") - systemEditCmd.Flags().String("name-servers-search", "", "name servers search path (space delimited)") - systemEditCmd.Flags().String("next-server-v4", "", "next server (IPv4) override (see manpage or leave blank)") - systemEditCmd.Flags().String("next-server-v6", "", "next server (IPv6) override (see manpage or leave blank)") - systemEditCmd.Flags().String("filename", "", "DHCP filename override (used to boot non-default bootloaders)") - systemEditCmd.Flags().String("parent", "", "parent system") - systemEditCmd.Flags().String("proxy", "", "proxy server URL") - systemEditCmd.Flags().String("server", "", "server override") - systemEditCmd.Flags().String("menu", "", "parent boot menu") - systemEditCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - systemEditCmd.Flags().String("virt-bridge", "", "virt bridge") - systemEditCmd.Flags().String("virt-cpus", "", "virt CPUs") - systemEditCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - systemEditCmd.Flags().String("virt-file-size", "", "virt file size in GB") - systemEditCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - systemEditCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - systemEditCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)") - systemEditCmd.Flags().String("gateway", "", "gateway") - systemEditCmd.Flags().String("hostname", "", "hostname") - systemEditCmd.Flags().String("image", "", "parent image (if not a profile)") - systemEditCmd.Flags().String("ipv6-default-device", "", "IPv6 default device") - systemEditCmd.Flags().Bool("netboot-enabled", false, "PXE (re)install this machine at next boot?") - systemEditCmd.Flags().String("power-address", "", "power management address (e.g. power-device.example.org)") - systemEditCmd.Flags().String("power-id", "", "power management ID (usually a plug number or blade name, if power type requires it)") - systemEditCmd.Flags().String("power-pass", "", "power management password") - systemEditCmd.Flags().String("power-type", "", "power management script to use") - systemEditCmd.Flags().String("power-user", "", "power management username") - systemEditCmd.Flags().String("power-options", "", "additional options, to be passed to the fencing agent") - systemEditCmd.Flags().String("power-identity-file", "", "identity file to be passed to the fencing agent (SSH key)") - systemEditCmd.Flags().String("profile", "", "Parent profile") - systemEditCmd.Flags().String("status", "", "system status. Valid options: development,testing,acceptance,production") - systemEditCmd.Flags().Bool("virt-pxe-boot", false, "use PXE to build this VM?") - systemEditCmd.Flags().String("serial-device", "", "serial device number") - systemEditCmd.Flags().String("serial-baud-rate", "", "serial Baud Rate. Valid options: 2400,4800,9600,19200,38400,57600,115200") - systemEditCmd.Flags().String("bonding-opts", "", "bonding opts (should be used with --interface)") - systemEditCmd.Flags().String("ridge-opts", "", "bridge opts (should be used with --interface") - systemEditCmd.Flags().String("cnames", "", "Cannonical Name Records, should be used with --interface (in quotes, space delimited)") - systemEditCmd.Flags().String("connected-mode", "", "InfiniBand connected mode (should be used with --interface)") - systemEditCmd.Flags().String("dns-name", "", "DNS name (should be used with --interface)") - systemEditCmd.Flags().String("if-gateway", "", "per-Interface Gateway (should be used with --interface)") - systemEditCmd.Flags().String("interface-master", "", "master interface (Should be used with --interface)") - systemEditCmd.Flags().String("interface-type", "", `interface Type. Valid options: na,bond,bond_slave,bridge,bridge_slave,bonded_bridge_slave,bmc,infiniband. - (should be used with --interface)`) - systemEditCmd.Flags().String("ip-address", "", "IPv4 address (should be used with --interface)") - systemEditCmd.Flags().String("ipv6-address", "", "IPv6 address (should be used with --interface)") - systemEditCmd.Flags().String("ipv6-default-gateway", "", "IPv6 Default Gateway (should be used with --interface)") - systemEditCmd.Flags().String("ipv6-mtu", "", "IPv6 MTU") - systemEditCmd.Flags().String("ipv6-prefix", "", "IPv6 Prefix (should be used with --interface)") - systemEditCmd.Flags().String("ipv6-secondaries", "", "IPv6 Secondaries (should be used with --interface)") - systemEditCmd.Flags().String("ipv6-static-routes", "", "IPv6 Static Routes (should be used with --interface)") - systemEditCmd.Flags().String("mac-address", "", "MAC Address (place 'random' in this field for a random MAC Address.)") - systemEditCmd.Flags().Bool("management", false, "declares the interface as management interface (should be used with --interface) ") - systemEditCmd.Flags().String("mtu", "", "MTU") - systemEditCmd.Flags().String("netmask", "", "subnet mask (should be used with --interface)") - systemEditCmd.Flags().Bool("static", false, "Is this interface static? (should be used with --interface)") - systemEditCmd.Flags().String("static-routes", "", "static routes (should be used with --interface)") systemEditCmd.Flags().String("interface", "", "the interface to operate on") // local flags for system find - systemFindCmd.Flags().String("name", "", "the system name") + addCommonArgs(systemFindCmd) + addStringFlags(systemFindCmd, systemStringFlagMetadata) + addStringFlags(systemFindCmd, systemPowerStringFlagMetadata) + addBoolFlags(systemFindCmd, systemBoolFlagMetadata) + addIntFlags(systemFindCmd, systemIntFlagMetadata) + addFloatFlags(systemFindCmd, systemFloatFlagMetadata) + addStringSliceFlags(systemFindCmd, systemStringSliceFlagMetadata) + addMapFlags(systemFindCmd, systemMapFlagMetadata) + // Network interface flags + addStringFlags(systemFindCmd, interfaceStringFlagMetadata) + addBoolFlags(systemFindCmd, interfaceBoolFlagMetadata) + addStringSliceFlags(systemFindCmd, interfaceStringSliceFlagMetadata) + // Other systemFindCmd.Flags().String("ctime", "", "") systemFindCmd.Flags().String("depth", "", "") systemFindCmd.Flags().String("mtime", "", "") systemFindCmd.Flags().String("uid", "", "UID") - systemFindCmd.Flags().String("autoinstall", "", "path to automatic installation template") - systemFindCmd.Flags().String("autoinstall-meta", "", "automatic installation metadata") - systemFindCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - systemFindCmd.Flags().String("boot-loaders", "", "boot loader space delimited list (network installation boot loaders). Valid options for list items are: <>,grub,pxe,ipxe") - systemFindCmd.Flags().String("comment", "", "free form text description") - systemFindCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - systemFindCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - systemFindCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - systemFindCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - systemFindCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - systemFindCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - systemFindCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") - systemFindCmd.Flags().String("dhcp-tag", "", "DHCP tag (see manpage or leave blank)") - systemFindCmd.Flags().Bool("enable-ipxe", false, "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)") - systemFindCmd.Flags().Bool("enable-menu", false, "enable PXE Menu? (show this system in the PXE menu?)") - systemFindCmd.Flags().String("mgmt-parameters", "", "Parameters which will be handed to your management application (must be a valid YAML dictionary))") - systemFindCmd.Flags().String("name-servers", "", "name servers (space delimited)") - systemFindCmd.Flags().String("name-servers-search", "", "name servers search path (space delimited)") - systemFindCmd.Flags().String("next-server-v4", "", "next server (IPv4) override (see manpage or leave blank)") - systemFindCmd.Flags().String("next-server-v6", "", "next server (IPv6) override (see manpage or leave blank)") - systemFindCmd.Flags().String("filename", "", "DHCP filename override (used to boot non-default bootloaders)") - systemFindCmd.Flags().String("parent", "", "parent system") - systemFindCmd.Flags().String("proxy", "", "proxy server URL") - systemFindCmd.Flags().String("server", "", "server override") - systemFindCmd.Flags().String("menu", "", "parent boot menu") - systemFindCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - systemFindCmd.Flags().String("virt-bridge", "", "virt bridge") - systemFindCmd.Flags().String("virt-cpus", "", "virt CPUs") - systemFindCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - systemFindCmd.Flags().String("virt-file-size", "", "virt file size in GB") - systemFindCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - systemFindCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - systemFindCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)") - systemFindCmd.Flags().Bool("ipv6-autoconfiguration", false, "IPv6 auto configuration") - systemFindCmd.Flags().Bool("repos-enabled", false, "(re)configure local repos on this machine at next config update?") - systemFindCmd.Flags().String("gateway", "", "gateway") - systemFindCmd.Flags().String("hostname", "", "hostname") - systemFindCmd.Flags().String("image", "", "parent image (if not a profile)") - systemFindCmd.Flags().String("ipv6-default-device", "", "IPv6 default device") - systemFindCmd.Flags().Bool("netboot-enabled", false, "PXE (re)install this machine at next boot?") - systemFindCmd.Flags().String("power-address", "", "power management address (e.g. power-device.example.org)") - systemFindCmd.Flags().String("power-id", "", "power management ID (usually a plug number or blade name, if power type requires it)") - systemFindCmd.Flags().String("power-pass", "", "power management password") - systemFindCmd.Flags().String("power-type", "", "power management script to use") - systemFindCmd.Flags().String("power-user", "", "power management username") - systemFindCmd.Flags().String("power-options", "", "additional options, to be passed to the fencing agent") - systemFindCmd.Flags().String("power-identity-file", "", "identity file to be passed to the fencing agent (SSH key)") - systemFindCmd.Flags().String("profile", "", "Parent profile") - systemFindCmd.Flags().String("status", "", "system status. Valid options: development,testing,acceptance,production") - systemFindCmd.Flags().Bool("virt-pxe-boot", false, "use PXE to build this VM?") - systemFindCmd.Flags().String("serial-device", "", "serial device number") - systemFindCmd.Flags().String("serial-baud-rate", "", "serial Baud Rate. Valid options: 2400,4800,9600,19200,38400,57600,115200") - systemFindCmd.Flags().String("bonding-opts", "", "bonding opts (should be used with --interface)") - systemFindCmd.Flags().String("ridge-opts", "", "bridge opts (should be used with --interface") - systemFindCmd.Flags().String("cnames", "", "Cannonical Name Records, should be used with --interface (in quotes, space delimited)") - systemFindCmd.Flags().String("connected-mode", "", "InfiniBand connected mode (should be used with --interface)") - systemFindCmd.Flags().String("dns-name", "", "DNS name (should be used with --interface)") - systemFindCmd.Flags().String("if-gateway", "", "per-Interface Gateway (should be used with --interface)") - systemFindCmd.Flags().String("interface-master", "", "master interface (Should be used with --interface)") - systemFindCmd.Flags().String("interface-type", "", `interface Type. Valid options: na,bond,bond_slave,bridge,bridge_slave,bonded_bridge_slave,bmc,infiniband. - (should be used with --interface)`) - systemFindCmd.Flags().String("ip-address", "", "IPv4 address (should be used with --interface)") - systemFindCmd.Flags().String("ipv6-address", "", "IPv6 address (should be used with --interface)") - systemFindCmd.Flags().String("ipv6-default-gateway", "", "IPv6 Default Gateway (should be used with --interface)") - systemFindCmd.Flags().String("ipv6-mtu", "", "IPv6 MTU") - systemFindCmd.Flags().String("ipv6-prefix", "", "IPv6 Prefix") - systemFindCmd.Flags().String("ipv6-secondaries", "", "IPv6 Secondaries (should be used with --interface)") - systemFindCmd.Flags().String("ipv6-static-routes", "", "IPv6 Static Routes (should be used with --interface)") - systemFindCmd.Flags().String("mac-address", "", "MAC Address (place 'random' in this field for a random MAC Address.)") - systemFindCmd.Flags().Bool("management", false, "declares the interface as management interface (should be used with --interface) ") - systemFindCmd.Flags().String("mtu", "", "MTU") - systemFindCmd.Flags().String("netmask", "", "subnet mask (should be used with --interface)") - systemFindCmd.Flags().Bool("static", false, "Is this interface static? (should be used with --interface)") - systemFindCmd.Flags().String("static-routes", "", "static routes (should be used with --interface)") systemFindCmd.Flags().String("interface", "", "the interface to operate on") - systemFindCmd.Flags().Bool("delete-interface", false, "delete the given interface (should be used with --interface)") - systemFindCmd.Flags().String("rename-interface", "", "rename the given interface (should be used with --interface)") // local flags for system get-autoinstall systemGetAutoinstallCmd.Flags().String("name", "", "the system name") @@ -819,81 +1216,21 @@ func init() { systemRemoveCmd.Flags().Bool("recursive", false, "also delete child objects") // local flags for system rename - systemRenameCmd.Flags().String("name", "", "the system name") + addCommonArgs(systemRenameCmd) + addStringFlags(systemRenameCmd, systemStringFlagMetadata) + addStringFlags(systemRenameCmd, systemPowerStringFlagMetadata) + addBoolFlags(systemRenameCmd, systemBoolFlagMetadata) + addIntFlags(systemRenameCmd, systemIntFlagMetadata) + addFloatFlags(systemRenameCmd, systemFloatFlagMetadata) + addStringSliceFlags(systemRenameCmd, systemStringSliceFlagMetadata) + addMapFlags(systemRenameCmd, systemMapFlagMetadata) + // Network interface flags + addStringFlags(systemRenameCmd, interfaceStringFlagMetadata) + addBoolFlags(systemRenameCmd, interfaceBoolFlagMetadata) + addStringSliceFlags(systemRenameCmd, interfaceStringSliceFlagMetadata) + // Other systemRenameCmd.Flags().String("newname", "", "the new system name") - systemRenameCmd.Flags().String("autoinstall", "", "path to automatic installation template") - systemRenameCmd.Flags().String("autoinstall-meta", "", "automatic installation metadata") - systemRenameCmd.Flags().String("boot-files", "", "TFTP boot files (files copied into tftpboot beyond the kernel/initrd)") - systemRenameCmd.Flags().String("boot-loaders", "", "boot loader space delimited list (network installation boot loaders). Valid options for list items are: <>,grub,pxe,ipxe") - systemRenameCmd.Flags().String("comment", "", "free form text description") - systemRenameCmd.Flags().String("fetchable-files", "", "fetchable files (templates for tftp, wget or curl)") - systemRenameCmd.Flags().String("kernel-options", "", "kernel options (e.g. selinux=permissive)") - systemRenameCmd.Flags().String("kernel-options-post", "", "post install kernel options (e.g. clocksource=pit noapic)") - systemRenameCmd.Flags().String("mgmt-classes", "", "management classes (for external config management)") - systemRenameCmd.Flags().String("owners", "", "owners list for authz_ownership (space delimited))") - systemRenameCmd.Flags().String("redhat-management-key", "", "RedHat management key (registration key for RHN, Spacewalk, or Satellite)") - systemRenameCmd.Flags().String("template-files", "", "template files (file mappings for built-in config management)") systemRenameCmd.Flags().Bool("in-place", false, "edit items in kopts or autoinstall without clearing the other items") - systemRenameCmd.Flags().String("dhcp-tag", "", "DHCP tag (see manpage or leave blank)") - systemRenameCmd.Flags().Bool("enable-ipxe", false, "enable iPXE? (use iPXE instead of PXELINUX for advanced booting options)") - systemRenameCmd.Flags().Bool("enable-menu", false, "enable PXE Menu? (show this system in the PXE menu?)") - systemRenameCmd.Flags().String("mgmt-parameters", "", "Parameters which will be handed to your management application (must be a valid YAML dictionary))") - systemRenameCmd.Flags().String("name-servers", "", "name servers (space delimited)") - systemRenameCmd.Flags().String("name-servers-search", "", "name servers search path (space delimited)") - systemRenameCmd.Flags().String("next-server-v4", "", "next server (IPv4) override (see manpage or leave blank)") - systemRenameCmd.Flags().String("next-server-v6", "", "next server (IPv6) override (see manpage or leave blank)") - systemRenameCmd.Flags().String("filename", "", "DHCP filename override (used to boot non-default bootloaders)") - systemRenameCmd.Flags().String("parent", "", "parent system") - systemRenameCmd.Flags().String("proxy", "", "proxy server URL") - systemRenameCmd.Flags().String("server", "", "server override") - systemRenameCmd.Flags().String("menu", "", "parent boot menu") - systemRenameCmd.Flags().Bool("virt-auto-boot", false, "auto boot this VM?") - systemRenameCmd.Flags().String("virt-bridge", "", "virt bridge") - systemRenameCmd.Flags().String("virt-cpus", "", "virt CPUs") - systemRenameCmd.Flags().String("virt-disk-driver", "", "the on-disk format for the virtualization disk. Valid options: <>,raw,qcow2,qed,vdi,vdmk") - systemRenameCmd.Flags().String("virt-file-size", "", "virt file size in GB") - systemRenameCmd.Flags().String("virt-path", "", "virt Path (e.g. /directory or VolGroup00)") - systemRenameCmd.Flags().String("virt-ram", "", "virt RAM size in MB") - systemRenameCmd.Flags().String("virt-type", "", "virtualization technology to use. Valid options: <>,qemu,kvm,xenpv,xenfv,vmware,vmwarew,openvz,auto)") - systemRenameCmd.Flags().String("gateway", "", "gateway") - systemRenameCmd.Flags().String("hostname", "", "hostname") - systemRenameCmd.Flags().String("image", "", "parent image (if not a profile)") - systemRenameCmd.Flags().String("ipv6-default-device", "", "IPv6 default device") - systemRenameCmd.Flags().Bool("netboot-enabled", false, "PXE (re)install this machine at next boot?") - systemRenameCmd.Flags().String("power-address", "", "power management address (e.g. power-device.example.org)") - systemRenameCmd.Flags().String("power-id", "", "power management ID (usually a plug number or blade name, if power type requires it)") - systemRenameCmd.Flags().String("power-pass", "", "power management password") - systemRenameCmd.Flags().String("power-type", "", "power management script to use") - systemRenameCmd.Flags().String("power-user", "", "power management username") - systemRenameCmd.Flags().String("power-options", "", "additional options, to be passed to the fencing agent") - systemRenameCmd.Flags().String("power-identity-file", "", "identity file to be passed to the fencing agent (SSH key)") - systemRenameCmd.Flags().String("profile", "", "Parent profile") - systemRenameCmd.Flags().String("status", "", "system status. Valid options: development,testing,acceptance,production") - systemRenameCmd.Flags().Bool("virt-pxe-boot", false, "use PXE to build this VM?") - systemRenameCmd.Flags().String("serial-device", "", "serial device number") - systemRenameCmd.Flags().String("serial-baud-rate", "", "serial Baud Rate. Valid options: 2400,4800,9600,19200,38400,57600,115200") - systemRenameCmd.Flags().String("bonding-opts", "", "bonding opts (should be used with --interface)") - systemRenameCmd.Flags().String("ridge-opts", "", "bridge opts (should be used with --interface") - systemRenameCmd.Flags().String("cnames", "", "Cannonical Name Records, should be used with --interface (in quotes, space delimited)") - systemRenameCmd.Flags().String("connected-mode", "", "InfiniBand connected mode (should be used with --interface)") - systemRenameCmd.Flags().String("dns-name", "", "DNS name (should be used with --interface)") - systemRenameCmd.Flags().String("if-gateway", "", "per-Interface Gateway (should be used with --interface)") - systemRenameCmd.Flags().String("interface-master", "", "master interface (Should be used with --interface)") - systemRenameCmd.Flags().String("interface-type", "", `interface Type. Valid options: na,bond,bond_slave,bridge,bridge_slave,bonded_bridge_slave,bmc,infiniband. - (should be used with --interface)`) - systemRenameCmd.Flags().String("ip-address", "", "IPv4 address (should be used with --interface)") - systemRenameCmd.Flags().String("ipv6-address", "", "IPv6 address (should be used with --interface)") - systemRenameCmd.Flags().String("ipv6-default-gateway", "", "IPv6 Default Gateway") - systemRenameCmd.Flags().String("ipv6-mtu", "", "IPv6 MTU") - systemRenameCmd.Flags().String("ipv6-prefix", "", "IPv6 Prefix") - systemRenameCmd.Flags().String("ipv6-secondaries", "", "IPv6 Secondaries") - systemRenameCmd.Flags().String("ipv6-static-routes", "", "IPv6 Static Routes") - systemRenameCmd.Flags().String("mac-address", "", "MAC Address (place 'random' in this field for a random MAC Address.)") - systemRenameCmd.Flags().Bool("management", false, "declares the interface as management interface (should be used with --interface) ") - systemRenameCmd.Flags().String("mtu", "", "MTU") - systemRenameCmd.Flags().String("netmask", "", "subnet mask (should be used with --interface)") - systemRenameCmd.Flags().Bool("static", false, "Is this interface static? (should be used with --interface)") - systemRenameCmd.Flags().String("static-routes", "", "static routes (should be used with --interface)") systemRenameCmd.Flags().String("interface", "", "the interface to operate on") // local flags for system report diff --git a/cmd/utils.go b/cmd/utils.go new file mode 100644 index 0000000..36fa6a3 --- /dev/null +++ b/cmd/utils.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "fmt" + "strconv" + "strings" + "time" +) + +func convertMapStringToMapInterface(m map[string]string) map[string]interface{} { + result := make(map[string]interface{}) + for k, v := range m { + result[k] = v + } + return result +} + +func covertFloatToUtcTime(t float64) (time.Time, error) { + parts := strings.Split(fmt.Sprintf("%f", t), ".") + seconds, err := strconv.ParseInt(parts[0], 10, 64) + if err != nil { + return time.Time{}, err + } + nanoSeconds, err := strconv.ParseInt(parts[1], 10, 64) + if err != nil { + return time.Time{}, err + } + timezone, err := time.LoadLocation("UTC") + if err != nil { + return time.Time{}, err + } + return time.Unix(seconds, nanoSeconds).In(timezone), nil +} diff --git a/cmd/utils_test.go b/cmd/utils_test.go new file mode 100644 index 0000000..c743d63 --- /dev/null +++ b/cmd/utils_test.go @@ -0,0 +1,39 @@ +package cmd + +import ( + "fmt" + "reflect" + "testing" + "time" +) + +func Test_covertFloatToTime(t *testing.T) { + type args struct { + t float64 + } + tests := []struct { + name string + args args + want time.Time + wantErr bool + }{ + { + name: "float", + args: args{t: 1725887974.610569}, + want: time.Date(2024, time.September, 9, 13, 19, 34, 610569, time.UTC), + wantErr: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := covertFloatToUtcTime(tt.args.t) + fmt.Println(got) + if (err != nil) != tt.wantErr { + t.Errorf("covertFloatToTime() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("covertFloatToTime() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/validateAutoinstalls.go b/cmd/validateAutoinstalls.go index ffaf2ff..ed3656f 100644 --- a/cmd/validateAutoinstalls.go +++ b/cmd/validateAutoinstalls.go @@ -5,6 +5,7 @@ package cmd import ( + "fmt" "github.com/spf13/cobra" ) @@ -13,9 +14,14 @@ var validateAutoinstallsCmd = &cobra.Command{ Use: "validate-autoinstalls", Short: "Autoinstall validation", Long: `Validates the autoinstall files.`, - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { generateCobblerClient() - // TODO: call cobblerclient + eventId, err := Client.BackgroundValidateAutoinstallFiles() + if err != nil { + return err + } + fmt.Printf("Event ID: %s\n", eventId) + return nil }, } diff --git a/cmd/version.go b/cmd/version.go index bfe932a..29c4705 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -5,7 +5,9 @@ package cmd import ( + "fmt" "github.com/spf13/cobra" + "runtime/debug" ) // versionCmd represents the version command @@ -15,11 +17,33 @@ var versionCmd = &cobra.Command{ Long: `Shows the Cobbler server version.`, Run: func(cmd *cobra.Command, args []string) { generateCobblerClient() - - // TODO: call cobblerclient + version, err := Client.ExtendedVersion() + if err != nil { + fmt.Println(err) + } + clientVersion, cliVersion, _ := getClientVersion() + fmt.Printf("Cobbler %s\n", version.Version) + fmt.Printf(" source: %s, %s\n", version.Gitstamp, version.Gitdate) + fmt.Printf(" build time: %s\n", version.Builddate) + fmt.Printf(" cli: %s\n", cliVersion) + fmt.Printf(" client: %s\n", clientVersion) }, } +func getClientVersion() (string, string, error) { + bi, _ := debug.ReadBuildInfo() + var clientVersion, cliVersion string + for _, dep := range bi.Deps { + switch dep.Path { + case "github.com/cobbler/cli": + cliVersion = dep.Version + case "github.com/cobbler/cobblerclient": + clientVersion = dep.Version + } + } + return clientVersion, cliVersion, nil +} + func init() { rootCmd.AddCommand(versionCmd) } diff --git a/go.mod b/go.mod index f24ff76..3de328b 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,16 @@ module github.com/cobbler/cli go 1.19 require ( - github.com/cobbler/cobblerclient v0.4.3 + github.com/cobbler/cobblerclient v0.5.1 github.com/spf13/cobra v1.8.1 + github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 ) require ( github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect @@ -22,7 +24,6 @@ require ( github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect diff --git a/go.sum b/go.sum index 44eb9e0..c1a0f24 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,5 @@ -github.com/ContainerSolutions/go-utils v0.1.0 h1:cjCek/u3qVUSDejTqdSNQFummm9W/OUR7yfzplTaDN0= -github.com/ContainerSolutions/go-utils v0.1.0/go.mod h1:kwETtmd5o1Ih1kSnvyZ5CPBNREDObipu4e0zWF3Vo8g= -github.com/cobbler/cobblerclient v0.4.2 h1:lt+lh2vs+wUzrzn3hEzyb1M4YC+LVt41b4Ob7MTIQdc= -github.com/cobbler/cobblerclient v0.4.2/go.mod h1:fjhmlYZN/VqHVmphwQiRpB/aNFsAVRQzB52ioASnoxg= -github.com/cobbler/cobblerclient v0.4.3 h1:dGjcayCorEB0WUk4l3xa11rtRuJjyG4SdOG6qWk+jtI= -github.com/cobbler/cobblerclient v0.4.3/go.mod h1:fjhmlYZN/VqHVmphwQiRpB/aNFsAVRQzB52ioASnoxg= +github.com/cobbler/cobblerclient v0.5.1 h1:LaK26SLuN6hAetNei94djkT9+rl058Bh2uPgZO2OnP8= +github.com/cobbler/cobblerclient v0.5.1/go.mod h1:n6b8fTUOlg7BdMl6FeifUm4Uk1JY6/tlTlOClV4x2Wc= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -13,20 +9,20 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= +github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/kolo/xmlrpc v0.0.0-20190909154602-56d5ec7c422e h1:JZPIpxHmcXiQn101f6P9wkfRZs2A9268tHHnanj+esA= -github.com/kolo/xmlrpc v0.0.0-20190909154602-56d5ec7c422e/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= @@ -71,7 +67,6 @@ golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjs golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= From 2ed128f0a2008759b34f54c678ccb99b2f98dbb7 Mon Sep 17 00:00:00 2001 From: Enno Gotthold Date: Mon, 12 Aug 2024 16:45:31 +0200 Subject: [PATCH 3/5] CI: Update linting workflows --- .github/workflows/lint.yml | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bab0247..2112e93 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,9 +12,9 @@ jobs: lint_docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v5 with: python-version: 3.9 - name: Install dependencies @@ -27,27 +27,10 @@ jobs: name: Linting Go runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: golangci-lint - uses: golangci/golangci-lint-action@v2 + # https://github.com/golangci/golangci-lint-action + uses: golangci/golangci-lint-action@v6 with: - # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version version: v1.29 - - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. args: -D errcheck - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true - - # Optional: if set to true then the action will use pre-installed Go. - # skip-go-installation: true - - # Optional: if set to true then the action don't cache or restore ~/go/pkg. - # skip-pkg-cache: true - - # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. - # skip-build-cache: true From 081d8c9e2a14d5fc20f76fe634cc6c3efd57eed0 Mon Sep 17 00:00:00 2001 From: Enno Gotthold Date: Mon, 12 Aug 2024 16:48:24 +0200 Subject: [PATCH 4/5] CI: Add setup go to linting workflow --- .github/workflows/lint.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2112e93..5d39811 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -28,6 +28,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: stable - name: golangci-lint # https://github.com/golangci/golangci-lint-action uses: golangci/golangci-lint-action@v6 From 17f6aea0076d0f71e1848ad63915c4f5f8cbe4b5 Mon Sep 17 00:00:00 2001 From: Enno Gotthold Date: Mon, 12 Aug 2024 16:56:27 +0200 Subject: [PATCH 5/5] CI: Update golangci version Co-authored-by: Ludovic Fernandez --- .github/workflows/lint.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5d39811..5d571d8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -35,5 +35,4 @@ jobs: # https://github.com/golangci/golangci-lint-action uses: golangci/golangci-lint-action@v6 with: - version: v1.29 - args: -D errcheck + version: v1.61