diff --git a/README.md b/README.md index a97ecec..21ece89 100644 --- a/README.md +++ b/README.md @@ -86,8 +86,9 @@ power -m wol --config config.yaml ``` Currently, two modules are available: -* `ilo`: use of HP iLO technology integrated into ProLiant range servers. This module enables the server to be switched on and off with a complete display of its status. -* `wol`: use Wake-on-LAN to start the server. This module only allows the server to be started, and has a restricted display of the server status. + * `ilo`: use of HP iLO technology integrated into ProLiant range servers. This module enables the server to be switched on and off with a complete display of its status. + * `wol`: use Wake-on-LAN to start the server. This module only allows the server to be started, and has a restricted display of + the server status. *❗️ The `ilo` module has only been implemented and tested based on the `iLO4` API, and is therefore probably not compatible with other major versions. Don't hesitate to start an issue or a pull request if you're interested in other versions.* @@ -205,7 +206,7 @@ Finally, all you have to do now is open your browser, type in the address corres - + ## Usage
@@ -226,6 +227,29 @@ sudo journalctl -u power@my_module.service *❕ As the page is not reactive, it must be reloaded to update and view the current server state.* +--- + +It is also possible to use this tool from the command line. There's no point in instantiating it as a daemon if you only want to use it that way. + +Three commands are available: +* `up`: starts the server +* `down`: turns off the server +* `state`: provides server status in JSON format + +Since the `ilo` module simulates the pressing of the power button, regardless of whether it is to switch the server on or off, it is advisable to check the status of the server before carrying out such an operation. + +For example, if you want to create an entry in your `crontab` to start the server while checking that it's not already running, you can use the following command: + +```crontab +SHELL=/bin/bash + +0 20 * * * [ "$(power -m ilo state | jq -r '.power == false and .led == false')" = "true" ] && power -m ilo up +``` + +*❕ The previous command requires the `jq` utility to run.* + +Concerning the `wol` module, as mentioned earlier, it does not allow you to shut down the server, so the `down` command will have no effect. +

(back to top)

diff --git a/main.go b/main.go index 6bf8b9a..decb589 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "embed" + "encoding/json" "fmt" "html/template" "io/fs" @@ -22,9 +23,9 @@ import ( ) func init() { - rootCmd.Flags().StringVar(&configFilePath, "config", path.Join("/opt", appName, "config.yaml"), "YAML configuration file") - rootCmd.Flags().StringVarP(&moduleName, "module", "m", "", "module for switching the server on or off") - rootCmd.MarkFlagRequired("module") + rootCmd.PersistentFlags().StringVar(&configFilePath, "config", path.Join("/etc", fmt.Sprintf("%s.d", appName), "config.yaml"), "YAML configuration file") + rootCmd.PersistentFlags().StringVarP(&moduleName, "module", "m", "", "module for switching the server on or off") + rootCmd.MarkPersistentFlagRequired("module") } const appName = "power" @@ -35,7 +36,7 @@ var ( rootCmd = &cobra.Command{ Use: appName, Short: "All-in-one tool for remote server power control", - Version: "1.0.0", + Version: "1.1.0", Args: cobra.NoArgs, Run: run, CompletionOptions: cobra.CompletionOptions{ @@ -65,7 +66,7 @@ func parseYAMLFile(filePath string) (*Config, error) { return &config, nil } -func run(cmd *cobra.Command, args []string) { +func parseConfigFile(filePath string) *Config { config, err := parseYAMLFile(configFilePath) if err != nil { fmt.Fprintf(os.Stderr, "Failed to parse YAML file %q: %s\n", configFilePath, err) @@ -79,6 +80,10 @@ func run(cmd *cobra.Command, args []string) { os.Exit(1) } + return config +} + +func createModule(config *Config, moduleName string) modules.Module { internalModules := map[string]modules.Module{ "ilo": ilo.New(), "wol": wakeonlan.New(), @@ -94,13 +99,19 @@ func run(cmd *cobra.Command, args []string) { os.Exit(1) } - err = module.Init(config.Module) + err := module.Init(config.Module) if err != nil { fmt.Fprintf(os.Stderr, "Error during module initialization: %s\n", err) os.Exit(1) - return } + return module +} + +func run(cmd *cobra.Command, args []string) { + config := parseConfigFile(configFilePath) + module := createModule(config, moduleName) + runServer(config, module) } @@ -173,6 +184,68 @@ func runServer(config *Config, module modules.Module) { router.Run() } +func init() { + rootCmd.AddCommand(upCmd, downCmd, stateCmd) +} + +var ( + upCmd = &cobra.Command{ + Use: "up", + Short: "Start the server", + Run: func(cmd *cobra.Command, args []string) { + config := parseConfigFile(configFilePath) + module := createModule(config, moduleName) + + module.PowerOn() + }, + } + downCmd = &cobra.Command{ + Use: "down", + Short: "Turn off the server", + Run: func(cmd *cobra.Command, args []string) { + config := parseConfigFile(configFilePath) + module := createModule(config, moduleName) + + module.PowerOff() + }, + } + stateCmd = &cobra.Command{ + Use: "state", + Short: "Fetch the server state", + Run: func(cmd *cobra.Command, args []string) { + config := parseConfigFile(configFilePath) + module := createModule(config, moduleName) + + powerState, ledState := module.State() + + if powerState.Err != nil { + fmt.Fprintf(os.Stderr, "Failed to retrieve POWER state: %s\n", powerState.Err) + os.Exit(1) + } + if ledState.Err != nil { + fmt.Fprintf(os.Stderr, "Failed to retrieve LED state: %s\n", powerState.Err) + os.Exit(1) + } + + state := struct { + Power bool `json:"power"` + Led bool `json:"led"` + }{ + Power: powerState.Value, + Led: ledState.Value, + } + + jsonString, err := json.Marshal(state) + if err != nil { + fmt.Println("Error during JSON conversion:", err) + os.Exit(1) + } + + fmt.Println(string(jsonString)) + }, + } +) + func main() { err := rootCmd.Execute() if err != nil {