diff --git a/.github/workflows/releasebuild.yml b/.github/workflows/releasebuild.yml new file mode 100644 index 00000000..92d41c77 --- /dev/null +++ b/.github/workflows/releasebuild.yml @@ -0,0 +1,88 @@ +--- +name: releasebuild + +on: + release: + types: + - published + +jobs: + + distbuild: + name: Checkout code and build tar.gz + runs-on: ubuntu-latest + steps: + - name: Set the version + id: version_number + run: | + echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV + - name: Set the git source location + id: source_loc + run: | + echo "SRC=${GITHUB_REF}" >> $GITHUB_ENV + - name: Checkout Code + uses: actions/checkout@v2 + - name: Install Go + uses: actions/setup-go@v2 + - uses: actions/cache@v2 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - name: Build tar.gz + run: make dist + - name: Upload mock script + uses: actions/upload-artifact@v2 + with: + name: mockbuild.sh + path: ci/mockbuild.sh + - name: Upload tar.gz + uses: actions/upload-artifact@v2 + with: + name: warewulf-${{ env.VERSION }}.tar.gz + path: warewulf-${{ env.VERSION }}.tar.gz + + rpmbuild: + name: Build using Rocky Linux + needs: distbuild + runs-on: ubuntu-latest + container: + image: rockylinux/rockylinux + options: --privileged + steps: + - name: Set the version + id: version_number + run: | + echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV + - name: Download mock build script + uses: actions/download-artifact@v2 + with: + name: mockbuild.sh + - name: Download tar.gz + uses: actions/download-artifact@v2 + with: + name: warewulf-${{ env.VERSION }}.tar.gz + - name: Set execute bit + run: chmod +x mockbuild.sh + - name: Run rpm build script + run: ./mockbuild.sh ${{ env.VERSION }} + - name: Upload EL8 RPM + uses: actions/upload-artifact@v2 + with: + name: warewulf-${{ env.VERSION }}-${{ env.RELEASE }}.el8.x86_64.rpm + path: warewulf-${{ env.VERSION }}-${{ env.RELEASE }}.el8.x86_64.rpm + - name: Upload EL7 RPM + uses: actions/upload-artifact@v2 + with: + name: warewulf-${{ env.VERSION }}-${{ env.RELEASE }}.el7.x86_64.rpm + path: warewulf-${{ env.VERSION }}-${{ env.RELEASE }}.el7.x86_64.rpm + - name: Upload Assets To Release + uses: xresloader/upload-to-github-release@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + file: 'warewulf-${{ env.VERSION }}.tar.gz;warewulf-${{ env.VERSION }}-${{ env.RELEASE }}.*.rpm' + draft: false + update_latest_release: true + tags: true diff --git a/.gitignore b/.gitignore index 9bfa6313..e1845cba 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,7 @@ /wwctl /bash_completion /man_page + +# other created files +/usr/share/man/man1/ +/etc/bash_completion.d/ diff --git a/Makefile b/Makefile index 0babd543..53efc9f1 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,20 @@ .PHONY: all -VERSION := 4.2.0 +VERSION ?= 4.2.0 +RELEASE ?= 1 + +SRC ?= main + +VERSION_FULL ?= $(shell test -e .git && git describe --tags --long --first-parent) +ifeq ($(VERSION_FULL),) +VERSION_FULL := $(VERSION) +endif + # auto installed tooling TOOLS_DIR := .tools TOOLS_BIN := $(TOOLS_DIR)/bin +CONFIG := $(shell pwd) # tools GO_TOOLS_BIN := $(addprefix $(TOOLS_BIN)/, $(notdir $(GO_TOOLS))) @@ -19,8 +29,14 @@ export GOPROXY # built tags needed for wwbuild binary WW_BUILD_GO_BUILD_TAGS := containers_image_openpgp containers_image_ostree +# system-overlay directories for install_wwclient +OVERLAY_DIR ?= $(DESTDIR)/var/warewulf/overlays/system/ +overlays = ${dir ${wildcard ${OVERLAY_DIR}*/}} + all: vendor wwctl wwclient bash_completion man_page +build: lint test-it vet all + # set the go tools into the tools bin. setup_tools: $(GO_TOOLS_BIN) $(GOLANGCI_LINT) @@ -48,6 +64,22 @@ lint: setup_tools @echo Running golangci-lint... @$(GOLANGCI_LINT) run --build-tags "$(WW_BUILD_GO_BUILD_TAGS)" --skip-dirs internal/pkg/staticfiles ./... +vet: + go vet ./... + +test-it: + go test -v ./... + +# Generate test coverage +test-cover: ## Run test coverage and generate html report + rm -fr coverage + mkdir coverage + go list -f '{{if gt (len .TestGoFiles) 0}}"go test -covermode count -coverprofile {{.Name}}.coverprofile -coverpkg ./... {{.ImportPath}}"{{end}}' ./... | xargs -I {} bash -c {} + echo "mode: count" > coverage/cover.out + grep -h -v "^mode:" *.coverprofile >> "coverage/cover.out" + rm *.coverprofile + go tool cover -html=coverage/cover.out -o=coverage/cover.html + debian: all files: all @@ -61,6 +93,7 @@ files: all install -d -m 0755 $(DESTDIR)/usr/share/man/man1 test -f $(DESTDIR)/etc/warewulf/warewulf.conf || install -m 644 etc/warewulf.conf $(DESTDIR)/etc/warewulf/ test -f $(DESTDIR)/etc/warewulf/hosts.tmpl || install -m 644 etc/hosts.tmpl $(DESTDIR)/etc/warewulf/ + test -f $(DESTDIR)/etc/warewulf/nodes.conf || install -m 640 etc/nodes.conf $(DESTDIR)/etc/warewulf/ cp -r etc/dhcp $(DESTDIR)/etc/warewulf/ cp -r etc/ipxe $(DESTDIR)/etc/warewulf/ cp -r overlays $(DESTDIR)/var/warewulf/ @@ -68,18 +101,18 @@ files: all chmod 600 $(DESTDIR)/var/warewulf/overlays/system/default/etc/ssh/ssh* chmod 644 $(DESTDIR)/var/warewulf/overlays/system/default/etc/ssh/ssh*.pub.ww mkdir -p $(DESTDIR)/var/warewulf/overlays/system/default/warewulf/bin/ - cp wwclient $(DESTDIR)/var/warewulf/overlays/system/default/warewulf/bin/ cp wwctl $(DESTDIR)/usr/bin/ mkdir -p $(DESTDIR)/usr/lib/firewalld/services install -c -m 0644 include/firewalld/warewulf.xml $(DESTDIR)/usr/lib/firewalld/services mkdir -p $(DESTDIR)/usr/lib/systemd/system install -c -m 0644 include/systemd/warewulfd.service $(DESTDIR)/usr/lib/systemd/system - ./bash_completion $(DESTDIR)/etc/bash_completion.d/warewulf - ./man_page $(DESTDIR)/usr/share/man/man1 - gzip --force $(DESTDIR)/usr/share/man/man1/wwctl*1 -# systemctl daemon-reload -# cp -r tftpboot/* /var/lib/tftpboot/warewulf/ipxe/ -# restorecon -r /var/lib/tftpboot/warewulf + cp etc/bash_completion.d/warewulf $(DESTDIR)/etc/bash_completion.d/ + cp usr/share/man/man1/* $(DESTDIR)/usr/share/man/man1/ + +init: + systemctl daemon-reload + cp -r tftpboot/* /var/lib/tftpboot/warewulf/ipxe/ + restorecon -r /var/lib/tftpboot/warewulf debfiles: debian chmod +x $(DESTDIR)/var/warewulf/overlays/system/debian/init @@ -89,23 +122,35 @@ debfiles: debian cp wwclient $(DESTDIR)/var/warewulf/overlays/system/debian/warewulf/bin/ wwctl: - cd cmd/wwctl; GOOS=linux go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../wwctl + cd cmd/wwctl; GOOS=linux go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/version.Version=$(VERSION_FULL)'" -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../wwctl wwclient: cd cmd/wwclient; CGO_ENABLED=0 GOOS=linux go build -mod vendor -a -ldflags '-extldflags -static' -o ../../wwclient +install_wwclient: wwclient + $(foreach overlay, ${overlays}, install -D -m 0755 wwclient ${overlay}warewulf/bin/wwclient;) + bash_completion: - cd cmd/bash_completion; go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../bash_completion + cd cmd/bash_completion && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=$(CONFIG)/etc/warewulf.conf'\ + -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=$(CONFIG)/etc/nodes.conf'"\ + -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../bash_completion + install -d -m 0755 ./etc/bash_completion.d/ + ./bash_completion ./etc/bash_completion.d/warewulf man_page: - cd cmd/man_page; go build -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../man_page + cd cmd/man_page && go build -ldflags="-X 'github.com/hpcng/warewulf/internal/pkg/warewulfconf.ConfigFile=$(CONFIG)/etc/warewulf.conf'\ + -X 'github.com/hpcng/warewulf/internal/pkg/node.ConfigFile=$(CONFIG)/etc/nodes.conf'"\ + -mod vendor -tags "$(WW_BUILD_GO_BUILD_TAGS)" -o ../../man_page + install -d -m 0755 ./usr/share/man/man1 + ./man_page ./usr/share/man/man1 + gzip --force ./usr/share/man/man1/wwctl*1 dist: vendor rm -rf _dist/warewulf-$(VERSION) mkdir -p _dist/warewulf-$(VERSION) - git archive --format=tar main | tar -xf - -C _dist/warewulf-$(VERSION) + git archive --format=tar $(SRC) | tar -xf - -C _dist/warewulf-$(VERSION) cp -r vendor _dist/warewulf-$(VERSION)/ - sed -e 's/@VERSION@/$(VERSION)/g' warewulf.spec.in > _dist/warewulf-$(VERSION)/warewulf.spec + sed -e 's/@VERSION@/$(VERSION)/g; s/@RELEASE@/$(RELEASE)/g' warewulf.spec.in > _dist/warewulf-$(VERSION)/warewulf.spec cd _dist; tar -czf ../warewulf-$(VERSION).tar.gz warewulf-$(VERSION) clean: @@ -116,7 +161,7 @@ clean: rm -f bash_completion rm -f man_page -install: files +install: files install_wwclient debinstall: files debfiles diff --git a/README.md b/README.md index c5f2b056..696d80c9 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,10 @@ #### Quick Links: -* [Documentation](https://warewulf.hpcng.org) -* [GitHub](http://github.com/hpcng/warewulf) +* [Website](https://warewulf.org) +* [Documentation](https://warewulf.org/docs) +* [Download / Releases](http://github.com/hpcng/warewulf/releases) +* [Support](http://www.ciq.co/warewulf) ## About Warewulf diff --git a/ci/mockbuild.sh b/ci/mockbuild.sh new file mode 100644 index 00000000..1547310a --- /dev/null +++ b/ci/mockbuild.sh @@ -0,0 +1,17 @@ +#!/usr/bin/bash + +VERSION=$1 + +dnf install -y epel-release +dnf install -y mock + +tar -xf warewulf-${VERSION}.tar.gz warewulf-${VERSION}/warewulf.spec + +RELEASE=$(grep 'Release: ' warewulf-${VERSION}/warewulf.spec | cut -d ':' -f2 | awk -F'%' '{print $1}' | tr -d ' ') +echo RELEASE=${RELEASE} >> $GITHUB_ENV + +mock -r epel-8-x86_64 --rebuild --spec=warewulf-${VERSION}/warewulf.spec --sources=. +mv /var/lib/mock/epel-8-x86_64/result/warewulf-${VERSION}-${RELEASE}.el8.x86_64.rpm . + +mock -r epel-7-x86_64 --rebuild --spec=warewulf-${VERSION}/warewulf.spec --sources=. +mv /var/lib/mock/epel-7-x86_64/result/warewulf-${VERSION}-${RELEASE}.el7.x86_64.rpm . \ No newline at end of file diff --git a/cmd/man_page/man_page.go b/cmd/man_page/man_page.go index 369026fa..8a35a3be 100644 --- a/cmd/man_page/man_page.go +++ b/cmd/man_page/man_page.go @@ -1,4 +1,5 @@ -// usage: ./bash_completion +// Generate man pages for wwctl command. +// usage: ./man_page package main import ( diff --git a/etc/nodes.conf b/etc/nodes.conf new file mode 100644 index 00000000..3999a8cd --- /dev/null +++ b/etc/nodes.conf @@ -0,0 +1,4 @@ +nodeprofiles: + default: + comment: This profile is automatically included for each node +nodes: {} diff --git a/internal/app/wwctl/configure/dhcp/root.go b/internal/app/wwctl/configure/dhcp/root.go index 76ec9e97..d27f8ad3 100644 --- a/internal/app/wwctl/configure/dhcp/root.go +++ b/internal/app/wwctl/configure/dhcp/root.go @@ -6,7 +6,8 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "dhcp", + DisableFlagsInUseLine: true, + Use: "dhcp [OPTIONS]", Short: "Manage and initialize DHCP", Long: "DHCP is a dependent service to Warewulf. This command will configure DHCP as defined\n" + "in the warewulf.conf file.", diff --git a/internal/app/wwctl/configure/hosts/root.go b/internal/app/wwctl/configure/hosts/root.go index a8294453..84877a40 100644 --- a/internal/app/wwctl/configure/hosts/root.go +++ b/internal/app/wwctl/configure/hosts/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "hosts", + DisableFlagsInUseLine: true, + Use: "hosts [OPTIONS]", Short: "Update the /etc/hosts file", Long: "Write out the /etc/hosts file based on the Warewulf template (hosts.tmpl) in the\n" + "Warewulf configuration directory.", diff --git a/internal/app/wwctl/configure/nfs/root.go b/internal/app/wwctl/configure/nfs/root.go index 4121efa9..4e074021 100644 --- a/internal/app/wwctl/configure/nfs/root.go +++ b/internal/app/wwctl/configure/nfs/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "nfs", + DisableFlagsInUseLine: true, + Use: "nfs [OPTIONS]", Short: "Manage and initialize NFS", Long: "NFS is an optional dependent service of Warewulf, this tool will automatically\n" + "configure NFS as per the configuration in the warewulf.conf file.", diff --git a/internal/app/wwctl/configure/root.go b/internal/app/wwctl/configure/root.go index aece4bfa..6d8a2c41 100644 --- a/internal/app/wwctl/configure/root.go +++ b/internal/app/wwctl/configure/root.go @@ -15,7 +15,8 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "configure", + DisableFlagsInUseLine: true, + Use: "configure [OPTIONS]", Short: "Manage system services", Long: "This application allows you to manage and initialize Warewulf dependent system\n" + "services based on the configuration in the warewulf.conf file.", diff --git a/internal/app/wwctl/configure/ssh/root.go b/internal/app/wwctl/configure/ssh/root.go index 8f7ba046..2246c0db 100644 --- a/internal/app/wwctl/configure/ssh/root.go +++ b/internal/app/wwctl/configure/ssh/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "ssh", + DisableFlagsInUseLine: true, + Use: "ssh [OPTIONS]", Short: "Manage and initialize SSH", Long: "SSH is an optionally dependent service for Warewulf, this tool will automatically\n" + "setup the ssh keys nodes using the 'default' system overlay as well as user owned\n" + diff --git a/internal/app/wwctl/configure/tftp/root.go b/internal/app/wwctl/configure/tftp/root.go index 9b5edb2e..7629fa2c 100644 --- a/internal/app/wwctl/configure/tftp/root.go +++ b/internal/app/wwctl/configure/tftp/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "tftp", + DisableFlagsInUseLine: true, + Use: "tftp [OPTIONS]", Short: "Manage and initialize TFTP", Long: "TFTP is a dependent service of Warewulf, this tool will enable the tftp services\n" + "on your Warewulf master.", diff --git a/internal/app/wwctl/container/build/root.go b/internal/app/wwctl/container/build/root.go index d02e651d..93ff98cd 100644 --- a/internal/app/wwctl/container/build/root.go +++ b/internal/app/wwctl/container/build/root.go @@ -7,9 +7,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "build [flags] [container name]...", + DisableFlagsInUseLine: true, + Use: "build [OPTIONS] CONTAINER [...]", Short: "(Re)build a bootable VNFS image", - Long: "This command will build a bootable VNFS image from an imported container image.", + Long: "This command will build a bootable VNFS image from imported CONTAINER image(s).", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/internal/app/wwctl/container/delete/root.go b/internal/app/wwctl/container/delete/root.go index fe0e5d23..64e909ba 100644 --- a/internal/app/wwctl/container/delete/root.go +++ b/internal/app/wwctl/container/delete/root.go @@ -7,9 +7,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "delete [flags] [container name]...", + DisableFlagsInUseLine: true, + Use: "delete [OPTIONS] CONTAINER [...]", Short: "Delete an imported container", - Long: "This command will delete a container that has been imported into Warewulf.", + Long: "This command will delete CONTAINERs that have been imported into Warewulf.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/container/exec/child/root.go b/internal/app/wwctl/container/exec/child/root.go index 8a9d422b..0b7dde7c 100644 --- a/internal/app/wwctl/container/exec/child/root.go +++ b/internal/app/wwctl/container/exec/child/root.go @@ -4,6 +4,7 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ + DisableFlagsInUseLine: true, Use: "__child", Hidden: true, RunE: CobraRunE, diff --git a/internal/app/wwctl/container/exec/root.go b/internal/app/wwctl/container/exec/root.go index 8e418541..a3022599 100644 --- a/internal/app/wwctl/container/exec/root.go +++ b/internal/app/wwctl/container/exec/root.go @@ -8,10 +8,11 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "exec [flags] [container name] [command]", + DisableFlagsInUseLine: true, + Use: "exec [OPTIONS] CONTAINER COMMAND", Short: "Run a command inside of a Warewulf container", - Long: "This command will allow you to run any command inside of a given\n" + - "warewulf container. This is commonly used with an interactive shell such as /bin/bash\n" + + Long: "Run a COMMAND inside of a warewulf CONTAINER.\n" + + "This is commonly used with an interactive shell such as /bin/bash\n" + "to run a virtual environment within the container.", RunE: CobraRunE, Args: cobra.MinimumNArgs(2), diff --git a/internal/app/wwctl/container/imprt/root.go b/internal/app/wwctl/container/imprt/root.go index 1725d06a..061c0efe 100644 --- a/internal/app/wwctl/container/imprt/root.go +++ b/internal/app/wwctl/container/imprt/root.go @@ -4,10 +4,14 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "import [flags] [container source URI]", + DisableFlagsInUseLine: true, + Use: "import [OPTIONS] SOURCE [NAME]", Short: "Import a container into Warewulf", - Long: "This command will pull and import a container into Warewulf so it can be used\n" + - "as a source to create a bootable VNFS image.", + Long: +`This command will pull and import a container into Warewulf from SOURCE, +optionally renaming it to NAME. The SOURCE must be in a supported URI format. +Imported containers are used to create bootable VNFS images.`, + Example: "wwctl container import docker://warewulf/centos-8 my_container", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), } diff --git a/internal/app/wwctl/container/list/root.go b/internal/app/wwctl/container/list/root.go index 273eb371..908aae77 100644 --- a/internal/app/wwctl/container/list/root.go +++ b/internal/app/wwctl/container/list/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "list [flags]", + DisableFlagsInUseLine: true, + Use: "list [OPTIONS]", Short: "List imported Warewulf containers", Long: "This command will show you the containers that are imported into Warewulf.", RunE: CobraRunE, diff --git a/internal/app/wwctl/container/root.go b/internal/app/wwctl/container/root.go index 79bc0a5f..02efc3b0 100644 --- a/internal/app/wwctl/container/root.go +++ b/internal/app/wwctl/container/root.go @@ -11,11 +11,12 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "container", + DisableFlagsInUseLine: true, + Use: "container COMMAND [OPTIONS]", Short: "Container / VNFS image management", Long: "Starting with version 4, Warewulf uses containers to build the bootable VNFS\n" + - "images for nodes to boot. These commands will help you import, management, and\n" + - "transform containers into bootable Warewulf VNFS images.", + "node images. These commands will help you import, manage, and transform\n" + + "containers into bootable Warewulf VNFS images.", Aliases: []string{"vnfs"}, } ) diff --git a/internal/app/wwctl/kernel/delete/root.go b/internal/app/wwctl/kernel/delete/root.go index 4e714b46..5fc68bea 100644 --- a/internal/app/wwctl/kernel/delete/root.go +++ b/internal/app/wwctl/kernel/delete/root.go @@ -7,9 +7,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "delete [flags] [kernel version]...", - Short: "Delete an imported kernel", - Long: "This command will delete a kernel that has been imported into Warewulf.", + DisableFlagsInUseLine: true, + Use: "delete [OPTIONS] KERNEL [...]", + Short: "Delete imported kernels", + Long: "This command will delete KERNEL versions that have been imported into Warewulf.", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/internal/app/wwctl/kernel/imprt/main.go b/internal/app/wwctl/kernel/imprt/main.go index f8b7f143..f2412240 100644 --- a/internal/app/wwctl/kernel/imprt/main.go +++ b/internal/app/wwctl/kernel/imprt/main.go @@ -18,7 +18,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { // Checking if container flag was set, then overwriting OptRoot kernelVersion := args[0] kernelName := kernelVersion - if len(args) > 1 { + if len(args) > 1 { kernelName = args[1] } if OptContainer != "" { diff --git a/internal/app/wwctl/kernel/imprt/root.go b/internal/app/wwctl/kernel/imprt/root.go index cc6e2539..7d398264 100644 --- a/internal/app/wwctl/kernel/imprt/root.go +++ b/internal/app/wwctl/kernel/imprt/root.go @@ -9,10 +9,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "import [flags] [kernel version]", + DisableFlagsInUseLine: true, + Use: "import [OPTIONS] KERNEL", Short: "Import Kernel version into Warewulf", - Long: "This will import a Kernel version from the control node into Warewulf for nodes\n" + - "to be configured to boot on.", + Long: "This will import a boot KERNEL version from the control node into Warewulf", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), } diff --git a/internal/app/wwctl/kernel/list/root.go b/internal/app/wwctl/kernel/list/root.go index 2fcceb4b..fba84101 100644 --- a/internal/app/wwctl/kernel/list/root.go +++ b/internal/app/wwctl/kernel/list/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "list [flags]", + DisableFlagsInUseLine: true, + Use: "list [OPTIONS]", Short: "List imported Kernel images", Long: "This command will list the kernels that have been imported into Warewulf.", RunE: CobraRunE, diff --git a/internal/app/wwctl/kernel/root.go b/internal/app/wwctl/kernel/root.go index 33d74b0a..bb0cdb8d 100644 --- a/internal/app/wwctl/kernel/root.go +++ b/internal/app/wwctl/kernel/root.go @@ -9,10 +9,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "kernel", + DisableFlagsInUseLine: true, + Use: "kernel COMMAND [OPTIONS]", Short: "Kernel Image Management", - Long: "This command is for management of Warewulf Kernels to be used for\n" + - "bootstrapping nodes", + Long: "This command manages Warewulf Kernels used for bootstrapping nodes", } ) diff --git a/internal/app/wwctl/node/add/main.go b/internal/app/wwctl/node/add/main.go index 917d5f42..0902be93 100644 --- a/internal/app/wwctl/node/add/main.go +++ b/internal/app/wwctl/node/add/main.go @@ -2,18 +2,23 @@ package add import ( "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/pkg/errors" "github.com/spf13/cobra" ) func CobraRunE(cmd *cobra.Command, args []string) error { + var count uint nodeDB, err := node.New() if err != nil { return errors.Wrap(err, "failed to open node database") } - for _, a := range args { + node_args := hostlist.Expand(args) + + for _, a := range node_args { n, err := nodeDB.AddNode(a) if err != nil { return errors.Wrap(err, "failed to add node") @@ -34,14 +39,16 @@ func CobraRunE(cmd *cobra.Command, args []string) error { return errors.New("you must include the '--netdev' option") } + NewIpaddr := util.IncrementIPv4(SetIpaddr, count) + if _, ok := n.NetDevs[SetNetDev]; !ok { var netdev node.NetDevEntry n.NetDevs[SetNetDev] = &netdev } - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Ipaddr to: %s\n", n.Id.Get(), SetNetDev, SetIpaddr) + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Ipaddr to: %s\n", n.Id.Get(), SetNetDev, NewIpaddr) - n.NetDevs[SetNetDev].Ipaddr.Set(SetIpaddr) + n.NetDevs[SetNetDev].Ipaddr.Set(NewIpaddr) n.NetDevs[SetNetDev].Default.SetB(true) err := nodeDB.NodeUpdate(n) if err != nil { @@ -123,7 +130,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { return errors.Wrap(err, "failed to update nodedb") } } - + count++ } return errors.Wrap(nodeDB.Persist(), "failed to persist nodedb") diff --git a/internal/app/wwctl/node/add/root.go b/internal/app/wwctl/node/add/root.go index c4c716b3..2df38971 100644 --- a/internal/app/wwctl/node/add/root.go +++ b/internal/app/wwctl/node/add/root.go @@ -4,9 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "add [flags] [node pattern]", + DisableFlagsInUseLine: true, + Use: "add [OPTIONS] NODENAME", Short: "Add new node to Warewulf", - Long: "This command will add a new node to Warewulf.", + Long: "This command will add a new node named NODENAME to Warewulf.", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), } diff --git a/internal/app/wwctl/node/console/power.go b/internal/app/wwctl/node/console/power.go index 2c2bf26f..aa9fcfa7 100644 --- a/internal/app/wwctl/node/console/power.go +++ b/internal/app/wwctl/node/console/power.go @@ -7,6 +7,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/power" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" ) @@ -25,6 +26,8 @@ func CobraRunE(cmd *cobra.Command, args []string) error { os.Exit(1) } + args = hostlist.Expand(args) + if len(args) > 0 { nodes = node.FilterByName(nodes, args) } else { @@ -46,11 +49,13 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } ipmiCmd := power.IPMI{ - NodeName: node.Id.Get(), - HostName: node.IpmiIpaddr.Get(), - User: node.IpmiUserName.Get(), - Password: node.IpmiPassword.Get(), - AuthType: "MD5", + NodeName: node.Id.Get(), + HostName: node.IpmiIpaddr.Get(), + Port: node.IpmiPort.Get(), + User: node.IpmiUserName.Get(), + Password: node.IpmiPassword.Get(), + AuthType: "MD5", + Interface: node.IpmiInterface.Get(), } err := ipmiCmd.Console() diff --git a/internal/app/wwctl/node/console/root.go b/internal/app/wwctl/node/console/root.go index 6a4e1b9f..a0d05f4e 100644 --- a/internal/app/wwctl/node/console/root.go +++ b/internal/app/wwctl/node/console/root.go @@ -6,9 +6,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "console [flags] [node pattern]", + DisableFlagsInUseLine: true, + Use: "console [OPTIONS] NODENAME", Short: "Connect to IPMI console", - Long: "Start IPMI console for a singe node.", + Long: "Start a new IPMI console for NODENAME.", Args: cobra.MinimumNArgs(1), RunE: CobraRunE, } diff --git a/internal/app/wwctl/node/delete/main.go b/internal/app/wwctl/node/delete/main.go index 9f70e819..ed184291 100644 --- a/internal/app/wwctl/node/delete/main.go +++ b/internal/app/wwctl/node/delete/main.go @@ -6,6 +6,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/manifoldco/promptui" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -27,6 +28,8 @@ func CobraRunE(cmd *cobra.Command, args []string) error { os.Exit(1) } + args = hostlist.Expand(args) + for _, r := range args { var match bool for _, n := range nodes { diff --git a/internal/app/wwctl/node/delete/root.go b/internal/app/wwctl/node/delete/root.go index b62664f2..95a96f8f 100644 --- a/internal/app/wwctl/node/delete/root.go +++ b/internal/app/wwctl/node/delete/root.go @@ -7,9 +7,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "delete [flags] [exact node name]...", + DisableFlagsInUseLine: true, + Use: "delete [OPTIONS] NODE [NODE ...]", Short: "Delete a node from Warewulf", - Long: "This command will remove a node from the Warewulf node configuration.", + Long: "This command will remove NODE(s) from the Warewulf node configuration.", Args: cobra.MinimumNArgs(1), RunE: CobraRunE, Aliases: []string{"rm", "del"}, diff --git a/internal/app/wwctl/node/list/main.go b/internal/app/wwctl/node/list/main.go index 074c0501..0a392862 100644 --- a/internal/app/wwctl/node/list/main.go +++ b/internal/app/wwctl/node/list/main.go @@ -8,6 +8,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" ) @@ -26,6 +27,8 @@ func CobraRunE(cmd *cobra.Command, args []string) error { os.Exit(1) } + args = hostlist.Expand(args) + if ShowAll { for _, node := range node.FilterByName(nodes, args) { fmt.Printf("################################################################################\n") @@ -81,11 +84,11 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } } else if ShowIpmi { - fmt.Printf("%-22s %-16s %-5s %-20s %-20s %-20s\n", "NODE NAME", "IPMI IPADDR", "IPMI PORT", "IPMI USERNAME", "IPMI PASSWORD", "IPMI INTERFACE") - fmt.Println(strings.Repeat("=", 80)) + fmt.Printf("%-22s %-16s %-10s %-20s %-20s %-14s\n", "NODE NAME", "IPMI IPADDR", "IPMI PORT", "IPMI USERNAME", "IPMI PASSWORD", "IPMI INTERFACE") + fmt.Println(strings.Repeat("=", 108)) for _, node := range node.FilterByName(nodes, args) { - fmt.Printf("%-22s %-16s %-5s %-20s %-20s %-20s\n", node.Id.Get(), node.IpmiIpaddr.Print(), node.IpmiPort.Print(), node.IpmiUserName.Print(), node.IpmiPassword.Print(), node.IpmiInterface.Print()) + fmt.Printf("%-22s %-16s %-10s %-20s %-20s %-14s\n", node.Id.Get(), node.IpmiIpaddr.Print(), node.IpmiPort.Print(), node.IpmiUserName.Print(), node.IpmiPassword.Print(), node.IpmiInterface.Print()) } } else if ShowLong { diff --git a/internal/app/wwctl/node/list/root.go b/internal/app/wwctl/node/list/root.go index b72903ed..b354c881 100644 --- a/internal/app/wwctl/node/list/root.go +++ b/internal/app/wwctl/node/list/root.go @@ -1,14 +1,32 @@ package list -import "github.com/spf13/cobra" +import ( + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/spf13/cobra" +) var ( baseCmd = &cobra.Command{ - Use: "list [flags] (node pattern)", - Short: "List nodes matching pattern", - Long: "This command will show you configured nodes.", + DisableFlagsInUseLine: true, + Use: "list [OPTIONS] [PATTERN]", + Short: "List nodes", + Long: "This command lists all configured nodes. Optionally, it will list only\n" + + "nodes matching a glob PATTERN.", RunE: CobraRunE, Aliases: []string{"ls"}, + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if len(args) != 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + + nodeDB, _ := node.New() + nodes, _ := nodeDB.FindAllNodes() + var node_names []string + for _, node := range nodes { + node_names = append(node_names, node.Id.Get()) + } + return node_names, cobra.ShellCompDirectiveNoFileComp + }, } ShowNet bool ShowIpmi bool diff --git a/internal/app/wwctl/node/ready/root.go b/internal/app/wwctl/node/ready/root.go index abcba8b8..fb129a22 100644 --- a/internal/app/wwctl/node/ready/root.go +++ b/internal/app/wwctl/node/ready/root.go @@ -7,8 +7,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "ready", - Short: "Warewulf Status Check", + DisableFlagsInUseLine: true, + Use: "ready [OPTIONS]", + Short: "Warewulf status check", + Long: "Provides the current status of all Warewulf nodes.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/node/root.go b/internal/app/wwctl/node/root.go index c5c92ca4..a8b10061 100644 --- a/internal/app/wwctl/node/root.go +++ b/internal/app/wwctl/node/root.go @@ -13,9 +13,12 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "node", + DisableFlagsInUseLine: true, + Use: "node COMMAND [OPTONS]", Short: "Node management", - Long: "Management of node settings", + Long: "Management of node settings. All node ranges can use brackets to identify\n" + + "node ranges. For example: n00[00-4].cluster[0-1] will identify the first 5 nodes\n" + + "in cluster0 and cluster1.", } ) diff --git a/internal/app/wwctl/node/sensors/power.go b/internal/app/wwctl/node/sensors/power.go index dc86dc0c..a2c0766e 100644 --- a/internal/app/wwctl/node/sensors/power.go +++ b/internal/app/wwctl/node/sensors/power.go @@ -8,6 +8,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/power" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" ) @@ -26,6 +27,8 @@ func CobraRunE(cmd *cobra.Command, args []string) error { os.Exit(1) } + args = hostlist.Expand(args) + if len(args) > 0 { nodes = node.FilterByName(nodes, args) } else { diff --git a/internal/app/wwctl/node/sensors/root.go b/internal/app/wwctl/node/sensors/root.go index ea95dd4f..07405812 100644 --- a/internal/app/wwctl/node/sensors/root.go +++ b/internal/app/wwctl/node/sensors/root.go @@ -7,9 +7,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "sensors [flags] [node pattern]", - Short: "Show node's IPMI sensor information", - Long: "Show IPMI sensors for a single node.", + DisableFlagsInUseLine: true, + Use: "sensors [OPTIONS] PATTERN", + Short: "Show node IPMI sensor information", + Long: "Show IPMI sensor information for nodes matching PATTERN.", Args: cobra.MinimumNArgs(1), RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/internal/app/wwctl/node/set/main.go b/internal/app/wwctl/node/set/main.go index 1e87ce3d..725266aa 100644 --- a/internal/app/wwctl/node/set/main.go +++ b/internal/app/wwctl/node/set/main.go @@ -9,6 +9,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/warewulfd" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/manifoldco/promptui" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -16,6 +17,7 @@ import ( func CobraRunE(cmd *cobra.Command, args []string) error { var err error + var count uint var SetProfiles []string nodeDB, err := node.New() @@ -32,7 +34,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { if !SetNodeAll { if len(args) > 0 { - nodes = node.FilterByName(nodes, args) + nodes = node.FilterByName(nodes, hostlist.Expand(args)) } else { //nolint:errcheck cmd.Usage() @@ -99,8 +101,9 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if SetIpmiIpaddr != "" { - wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI IP address to: %s\n", n.Id.Get(), SetIpmiIpaddr) - n.IpmiIpaddr.Set(SetIpmiIpaddr) + NewIpaddr := util.IncrementIPv4(SetIpmiIpaddr, count) + wwlog.Printf(wwlog.VERBOSE, "Node: %s, Setting IPMI IP address to: %s\n", n.Id.Get(), NewIpaddr) + n.IpmiIpaddr.Set(NewIpaddr) } if SetIpmiNetmask != "" { @@ -182,13 +185,15 @@ func CobraRunE(cmd *cobra.Command, args []string) error { os.Exit(1) } + NewIpaddr := util.IncrementIPv4(SetIpaddr, count) + if _, ok := n.NetDevs[SetNetDev]; !ok { var nd node.NetDevEntry n.NetDevs[SetNetDev] = &nd } - wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Ipaddr to: %s\n", n.Id.Get(), SetNetDev, SetIpaddr) - n.NetDevs[SetNetDev].Ipaddr.Set(SetIpaddr) + wwlog.Printf(wwlog.VERBOSE, "Node: %s:%s, Setting Ipaddr to: %s\n", n.Id.Get(), SetNetDev, NewIpaddr) + n.NetDevs[SetNetDev].Ipaddr.Set(NewIpaddr) } if SetNetmask != "" { if SetNetDev == "" { @@ -302,6 +307,8 @@ func CobraRunE(cmd *cobra.Command, args []string) error { wwlog.Printf(wwlog.ERROR, "%s\n", err) os.Exit(1) } + + count++ } if SetYes { diff --git a/internal/app/wwctl/node/set/root.go b/internal/app/wwctl/node/set/root.go index 86ea559e..19fca758 100644 --- a/internal/app/wwctl/node/set/root.go +++ b/internal/app/wwctl/node/set/root.go @@ -12,9 +12,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "set [flags] [node pattern]...", + DisableFlagsInUseLine: true, + Use: "set [OPTIONS] PATTERN [PATTERN ...]", Short: "Configure node properties", - Long: "This command will allow you to set configuration properties for nodes.\n\n" + + Long: "This command sets configuration properties for nodes matching PATTERN.\n\n" + "Note: use the string 'UNSET' to remove a configuration", Args: cobra.MinimumNArgs(1), RunE: CobraRunE, @@ -105,13 +106,13 @@ func init() { }); err != nil { log.Println(err) } - baseCmd.PersistentFlags().StringVar(&SetIpmiIpaddr, "ipmi", "", "Set the node's IPMI IP address") + baseCmd.PersistentFlags().StringVar(&SetIpmiIpaddr, "ipmiaddr", "", "Set the node's IPMI IP address") baseCmd.PersistentFlags().StringVar(&SetIpmiNetmask, "ipminetmask", "", "Set the node's IPMI netmask") baseCmd.PersistentFlags().StringVar(&SetIpmiPort, "ipmiport", "", "Set the node's IPMI port") baseCmd.PersistentFlags().StringVar(&SetIpmiGateway, "ipmigateway", "", "Set the node's IPMI gateway") baseCmd.PersistentFlags().StringVar(&SetIpmiUsername, "ipmiuser", "", "Set the node's IPMI username") baseCmd.PersistentFlags().StringVar(&SetIpmiPassword, "ipmipass", "", "Set the node's IPMI password") - baseCmd.PersistentFlags().StringVar(&SetIpmiInterface, "ipmiinterface", "", "Set the node's IPMI interface (defaults to 'lan' if empty)") + baseCmd.PersistentFlags().StringVar(&SetIpmiInterface, "ipmiinterface", "", "Set the node's IPMI interface (defaults: 'lan')") baseCmd.PersistentFlags().StringSliceVar(&SetAddProfile, "addprofile", []string{}, "Add Profile(s) to node") baseCmd.PersistentFlags().StringSliceVar(&SetDelProfile, "delprofile", []string{}, "Remove Profile(s) to node") baseCmd.PersistentFlags().StringVarP(&SetProfile, "profile", "P", "", "Set the node's profile members (comma separated)") diff --git a/internal/app/wwctl/overlay/build/root.go b/internal/app/wwctl/overlay/build/root.go index a6901f87..5b5bb45d 100644 --- a/internal/app/wwctl/overlay/build/root.go +++ b/internal/app/wwctl/overlay/build/root.go @@ -4,9 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "build [flags] ", + DisableFlagsInUseLine: true, + Use: "build [OPTIONS] {system|runtime} OVERLAY_NAME", Short: "(Re)build an overlay", - Long: "This command will build a system or runtime overlay.", + Long: "This command builds a new system or runtime overlay named OVERLAY_NAME.", RunE: CobraRunE, Args: cobra.RangeArgs(0, 2), } diff --git a/internal/app/wwctl/overlay/chmod/root.go b/internal/app/wwctl/overlay/chmod/root.go index b9a254d6..7790c5df 100644 --- a/internal/app/wwctl/overlay/chmod/root.go +++ b/internal/app/wwctl/overlay/chmod/root.go @@ -1,15 +1,21 @@ package chmod -import "github.com/spf13/cobra" +import ( + "github.com/spf13/cobra" +) var ( baseCmd = &cobra.Command{ - Use: "chmod [flags] ", - Short: "Change file permissions within an overlay", - Long: "This command will allow you to change the permissions of a file within an\n" + - "overlay.", - RunE: CobraRunE, - Args: cobra.ExactArgs(4), + DisableFlagsInUseLine: true, + Use: "chmod [OPTIONS] {runtime|system} OVERLAY_NAME FILENAME MODE", + Short: "Change file permissions in an overlay", + Long: `Changes the permissions of a single FILENAME within an overlay specified by +overlay type (system or runtime) and its OVERLAY_NAME. + +You can use any MODE format supported by the chmod command.`, + Example: "wwctl overlay chmod system default /etc/hostname.ww 0660", + RunE: CobraRunE, + Args: cobra.ExactArgs(4), } ) diff --git a/internal/app/wwctl/overlay/chown/root.go b/internal/app/wwctl/overlay/chown/root.go index 941328f0..8d45823e 100644 --- a/internal/app/wwctl/overlay/chown/root.go +++ b/internal/app/wwctl/overlay/chown/root.go @@ -4,10 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "chown [flags] []", + Use: "chown [OPTIONS] {system|runtime} OVERLAY_NAME FILE UID [GID]", Short: "Change file ownership within an overlay", - Long: "This command will allow you to change the ownership of a file within an\n" + - "overlay.", + Long: "This command changes the ownership of a FILE within the system or runtime OVERLAY_NAME\n" + + "to the user specified by UID. Optionally, it will also change group ownership to GID.", RunE: CobraRunE, Args: cobra.RangeArgs(4, 5), } diff --git a/internal/app/wwctl/overlay/create/root.go b/internal/app/wwctl/overlay/create/root.go index 15242009..b09b249e 100644 --- a/internal/app/wwctl/overlay/create/root.go +++ b/internal/app/wwctl/overlay/create/root.go @@ -6,9 +6,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "create [flags] ", + DisableFlagsInUseLine: true, + Use: "create [OPTIONS] {system|runtime} OVERLAY_NAME", Short: "Initialize a new Overlay", - Long: "This command will create a new empty overlay.", + Long: "This command creates a new empty system or runtime overlay named OVERLAY_NAME.", RunE: CobraRunE, Args: cobra.ExactArgs(2), } diff --git a/internal/app/wwctl/overlay/delete/main.go b/internal/app/wwctl/overlay/delete/main.go index 7a3a17a5..69d20226 100644 --- a/internal/app/wwctl/overlay/delete/main.go +++ b/internal/app/wwctl/overlay/delete/main.go @@ -24,10 +24,6 @@ func CobraRunE(cmd *cobra.Command, args []string) error { fileName = args[2] } - if overlayName == "default" { - return errors.New("refusing to delete the default overlay") - } - if overlayKind != "system" && overlayKind != "runtime" { return errors.New("overlay kind must be of type 'system' or 'runtime'") } @@ -49,6 +45,9 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if fileName == "" { + if overlayName == "default" { + return errors.New("refusing to delete the default overlay") + } if Force { err := os.RemoveAll(overlayPath) if err != nil { diff --git a/internal/app/wwctl/overlay/delete/root.go b/internal/app/wwctl/overlay/delete/root.go index bfe9299c..875909b3 100644 --- a/internal/app/wwctl/overlay/delete/root.go +++ b/internal/app/wwctl/overlay/delete/root.go @@ -6,10 +6,11 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "delete [flags] [overlay file]", + DisableFlagsInUseLine: true, + Use: "delete [OPTIONS] {runtime|system} OVERLAY_NAME [FILE [FILE ...]]", Short: "Delete Warewulf Overlay or files", - Long: "This command will delete files within an overlay or an entire overlay if no\n" + - "files are given to remove (use with caution).", + Long: "This command will delete FILEs within OVERLAY_NAME or the entire OVERLAY_NAME if no\n" + + "files are listed. Use with caution!", RunE: CobraRunE, Args: cobra.RangeArgs(2, 3), Aliases: []string{"rm", "del"}, diff --git a/internal/app/wwctl/overlay/edit/root.go b/internal/app/wwctl/overlay/edit/root.go index 3ec6add2..7f981614 100644 --- a/internal/app/wwctl/overlay/edit/root.go +++ b/internal/app/wwctl/overlay/edit/root.go @@ -6,11 +6,12 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "edit [flags] ", - Short: "Edit/Create a file within a Warewulf Overlay", - Long: "This command will allow you to edit or create a new file within a given\n" + - "overlay. Note: when creating files ending in a '.ww' suffix this will always be\n" + - "parsed as a Warewulf template file, and the suffix will be removed automatically.", + DisableFlagsInUseLine: true, + Use: "edit [OPTIONS] {system|runtime} OVERLAY_NAME FILE", + Short: "Edit or create a file within a Warewulf Overlay", + Long: "This command will open the FILE for editing or create a new file within the\n" + + "OVERLAY_NAME. Note: files created with a '.ww' suffix will always be\n" + + "parsed as Warewulf template files, and the suffix will be removed automatically.", RunE: CobraRunE, Args: cobra.ExactArgs(3), } diff --git a/internal/app/wwctl/overlay/imprt/root.go b/internal/app/wwctl/overlay/imprt/root.go index 983052b8..0e4c2146 100644 --- a/internal/app/wwctl/overlay/imprt/root.go +++ b/internal/app/wwctl/overlay/imprt/root.go @@ -4,9 +4,11 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "import [flags] [overlay target]", + DisableFlagsInUseLine: true, + Use: "import [OPTIONS] {system|runtime} OVERLAY_NAME FILE [NEW_NAME]", Short: "Import a file into a Warewulf Overlay", - Long: "This command will import a file into a given Warewulf overlay.", + Long: "This command imports the FILE into the Warewulf OVERLAY_NAME.\n" + + "Optionally, the file can be renamed to NEW_NAME", RunE: CobraRunE, Args: cobra.RangeArgs(3, 4), Aliases: []string{"cp"}, diff --git a/internal/app/wwctl/overlay/list/root.go b/internal/app/wwctl/overlay/list/root.go index 9643845b..c7b33ce4 100644 --- a/internal/app/wwctl/overlay/list/root.go +++ b/internal/app/wwctl/overlay/list/root.go @@ -6,10 +6,11 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "list [flags] [overlay name]", + DisableFlagsInUseLine: true, + Use: "list [OPTIONS] {system|runtime} [OVERLAY_NAME]", Short: "List Warewulf Overlays and files", - Long: "This command will show you information about Warewulf overlays and the\n" + - "files contained within.", + Long: "This command displays information about all Warewulf overlays or the specified\n" + + "OVERLAY_NAME. It also supports listing overlay content information.", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), Aliases: []string{"ls"}, diff --git a/internal/app/wwctl/overlay/mkdir/root.go b/internal/app/wwctl/overlay/mkdir/root.go index 8a557079..e95cba0a 100644 --- a/internal/app/wwctl/overlay/mkdir/root.go +++ b/internal/app/wwctl/overlay/mkdir/root.go @@ -6,9 +6,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "mkdir [flags] ", + DisableFlagsInUseLine: true, + Use: "mkdir [OPTIONS] {system|runtime} OVERLAY_NAME DIRECTORY", Short: "Create a new directory within an Overlay", - Long: "This command will allow you to create a new file within a given Warewulf overlay.", + Long: "This command creates a new directory within the Warewulf OVERLAY_NAME.", RunE: CobraRunE, Args: cobra.MinimumNArgs(3), } diff --git a/internal/app/wwctl/overlay/root.go b/internal/app/wwctl/overlay/root.go index 616a99c0..4bb1cd95 100644 --- a/internal/app/wwctl/overlay/root.go +++ b/internal/app/wwctl/overlay/root.go @@ -16,7 +16,8 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "overlay", + DisableFlagsInUseLine: true, + Use: "overlay COMMAND [OPTIONS]", Short: "Warewulf Overlay Management", Long: "Management interface for Warewulf overlays", } diff --git a/internal/app/wwctl/overlay/show/root.go b/internal/app/wwctl/overlay/show/root.go index a8c51efc..43c491ce 100644 --- a/internal/app/wwctl/overlay/show/root.go +++ b/internal/app/wwctl/overlay/show/root.go @@ -6,10 +6,9 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "show [flags] ", + Use: "show [OPTIONS] {system|runtime} OVERLAY_NAME FILE", Short: "Show (cat) a file within a Warewulf Overlay", - Long: "This command will output the contents of a file within a given\n" + - "Warewulf overlay.", + Long: "This command displays the contents of FILE within OVERLAY_NAME.", RunE: CobraRunE, Aliases: []string{"cat"}, Args: cobra.ExactArgs(3), diff --git a/internal/app/wwctl/power/cycle/power.go b/internal/app/wwctl/power/cycle/power.go index a846ad7e..728548f8 100644 --- a/internal/app/wwctl/power/cycle/power.go +++ b/internal/app/wwctl/power/cycle/power.go @@ -8,6 +8,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/power" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" ) @@ -27,7 +28,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if len(args) > 0 { - nodes = node.FilterByName(nodes, args) + nodes = node.FilterByName(nodes, hostlist.Expand(args)) } else { //nolint:errcheck cmd.Usage() diff --git a/internal/app/wwctl/power/cycle/root.go b/internal/app/wwctl/power/cycle/root.go index e89899fc..18f0fc51 100644 --- a/internal/app/wwctl/power/cycle/root.go +++ b/internal/app/wwctl/power/cycle/root.go @@ -6,9 +6,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "cycle [flags] ...", + DisableFlagsInUseLine: true, + Use: "cycle [OPTIONS] [PATTERN ...]", Short: "Power cycle the given node(s)", - Long: "This command will cycle the power for a given set of nodes.", + Long: "This command cycles power for a set of nodes specified by PATTERN.", RunE: CobraRunE, } ) diff --git a/internal/app/wwctl/power/off/power.go b/internal/app/wwctl/power/off/power.go index 7f9dc603..2afe8276 100644 --- a/internal/app/wwctl/power/off/power.go +++ b/internal/app/wwctl/power/off/power.go @@ -8,6 +8,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/power" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" ) @@ -27,7 +28,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if len(args) > 0 { - nodes = node.FilterByName(nodes, args) + nodes = node.FilterByName(nodes, hostlist.Expand(args)) } else { //nolint:errcheck cmd.Usage() diff --git a/internal/app/wwctl/power/off/root.go b/internal/app/wwctl/power/off/root.go index 0b98bed1..cb659dda 100644 --- a/internal/app/wwctl/power/off/root.go +++ b/internal/app/wwctl/power/off/root.go @@ -7,9 +7,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "off", + DisableFlagsInUseLine: true, + Use: "off [OPTIONS] [PATTERN ...]", Short: "Power off the given node(s)", - Long: "This command will shutdown the power to a given set of nodes.", + Long: "This command will shutdown power to a set of nodes specified by PATTERN.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/power/on/power.go b/internal/app/wwctl/power/on/power.go index 7f5624a8..a92bf50b 100644 --- a/internal/app/wwctl/power/on/power.go +++ b/internal/app/wwctl/power/on/power.go @@ -8,6 +8,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/power" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" ) @@ -27,7 +28,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if len(args) > 0 { - nodes = node.FilterByName(nodes, args) + nodes = node.FilterByName(nodes, hostlist.Expand(args)) } else { //nolint:errcheck cmd.Usage() diff --git a/internal/app/wwctl/power/on/root.go b/internal/app/wwctl/power/on/root.go index 7918299a..8e195e18 100644 --- a/internal/app/wwctl/power/on/root.go +++ b/internal/app/wwctl/power/on/root.go @@ -7,9 +7,9 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "on", + Use: "on [OPTIONS] [PATTERN ...]", Short: "Power on the given node(s)", - Long: "This command will power on a given set of nodes.", + Long: "This command will power on a set of nodes specified by PATTERN.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/power/reset/power.go b/internal/app/wwctl/power/reset/power.go index 3996648e..3126a301 100644 --- a/internal/app/wwctl/power/reset/power.go +++ b/internal/app/wwctl/power/reset/power.go @@ -8,6 +8,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/power" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" ) @@ -27,7 +28,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if len(args) > 0 { - nodes = node.FilterByName(nodes, args) + nodes = node.FilterByName(nodes, hostlist.Expand(args)) } else { //nolint:errcheck cmd.Usage() diff --git a/internal/app/wwctl/power/reset/root.go b/internal/app/wwctl/power/reset/root.go index 6622de8e..02d79c3a 100644 --- a/internal/app/wwctl/power/reset/root.go +++ b/internal/app/wwctl/power/reset/root.go @@ -7,9 +7,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "reset", - Short: "Issue a reset to the given node(s)", - Long: "This command will issue a reset to the given set of nodes.", + DisableFlagsInUseLine: true, + Use: "reset [OPTIONS] [PATTERN ...]", + Short: "Issue a reset to node(s)", + Long: "This command will issue a reset to a set of nodes specified by PATTERN.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/power/root.go b/internal/app/wwctl/power/root.go index e2222e29..135139b2 100644 --- a/internal/app/wwctl/power/root.go +++ b/internal/app/wwctl/power/root.go @@ -12,9 +12,10 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "power", + DisableFlagsInUseLine: true, + Use: "power COMMAND [OPTIONS]", Short: "Warewulf node power management", - Long: "This command can control the power state of nodes.", + Long: "This command controls the power state of nodes.", } ) diff --git a/internal/app/wwctl/power/soft/power.go b/internal/app/wwctl/power/soft/power.go index cdbf8462..7771cbab 100644 --- a/internal/app/wwctl/power/soft/power.go +++ b/internal/app/wwctl/power/soft/power.go @@ -8,6 +8,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/power" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" ) @@ -27,7 +28,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if len(args) > 0 { - nodes = node.FilterByName(nodes, args) + nodes = node.FilterByName(nodes, hostlist.Expand(args)) } else { //nolint:errcheck cmd.Usage() diff --git a/internal/app/wwctl/power/soft/root.go b/internal/app/wwctl/power/soft/root.go index af13114c..77100254 100644 --- a/internal/app/wwctl/power/soft/root.go +++ b/internal/app/wwctl/power/soft/root.go @@ -7,9 +7,10 @@ import ( var ( powerCmd = &cobra.Command{ + DisableFlagsInUseLine: true, Use: "soft", Short: "Gracefully shuts down the given node(s)", - Long: "This command will gracefully shutdown the given set of nodes.", + Long: "This command uses the operationg system to shut down the set of nodes specified by PATTERN.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/power/status/power.go b/internal/app/wwctl/power/status/power.go index b1f15dd6..9d67cd17 100644 --- a/internal/app/wwctl/power/status/power.go +++ b/internal/app/wwctl/power/status/power.go @@ -8,6 +8,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/node" "github.com/hpcng/warewulf/internal/pkg/power" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/pkg/hostlist" "github.com/spf13/cobra" ) @@ -27,7 +28,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if len(args) > 0 { - nodes = node.FilterByName(nodes, args) + nodes = node.FilterByName(nodes, hostlist.Expand(args)) } else { //nolint:errcheck cmd.Usage() diff --git a/internal/app/wwctl/power/status/root.go b/internal/app/wwctl/power/status/root.go index 454165e5..40c6fc64 100644 --- a/internal/app/wwctl/power/status/root.go +++ b/internal/app/wwctl/power/status/root.go @@ -7,9 +7,10 @@ import ( var ( powerCmd = &cobra.Command{ - Use: "status", + DisableFlagsInUseLine: true, + Use: "status [OPTIONS] [PATTERN ...]", Short: "Show power status for the given node(s)", - Long: "This command will show the power status of a given set of nodes.", + Long: "This command displays the power status of a set of nodes specified by PATTERN.", RunE: CobraRunE, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { diff --git a/internal/app/wwctl/profile/add/root.go b/internal/app/wwctl/profile/add/root.go index 85d1ac42..9e95d8d2 100644 --- a/internal/app/wwctl/profile/add/root.go +++ b/internal/app/wwctl/profile/add/root.go @@ -4,9 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "add ", + DisableFlagsInUseLine: true, + Use: "add PROFILE", Short: "Add a new node profile", - Long: "This command will add a new node profile.", + Long: "This command adds a new named PROFILE.", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), } diff --git a/internal/app/wwctl/profile/delete/root.go b/internal/app/wwctl/profile/delete/root.go index 44f3afe0..a5cea9e1 100644 --- a/internal/app/wwctl/profile/delete/root.go +++ b/internal/app/wwctl/profile/delete/root.go @@ -4,9 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "delete [flags] ", + DisableFlagsInUseLine: true, + Use: "delete [OPTIONS] PROFILE", Short: "Delete a node profile", - Long: "This command will delete a node profile.", + Long: "This command deletes the node PROFILE. You may use a pattern for PROFILE.", RunE: CobraRunE, Args: cobra.MinimumNArgs(1), } diff --git a/internal/app/wwctl/profile/list/main.go b/internal/app/wwctl/profile/list/main.go index f43a2046..a549abd7 100644 --- a/internal/app/wwctl/profile/list/main.go +++ b/internal/app/wwctl/profile/list/main.go @@ -41,7 +41,6 @@ func CobraRunE(cmd *cobra.Command, args []string) error { fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "RuntimeOverlay", profile.RuntimeOverlay.Print()) fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "SystemOverlay", profile.SystemOverlay.Print()) fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "Ipxe", profile.Ipxe.Print()) - fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "IpmiIpaddr", profile.IpmiIpaddr.Print()) fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "IpmiNetmask", profile.IpmiNetmask.Print()) fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "IpmiPort", profile.IpmiPort.Print()) fmt.Printf("%-20s %-18s %s\n", profile.Id.Get(), "IpmiGateway", profile.IpmiGateway.Print()) diff --git a/internal/app/wwctl/profile/list/root.go b/internal/app/wwctl/profile/list/root.go index 6c795ebb..1144d5c2 100644 --- a/internal/app/wwctl/profile/list/root.go +++ b/internal/app/wwctl/profile/list/root.go @@ -4,9 +4,10 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "list [flags] [profile pattern]...", + DisableFlagsInUseLine: true, + Use: "list [OPTIONS] [PROFILE ...]", Short: "List profiles and configurations", - Long: "This command will list and show the profile configurations.", + Long: "This command will display configurations for PROFILE.", RunE: CobraRunE, Aliases: []string{"ls"}, } diff --git a/internal/app/wwctl/profile/root.go b/internal/app/wwctl/profile/root.go index faa89044..2945d04e 100644 --- a/internal/app/wwctl/profile/root.go +++ b/internal/app/wwctl/profile/root.go @@ -10,7 +10,8 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "profile", + DisableFlagsInUseLine: true, + Use: "profile COMMAND [OPTIONS]", Short: "Node configuration profile management", Long: "Management of node profile settings", } diff --git a/internal/app/wwctl/profile/set/main.go b/internal/app/wwctl/profile/set/main.go index 667111f7..74d128bd 100644 --- a/internal/app/wwctl/profile/set/main.go +++ b/internal/app/wwctl/profile/set/main.go @@ -96,12 +96,17 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if SetIpmiNetmask != "" { - wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI username to: %s\n", p.Id.Get(), SetIpmiNetmask) + wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI netmask to: %s\n", p.Id.Get(), SetIpmiNetmask) p.IpmiNetmask.Set(SetIpmiNetmask) } + if SetIpmiPort != "" { + wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI port to: %s\n", p.Id.Get(), SetIpmiPort) + p.IpmiPort.Set(SetIpmiPort) + } + if SetIpmiGateway != "" { - wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI username to: %s\n", p.Id.Get(), SetIpmiGateway) + wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI gateway to: %s\n", p.Id.Get(), SetIpmiGateway) p.IpmiGateway.Set(SetIpmiGateway) } @@ -111,12 +116,12 @@ func CobraRunE(cmd *cobra.Command, args []string) error { } if SetIpmiPassword != "" { - wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI username to: %s\n", p.Id.Get(), SetIpmiPassword) + wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI password to: %s\n", p.Id.Get(), SetIpmiPassword) p.IpmiPassword.Set(SetIpmiPassword) } if SetIpmiInterface != "" { - wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI username to: %s\n", p.Id.Get(), SetIpmiInterface) + wwlog.Printf(wwlog.VERBOSE, "Profile: %s, Setting IPMI interface to: %s\n", p.Id.Get(), SetIpmiInterface) p.IpmiInterface.Set(SetIpmiInterface) } diff --git a/internal/app/wwctl/profile/set/root.go b/internal/app/wwctl/profile/set/root.go index cb0b765a..392c1476 100644 --- a/internal/app/wwctl/profile/set/root.go +++ b/internal/app/wwctl/profile/set/root.go @@ -12,9 +12,9 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "set [flags] ...", + Use: "set [OPTIONS] [PROFILE ...]", Short: "Configure node profile properties", - Long: "This command will allow you to set configuration properties for node profiles.\n\n" + + Long: "This command sets configuration properties for the node PROFILE(s).\n\n" + "Note: use the string 'UNSET' to remove a configuration", Args: cobra.MinimumNArgs(1), RunE: CobraRunE, @@ -44,6 +44,7 @@ var ( SetRuntimeOverlay string SetSystemOverlay string SetIpmiNetmask string + SetIpmiPort string SetIpmiGateway string SetIpmiUsername string SetIpmiPassword string @@ -103,10 +104,11 @@ func init() { log.Println(err) } baseCmd.PersistentFlags().StringVar(&SetIpmiNetmask, "ipminetmask", "", "Set the node's IPMI netmask") + baseCmd.PersistentFlags().StringVar(&SetIpmiPort, "ipmiport", "", "Set the node's IPMI port") baseCmd.PersistentFlags().StringVar(&SetIpmiGateway, "ipmigateway", "", "Set the node's IPMI gateway") baseCmd.PersistentFlags().StringVar(&SetIpmiUsername, "ipmiuser", "", "Set the node's IPMI username") baseCmd.PersistentFlags().StringVar(&SetIpmiPassword, "ipmipass", "", "Set the node's IPMI password") - baseCmd.PersistentFlags().StringVar(&SetIpmiInterface, "ipmiinterface", "", "Set the node's IPMI interface (defaults to 'lan' if empty)") + baseCmd.PersistentFlags().StringVar(&SetIpmiInterface, "ipmiinterface", "", "Set the node's IPMI interface (defaults to 'lan')") baseCmd.PersistentFlags().StringVarP(&SetNetDev, "netdev", "N", "", "Define the network device to configure") baseCmd.PersistentFlags().StringVarP(&SetIpaddr, "ipaddr", "I", "", "Set the node's network device IP address") diff --git a/internal/app/wwctl/root.go b/internal/app/wwctl/root.go index 840fbb6e..059b38db 100644 --- a/internal/app/wwctl/root.go +++ b/internal/app/wwctl/root.go @@ -9,7 +9,9 @@ import ( "github.com/hpcng/warewulf/internal/app/wwctl/power" "github.com/hpcng/warewulf/internal/app/wwctl/profile" "github.com/hpcng/warewulf/internal/app/wwctl/server" + "github.com/hpcng/warewulf/internal/app/wwctl/version" "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/hpcng/warewulf/internal/pkg/help" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" @@ -18,9 +20,10 @@ import ( var ( rootCmd = &cobra.Command{ - Use: "wwctl", + DisableFlagsInUseLine: true, + Use: "wwctl COMMAND [OPTIONS]", Short: "Warewulf Control", - Long: "Control interface to the Cluster Warewulf Provisioning System.", + Long: "Control interface to the Warewulf Cluster Provisioning System.", PersistentPreRunE: rootPersistentPreRunE, SilenceUsage: true, SilenceErrors: true, @@ -33,6 +36,9 @@ func init() { rootCmd.PersistentFlags().BoolVarP(&verboseArg, "verbose", "v", false, "Run with increased verbosity.") rootCmd.PersistentFlags().BoolVarP(&DebugFlag, "debug", "d", false, "Run with debugging messages enabled.") + rootCmd.SetUsageTemplate(help.UsageTemplate) + rootCmd.SetHelpTemplate(help.HelpTemplate) + rootCmd.AddCommand(overlay.GetCommand()) rootCmd.AddCommand(container.GetCommand()) rootCmd.AddCommand(node.GetCommand()) @@ -41,6 +47,7 @@ func init() { rootCmd.AddCommand(profile.GetCommand()) rootCmd.AddCommand(configure.GetCommand()) rootCmd.AddCommand(server.GetCommand()) + rootCmd.AddCommand(version.GetCommand()) } @@ -60,14 +67,17 @@ func rootPersistentPreRunE(cmd *cobra.Command, args []string) error { return nil } -// GenBashCompletionFile +// External functions not used by the wwctl command line + +// Generate Bash completion file func GenBashCompletion(w io.Writer) error { return rootCmd.GenBashCompletion(w) } +// Generate man pages func GenManTree(fileName string) error { header := &doc.GenManHeader{ - Title: "MINE", + Title: "WWCTL", Section: "1", } return doc.GenManTree(rootCmd, header, fileName) diff --git a/internal/app/wwctl/server/reload/root.go b/internal/app/wwctl/server/reload/root.go index b503b746..9a880b7a 100644 --- a/internal/app/wwctl/server/reload/root.go +++ b/internal/app/wwctl/server/reload/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "reload", + DisableFlagsInUseLine: true, + Use: "reload [OPTIONS]", Short: "Reload the Warewulf server configuration", RunE: CobraRunE, } diff --git a/internal/app/wwctl/server/restart/root.go b/internal/app/wwctl/server/restart/root.go index c1538dcd..84224ed6 100644 --- a/internal/app/wwctl/server/restart/root.go +++ b/internal/app/wwctl/server/restart/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "restart", + DisableFlagsInUseLine: true, + Use: "restart [OPTIONS]", Short: "Restart the Warewulf server", RunE: CobraRunE, } diff --git a/internal/app/wwctl/server/root.go b/internal/app/wwctl/server/root.go index de865f63..22b1bce3 100644 --- a/internal/app/wwctl/server/root.go +++ b/internal/app/wwctl/server/root.go @@ -11,7 +11,8 @@ import ( var ( baseCmd = &cobra.Command{ - Use: "server", + DisableFlagsInUseLine: true, + Use: "server COMMAND [OPTIONS]", Short: "Warewulf server process commands", Long: "This command will allow you to control the Warewulf daemon process.", } diff --git a/internal/app/wwctl/server/start/root.go b/internal/app/wwctl/server/start/root.go index 2d4ca442..be4e0621 100644 --- a/internal/app/wwctl/server/start/root.go +++ b/internal/app/wwctl/server/start/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "start", + DisableFlagsInUseLine: true, + Use: "start [OPTIONS]", Short: "Start Warewulf server", RunE: CobraRunE, } diff --git a/internal/app/wwctl/server/status/root.go b/internal/app/wwctl/server/status/root.go index d7e6270a..83a07b8c 100644 --- a/internal/app/wwctl/server/status/root.go +++ b/internal/app/wwctl/server/status/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "status", + DisableFlagsInUseLine: true, + Use: "status [OPTIONS]", Short: "Warewulf server status", RunE: CobraRunE, } diff --git a/internal/app/wwctl/server/stop/root.go b/internal/app/wwctl/server/stop/root.go index 23549d95..94df3acc 100644 --- a/internal/app/wwctl/server/stop/root.go +++ b/internal/app/wwctl/server/stop/root.go @@ -4,7 +4,8 @@ import "github.com/spf13/cobra" var ( baseCmd = &cobra.Command{ - Use: "stop", + DisableFlagsInUseLine: true, + Use: "stop [OPTIONS]", Short: "Stop Warewulf server", RunE: CobraRunE, } diff --git a/internal/app/wwctl/version/main.go b/internal/app/wwctl/version/main.go new file mode 100644 index 00000000..08e48d09 --- /dev/null +++ b/internal/app/wwctl/version/main.go @@ -0,0 +1,15 @@ +package version + +import ( + "fmt" + + "github.com/hpcng/warewulf/internal/pkg/version" + "github.com/spf13/cobra" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + + fmt.Println("Version:\t", version.GetVersion()) + + return nil +} diff --git a/internal/app/wwctl/version/root.go b/internal/app/wwctl/version/root.go new file mode 100644 index 00000000..766d3b8f --- /dev/null +++ b/internal/app/wwctl/version/root.go @@ -0,0 +1,22 @@ +package version + +import "github.com/spf13/cobra" + +var ( + baseCmd = &cobra.Command{ + Use: "version", + Short: "Version information", + Long: "This command will print the Warewulf version.", + RunE: CobraRunE, + Args: cobra.ExactArgs(0), + Aliases: []string{"vers"}, + } +) + +func init() { +} + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return baseCmd +} diff --git a/internal/pkg/help/help.go b/internal/pkg/help/help.go new file mode 100644 index 00000000..93726f76 --- /dev/null +++ b/internal/pkg/help/help.go @@ -0,0 +1,35 @@ +package help + +// UsageTemplate replaces the default usage template from cobra +const UsageTemplate string = `Usage: +{{if .Runnable}} {{.UseLine}} +{{end}}{{if .HasAvailableSubCommands}} {{.CommandPath}} COMMAND [OPTIONS] +{{end}}{{if gt (len .Aliases) 0}} +Aliases: + {{.NameAndAliases}} +{{end}}{{if .HasExample}} +Examples: + {{.Example}} +{{end}}{{if .HasAvailableSubCommands}} +Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}} +{{end}}{{if .HasAvailableLocalFlags}} +Options: +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}} +{{end}}{{if .HasAvailableInheritedFlags}} +Global Options: +{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}} +{{end}}{{if .HasHelpSubCommands}} +Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} + {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}} +{{end}}{{if .HasAvailableSubCommands}} +Use "{{.CommandPath}} COMMAND --help" for more information about a COMMAND. +{{end}} +` +// End UsageTemplate + +// HelpTemplate replaces the default help template from cobra +const HelpTemplate string = ` +{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}} +{{end}} +{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` diff --git a/internal/pkg/kernel/kernel.go b/internal/pkg/kernel/kernel.go index 7b342a2b..0d1024a8 100644 --- a/internal/pkg/kernel/kernel.go +++ b/internal/pkg/kernel/kernel.go @@ -102,6 +102,7 @@ func ListKernels() ([]string, error) { func Build(kernelVersion string, kernelName string, root string) (string, error) { kernelImage := path.Join(root, "/boot/vmlinuz-"+kernelVersion) kernelDrivers := path.Join(root, "/lib/modules/"+kernelVersion) + kernelDriversRelative := path.Join("/lib/modules/"+kernelVersion) kernelDestination := KernelImage(kernelName) driversDestination := KmodsImage(kernelName) versionDestination := KernelVersion(kernelName) @@ -177,7 +178,7 @@ func Build(kernelVersion string, kernelName string, root string) (string, error) wwlog.Printf(wwlog.VERBOSE, "Using PIGZ to compress the container: %s\n", compressor) } - cmd := fmt.Sprintf("cd /; find .%s | cpio --quiet -o -H newc | %s -c > \"%s\"", kernelDrivers, compressor, driversDestination) + cmd := fmt.Sprintf("cd %s; find .%s | cpio --quiet -o -H newc | %s -c > \"%s\"", root, kernelDriversRelative, compressor, driversDestination) wwlog.Printf(wwlog.DEBUG, "RUNNING: %s\n", cmd) err = exec.Command("/bin/sh", "-c", cmd).Run() diff --git a/internal/pkg/node/constructors.go b/internal/pkg/node/constructors.go index 0d8b1c0f..13952237 100644 --- a/internal/pkg/node/constructors.go +++ b/internal/pkg/node/constructors.go @@ -1,17 +1,16 @@ package node import ( + "errors" "io/ioutil" "sort" "strings" - "github.com/pkg/errors" - "gopkg.in/yaml.v2" - "github.com/hpcng/warewulf/internal/pkg/wwlog" + "gopkg.in/yaml.v2" ) -const ConfigFile = "/etc/warewulf/nodes.conf" +var ConfigFile = "/etc/warewulf/nodes.conf" func New() (nodeYaml, error) { var ret nodeYaml @@ -264,35 +263,3 @@ func (config *nodeYaml) FindDiscoverableNode() (NodeInfo, string, error) { return ret, "", errors.New("No unconfigured nodes found") } - -func (config *nodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) { - var ret NodeInfo - - n, _ := config.FindAllNodes() - - for _, node := range n { - for _, dev := range node.NetDevs { - if dev.Hwaddr.Get() == hwa { - return node, nil - } - } - } - - return ret, errors.New("No nodes found with HW Addr: " + hwa) -} - -func (config *nodeYaml) FindByIpaddr(ipaddr string) (NodeInfo, error) { - var ret NodeInfo - - n, _ := config.FindAllNodes() - - for _, node := range n { - for _, dev := range node.NetDevs { - if dev.Ipaddr.Get() == ipaddr { - return node, nil - } - } - } - - return ret, errors.New("No nodes found with IP Addr: " + ipaddr) -} diff --git a/internal/pkg/node/datastructure.go b/internal/pkg/node/datastructure.go index e47275e5..17be4d41 100644 --- a/internal/pkg/node/datastructure.go +++ b/internal/pkg/node/datastructure.go @@ -1,9 +1,6 @@ package node import ( - "fmt" - "os" - "github.com/hpcng/warewulf/internal/pkg/util" "github.com/hpcng/warewulf/internal/pkg/wwlog" ) @@ -104,22 +101,10 @@ type NetDevEntry struct { } func init() { - //TODO: Check to make sure nodes.conf is found + // Check that nodes.conf is found if !util.IsFile(ConfigFile) { - c, err := os.OpenFile(ConfigFile, os.O_RDWR|os.O_CREATE, 0644) - if err != nil { - wwlog.Printf(wwlog.ERROR, "Could not create new configuration file: %s\n", err) - // just return silently, as init is also called for bash_completion - return - } - - fmt.Fprintf(c, "nodeprofiles:\n") - fmt.Fprintf(c, " default:\n") - fmt.Fprintf(c, " comment: This profile is automatically included for each node\n") - fmt.Fprintf(c, "nodes: {}\n") - - c.Close() - - wwlog.Printf(wwlog.INFO, "Created default node configuration\n") + wwlog.Printf(wwlog.WARN, "Missing node configuration file\n") + // just return silently, as init is also called for bash_completion + return } } diff --git a/internal/pkg/node/methods.go b/internal/pkg/node/methods.go index b2179a1d..bc5de819 100644 --- a/internal/pkg/node/methods.go +++ b/internal/pkg/node/methods.go @@ -14,7 +14,7 @@ func FilterByName(set []NodeInfo, searchList []string) []NodeInfo { if len(searchList) > 0 { for _, search := range searchList { for _, entry := range set { - b, _ := regexp.MatchString(search, entry.Id.Get()) + b, _ := regexp.MatchString("^"+search+"$", entry.Id.Get()) if b { ret = append(ret, entry) } diff --git a/internal/pkg/node/util.go b/internal/pkg/node/util.go new file mode 100644 index 00000000..4b8bb9b3 --- /dev/null +++ b/internal/pkg/node/util.go @@ -0,0 +1,47 @@ +package node + +import ( + "errors" + "net" + "strings" +) + +func (config *nodeYaml) FindByHwaddr(hwa string) (NodeInfo, error) { + if _, err := net.ParseMAC(hwa); err != nil { + return NodeInfo{}, errors.New("invalid hardware address: " + hwa) + } + + var ret NodeInfo + + n, _ := config.FindAllNodes() + + for _, node := range n { + for _, dev := range node.NetDevs { + if strings.EqualFold(dev.Hwaddr.Get(), hwa) { + return node, nil + } + } + } + + return ret, errors.New("No nodes found with HW Addr: " + hwa) +} + +func (config *nodeYaml) FindByIpaddr(ipaddr string) (NodeInfo, error) { + if net.ParseIP(ipaddr) == nil { + return NodeInfo{}, errors.New("invalid IP:" + ipaddr) + } + + var ret NodeInfo + + n, _ := config.FindAllNodes() + + for _, node := range n { + for _, dev := range node.NetDevs { + if dev.Ipaddr.Get() == ipaddr { + return node, nil + } + } + } + + return ret, errors.New("No nodes found with IP Addr: " + ipaddr) +} diff --git a/internal/pkg/node/util_test.go b/internal/pkg/node/util_test.go new file mode 100644 index 00000000..78f4127c --- /dev/null +++ b/internal/pkg/node/util_test.go @@ -0,0 +1,135 @@ +package node + +import ( + "gopkg.in/yaml.v2" + "testing" +) + +func NewTestNode() (nodeYaml, error) { + var data = ` +nodeprofiles: + default: + comment: This profile is automatically included for each node +nodes: + test_node: + profiles: + - default + network devices: + eth0: + default: true + hwaddr: 00:00:00:00:12:34 + ipaddr: 1.2.3.4 + ipcidr: "" + prefix: "" + netmask: "" + eno1: + default: true + hwaddr: ab:cd:ef:00:12:34 + ipaddr: 1.2.3.4 + ipcidr: "" + prefix: "" + netmask: "" + eno2: + default: true + hwaddr: aB:Cd:eF:12:34:56 + ipaddr: 1.2.3.4 + ipcidr: "" + prefix: "" + netmask: "" + test_node_IPv6: + profiles: + - default + network devices: + eth1: + default: false + hwaddr: "" + ipaddr: fd1a:2b3c:4d5e:06f0:1234:5678:90ab:cdef + ipcidr: "" + prefix: "" + netmask: "" +` + var ret nodeYaml + err := yaml.Unmarshal([]byte(data), &ret) + if err != nil { + return ret, err + } + return ret, nil +} + +func Test_nodeYaml_FindByHwaddr(t *testing.T) { + c, _ := NewTestNode() + //type fields struct { + // NodeProfiles map[string]*NodeConf + // Nodes map[string]*NodeConf + //} + type args struct { + hwa string + } + tests := []struct { + name string + //fields fields + config nodeYaml + args args + want string + wantErr bool + }{ + {"emptyString", c, args{hwa: ""}, "", true}, + {"noIpString", c, args{hwa: "this is not a MAC"}, "", true}, + {"intString", c, args{hwa: "4294967296"}, "", true}, + {"invalidMAC", c, args{hwa: "xx:00:00:00:12:34"}, "", true}, + {"validMACNotFound", c, args{hwa: "aa:FF:ee:65:43:21"}, "", true}, + {"validMAC", c, args{hwa: "ab:cd:ef:00:12:34"}, "test_node", false}, + {"validMAC2", c, args{hwa: "aB:Cd:eF:00:12:34"}, "test_node", false}, + {"validMAC3", c, args{hwa: "Ab:cD:Ef:12:34:56"}, "test_node", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := tt.config + got, err := config.FindByHwaddr(tt.args.hwa) + if (err != nil) != tt.wantErr { + t.Errorf("FindByHwaddr() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !(got.Id.Get() == tt.want) { + t.Errorf("FindByHwaddr() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_nodeYaml_FindByIpaddr(t *testing.T) { + c, _ := NewTestNode() + type args struct { + ipaddr string + } + tests := []struct { + name string + config nodeYaml + args args + want string + wantErr bool + }{ + {"emptyString", c, args{ipaddr: ""}, "", true}, + {"noIpString", c, args{ipaddr: "this is not an IP"}, "", true}, + {"intString", c, args{ipaddr: "4294967296"}, "", true}, + {"invalidIPv4", c, args{ipaddr: "1.2.3.256"}, "", true}, + {"invalidIPv6", c, args{ipaddr: "xd1a:2b3c:4d5e:06f0:1234:5678:90ab:cdef"}, "", true}, + {"validIPv4NotFound", c, args{ipaddr: "1.1.1.1"}, "", true}, + {"validIPv6NotFound", c, args{ipaddr: "fd1a:2b3c:4d5e:06f0:1234:5678:90ab:fedc"}, "", true}, + {"validIPv4", c, args{ipaddr: "1.2.3.4"}, "test_node", false}, + {"validIPv6", c, args{ipaddr: "fd1a:2b3c:4d5e:06f0:1234:5678:90ab:cdef"}, "test_node_IPv6", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := tt.config + got, err := config.FindByIpaddr(tt.args.ipaddr) + if (err != nil) != tt.wantErr { + t.Errorf("FindByIpaddr() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !(got.Id.Get() == tt.want) { + t.Errorf("FindByHwaddr() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go index a6ebc2c9..dda449e7 100644 --- a/internal/pkg/util/util.go +++ b/internal/pkg/util/util.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "math/rand" + "net" "os" "os/exec" "path" @@ -368,3 +369,16 @@ func SplitValidPaths(input, delim string) []string { return (ret) } + +func IncrementIPv4(start string, inc uint) string { + ip_start := net.ParseIP(start) + ipv4 := ip_start.To4() + v4_int := uint(ipv4[0])<<24 + uint(ipv4[1])<<16 + uint(ipv4[2])<<8 + uint(ipv4[3]) + v4_int += inc + v4_o3 := byte(v4_int & 0xFF) + v4_o2 := byte((v4_int >> 8) & 0xFF) + v4_o1 := byte((v4_int >> 16) & 0xFF) + v4_o0 := byte((v4_int >> 24) & 0xFF) + ipv4_new := net.IPv4(v4_o0, v4_o1, v4_o2, v4_o3) + return ipv4_new.String() +} diff --git a/internal/pkg/version/version.go b/internal/pkg/version/version.go new file mode 100644 index 00000000..0e8e1a5a --- /dev/null +++ b/internal/pkg/version/version.go @@ -0,0 +1,7 @@ +package version + +var Version string = "development" + +func GetVersion() string { + return Version +} diff --git a/internal/pkg/warewulfconf/datastructure.go b/internal/pkg/warewulfconf/datastructure.go index 8315ddd6..d47670ec 100644 --- a/internal/pkg/warewulfconf/datastructure.go +++ b/internal/pkg/warewulfconf/datastructure.go @@ -5,7 +5,7 @@ import ( "github.com/hpcng/warewulf/internal/pkg/wwlog" ) -const ConfigFile = "/etc/warewulf/warewulf.conf" +var ConfigFile = "/etc/warewulf/warewulf.conf" type ControllerConf struct { Comment string `yaml:"comment"` diff --git a/internal/pkg/warewulfd/daemon.go b/internal/pkg/warewulfd/daemon.go index 042808d0..acb04ac8 100644 --- a/internal/pkg/warewulfd/daemon.go +++ b/internal/pkg/warewulfd/daemon.go @@ -9,6 +9,7 @@ import ( "syscall" "github.com/hpcng/warewulf/internal/pkg/util" + "github.com/hpcng/warewulf/internal/pkg/version" "github.com/pkg/errors" ) @@ -53,7 +54,7 @@ func DaemonStart() error { fmt.Fprintf(p, "%d", pid) - fmt.Printf("Started Warewulf server at PID: %d\n", pid) + fmt.Printf("Started Warewulf (%s) server at PID: %d\n", version.GetVersion(), pid) } diff --git a/internal/pkg/warewulfd/ipxe.go b/internal/pkg/warewulfd/ipxe.go index f8a57dc9..2eec83ec 100644 --- a/internal/pkg/warewulfd/ipxe.go +++ b/internal/pkg/warewulfd/ipxe.go @@ -18,6 +18,8 @@ type iPxeTemplate struct { WaitTime string Hostname string Fqdn string + Id string + Cluster string ContainerName string Hwaddr string Ipaddr string @@ -129,6 +131,8 @@ func IpxeSend(w http.ResponseWriter, req *http.Request) { var replace iPxeTemplate + replace.Id = nodeobj.Id.Get() + replace.Cluster = nodeobj.ClusterName.Get() replace.Fqdn = nodeobj.Id.Get() replace.Ipaddr = conf.Ipaddr replace.Port = strconv.Itoa(conf.Warewulf.Port) diff --git a/pkg/hostlist/hostlist.go b/pkg/hostlist/hostlist.go new file mode 100644 index 00000000..3c821f87 --- /dev/null +++ b/pkg/hostlist/hostlist.go @@ -0,0 +1,80 @@ +package hostlist + +import ( + "fmt" + "strconv" + "strings" + "unicode" +) + +func toInt(i string) int { + ret, _ := strconv.Atoi(i) + return ret +} + +func isDigit(s string) bool { + r := []rune(s) + + for i := 0; i < len(r); i++ { + if !unicode.IsDigit(r[i]) { + return false + } + } + + return true +} + +func expand_iterate(list []string) ([]string, int) { + var ret []string + var count int + + for _, i := range list { + + bracketIndex1 := strings.Index(i, "[") + bracketIndex2 := strings.Index(i, "]") + + if bracketIndex1 >= 0 && bracketIndex1 < bracketIndex2 { + prefix := i[:bracketIndex1] + suffix := i[bracketIndex2+1:] + ranges := strings.Split(i[bracketIndex1+1:bracketIndex2], ",") + count++ + + for _, r := range ranges { + iterate := strings.Split(r, "-") + + if len(iterate) == 1 { + if !isDigit(iterate[0]) { + return ret, 0 + } else { + ret = append(ret, prefix+iterate[0]+suffix) + } + } else if len(iterate) == 2 { + if !isDigit(iterate[0]) || !isDigit(iterate[1]) { + return ret, 0 + } else { + sigfigures := len(iterate[0]) + for i := toInt(iterate[0]); i <= toInt(iterate[1]); i++ { + ret = append(ret, fmt.Sprintf(`%s%.`+fmt.Sprintf("%d", sigfigures)+`d%s`, prefix, i, suffix)) + } + } + } + } + } + } + return ret, count +} + +func Expand(list []string) []string { + ret := list + + for { + loop, count := expand_iterate(ret) + + if count == 0 { + break + } + ret = loop + } + + return ret +} diff --git a/warewulf.spec.in b/warewulf.spec.in index 7e771cc1..19c4a637 100644 --- a/warewulf.spec.in +++ b/warewulf.spec.in @@ -1,74 +1,107 @@ +%global wwgroup warewulf +%{!?wwshared: %global wwshared %{_localstatedir}} %define debug_package %{nil} Name: warewulf -Summary: A suite of tools for bare metal provisioning of containers +Summary: A provisioning system for large clusters of bare metal and/or virtual systems Version: @VERSION@ -Release: 1%{?dist} -License: BSD -Source: https://github.com/hpcng/warewulf/archive/refs/tags/%{name}-%{version}.tar.gz +Release: @RELEASE@%{?dist} +License: BSD-3-Clause +URL: https://github.com/hpcng/warewulf +Source: https://github.com/hpcng/warewulf/archive/refs/tags/v%{version}.tar.gz + ExclusiveOS: linux +Conflicts: warewulf < 4 +Conflicts: warewulf-common +Conflicts: warewulf-cluster +Conflicts: warewulf-vnfs +Conflicts: warewulf-provision +Conflicts: warewulf-ipmi + BuildRequires: make -BuildRequires: golang %if 0%{?rhel} BuildRequires: systemd +BuildRequires: golang +Requires: tftp-server +Requires: nfs-utils %else +# sle_version BuildRequires: systemd-rpm-macros +BuildRequires: go +Requires: tftp +Requires: nfs-kernel-server %endif -%if 0%{?rhel} > 7 +%if 0%{?rhel} >= 8 || 0%{?sle_version} Requires: dhcp-server %else +# rhel < 8 Requires: dhcp %endif -Requires: tftp-server -Requires: nfs-utils - %description -Warewulf is a bare metal container provisioning system. +Warewulf is a stateless and diskless container operating system provisioning +system for large clusters of bare metal and/or virtual systems. + %prep -%setup -q +%setup -q -n %{name}-%{version} + %build make all + %install %make_install DESTDIR=%{buildroot} %{?mflags_install} + %pre -getent group warewulf >/dev/null || groupadd -r warewulf +getent group %{wwgroup} >/dev/null || groupadd -r %{wwgroup} + %post %systemd_post warewulfd.service + %preun %systemd_preun warewulfd.service + %postun %systemd_postun_with_restart warewulfd.service -%files -%attr(0755, root, warewulf) %dir %{_sysconfdir}/%{name} -%config(noreplace) %{_sysconfdir}/%{name}/* +%files +%defattr(-, root, %{wwgroup}) +%dir %{_sysconfdir}/%{name} +%config(noreplace) %{_sysconfdir}/%{name}/* +%config(noreplace) %attr(0640,-,-) %{_sysconfdir}/%{name}/nodes.conf %{_sysconfdir}/bash_completion.d/warewulf -%{_bindir}/wwctl +%dir %{wwshared}/%{name} +%{wwshared}/%{name}/overlays/runtime/* +%{wwshared}/%{name}/overlays/system/* -%{_prefix}/lib/firewalld/services/warewulf.xml - -%{_unitdir}/warewulfd.service - -%attr(0755, root, warewulf) %dir %{_localstatedir}/%{name} -%{_localstatedir}/%{name}/overlays/runtime/* -%{_localstatedir}/%{name}/overlays/system/* - -%{_mandir}/man1/wwctl* +%attr(-, root, root) %{_bindir}/wwctl +%if 0%{?rhel} +%attr(-, root, root) %{_prefix}/lib/firewalld/services/warewulf.xml +%else +# sle_version +%attr(-, root, root) %{_libexecdir}/firewalld/services/warewulf.xml +%endif +%attr(-, root, root) %{_unitdir}/warewulfd.service +%attr(-, root, root) %{_mandir}/man1/wwctl* %changelog +* Mon Nov 1 2021 Jeremy Siadal - 4.2.0-1 +- Add support for OpenSUSE +- Update file attribs +- Update license string +- Make shared store relocatable + * Fri Sep 24 2021 Michael L. Young - 4.2.0-1 - Update spec file to use systemd macros - Use macros to refer to system paths