Class: Pvectl::Commands::Service
- Inherits:
-
Object
- Object
- Pvectl::Commands::Service
- Defined in:
- lib/pvectl/commands/service.rb,
sig/pvectl/commands/service.rbs
Overview
Top-level pvectl service command for managing systemd services on
Proxmox nodes.
Exposes four lifecycle sub-commands:
service start <name>start a serviceservice stop <name>stop a service (irreversible — requires --yes or prompt)service restart <name>hard restart a service (irreversible — requires --yes or prompt)service reload <name>reload (graceful where supported)
Each sub-command requires --node NODE (falls back to default-node from
the active context configuration when not provided).
Constant Summary collapse
- CONFIRMABLE_OPERATIONS =
All confirmable operations (stop and restart can disrupt running workloads).
i[stop restart].freeze
- OPERATIONS =
All supported operations.
i[start stop restart reload].freeze
Class Method Summary collapse
-
.execute(operation, args, options, global_options) ⇒ Integer
Executes the command.
-
.register(cli) ⇒ void
Registers the service command and all sub-commands with the CLI.
-
.register_subcommand(parent, operation) ⇒ void
Registers a single lifecycle sub-command.
-
.subcommand_long_desc(operation) ⇒ String
Builds the man-page-style long_desc for a sub-command.
Instance Method Summary collapse
-
#config_error(message) ⇒ Integer
Outputs a config error.
-
#confirm!(service_name, node) ⇒ Boolean
Confirms the operation when required.
-
#execute ⇒ Integer
Executes the command.
-
#initialize(operation, args, options, global_options, prompt: $stdin, output: $stdout) ⇒ Service
constructor
Initializes a service lifecycle command.
-
#load_config ⇒ void
Loads configuration.
-
#output_result(result) ⇒ void
Outputs the operation result using the configured formatter.
-
#perform(service_name, node) ⇒ Models::NodeOperationResult
Performs the API call via ServiceLifecycle.
-
#resolve_node ⇒ String?
Resolves the node from --node option or default-node config.
-
#service_warning(service_name) ⇒ String?
Returns a warning string for sensitive services, or nil.
-
#usage_error(message) ⇒ Integer
Outputs a usage error.
Constructor Details
#initialize(operation, args, options, global_options, prompt: $stdin, output: $stdout) ⇒ Service
Initializes a service lifecycle command.
163 164 165 166 167 168 169 170 |
# File 'lib/pvectl/commands/service.rb', line 163 def initialize(operation, args, , , prompt: $stdin, output: $stdout) @operation = operation @args = Array(args).compact = = @prompt = prompt @output = output end |
Class Method Details
.execute(operation, args, options, global_options) ⇒ Integer
Executes the command.
151 152 153 |
# File 'lib/pvectl/commands/service.rb', line 151 def self.execute(operation, args, , ) new(operation, args, , ).execute end |
.register(cli) ⇒ void
This method returns an undefined value.
Registers the service command and all sub-commands with the CLI.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/pvectl/commands/service.rb', line 32 def self.register(cli) cli.desc "Manage systemd services on Proxmox nodes" cli.long_desc " Manage systemd services running on Proxmox VE nodes. Wraps the Proxmox\n /nodes/{node}/services API to start, stop, restart, or reload daemons\n such as pveproxy, pvedaemon, corosync, and others.\n\n SUB-COMMANDS\n service start NAME Start a stopped service\n service stop NAME Stop a running service (irreversible)\n service restart NAME Hard restart a service (irreversible)\n service reload NAME Reload a service (graceful where supported)\n\n EXAMPLES\n Restart the API proxy on a single node (with confirmation skipped):\n $ pvectl service restart pveproxy --node pve1 --yes\n\n Start a stopped service on the default node:\n $ pvectl service start cron\n\n Stop a service after interactive confirmation:\n $ pvectl service stop pve-firewall --node pve1\n\n Reload the syslog daemon (no interruption to running workloads):\n $ pvectl service reload syslog --node pve1\n\n NOTES\n --node defaults to the context's default-node if configured.\n\n stop and restart are irreversible and require either an interactive\n \"yes\" confirmation or the --yes flag to skip the prompt.\n\n Restarting pveproxy or corosync can momentarily disconnect the\n current API session and break cluster membership respectively. Use\n --yes only when you understand the impact.\n\n Operations are asynchronous \u2014 the result includes the Proxmox task\n UPID which can be inspected with `pvectl get tasks` and\n `pvectl logs task UPID`.\n\n SEE ALSO\n pvectl help get List resources (try `get services`)\n pvectl help logs Inspect task output\n HELP\n cli.command :service do |c|\n # Shared flags declared on the parent so all subcommands inherit them.\n # Avoids GLI flag-redefinition errors when the same flag is needed by\n # multiple sibling subcommands (e.g. start/stop/restart/reload all\n # need --node and --yes).\n c.desc \"Node name (defaults to context default-node)\"\n c.flag [:node], arg_name: \"NODE\"\n\n c.desc \"Skip interactive confirmation prompt\"\n c.switch [:yes, :y], negatable: false\n\n OPERATIONS.each do |op|\n register_subcommand(c, op)\n end\n end\nend\n" |
.register_subcommand(parent, operation) ⇒ void
This method returns an undefined value.
Registers a single lifecycle sub-command.
98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/pvectl/commands/service.rb', line 98 def self.register_subcommand(parent, operation) parent.desc "#{operation.capitalize} a systemd service on a Proxmox node" parent.long_desc subcommand_long_desc(operation) parent.arg_name "SERVICE_NAME" parent.command operation do |sub| sub.action do |, , args| exit_code = execute(operation, args, , ) exit exit_code if exit_code != 0 end end end |
.subcommand_long_desc(operation) ⇒ String
Builds the man-page-style long_desc for a sub-command.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/pvectl/commands/service.rb', line 114 def self.subcommand_long_desc(operation) action = operation.to_s confirm_note = if CONFIRMABLE_OPERATIONS.include?(operation) "This operation is irreversible. Without --yes, pvectl will\n prompt for interactive confirmation before contacting the API." else "No confirmation is required for this operation." end " \#{action.capitalize} a systemd service on a Proxmox node.\n\n EXAMPLES\n $ pvectl service \#{action} pveproxy --node pve1\n $ pvectl service \#{action} pveproxy --node pve1 --yes\n\n NOTES\n \#{confirm_note}\n\n Restarting pveproxy or corosync can momentarily disconnect the\n current API session and break cluster membership respectively.\n\n --node defaults to the context's default-node.\n\n SEE ALSO\n pvectl help service Parent command\n pvectl help get List resources (try `get services`)\n HELP\nend\n" |
Instance Method Details
#config_error(message) ⇒ Integer
Outputs a config error.
287 288 289 290 |
# File 'lib/pvectl/commands/service.rb', line 287 def config_error() $stderr.puts "Error: #{message}" ExitCodes::CONFIG_ERROR end |
#confirm!(service_name, node) ⇒ Boolean
Confirms the operation when required.
223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/pvectl/commands/service.rb', line 223 def confirm!(service_name, node) return true unless CONFIRMABLE_OPERATIONS.include?(@operation) return true if [:yes] warning = service_warning(service_name) @output.puts "About to #{@operation} service '#{service_name}' on node '#{node}'." @output.puts warning if warning @output.print "Continue? [y/N] " answer = @prompt.gets&.strip&.downcase confirmed = %w[y yes].include?(answer) @output.puts "Aborted." unless confirmed confirmed end |
#execute ⇒ Integer
Executes the command.
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/pvectl/commands/service.rb', line 175 def execute return usage_error("service name is required") if @args.empty? service_name = @args.first load_config node = resolve_node return config_error("node is required (provide --node or configure default-node)") unless node return ExitCodes::SUCCESS unless confirm!(service_name, node) result = perform(service_name, node) output_result(result) result.failed? ? ExitCodes::GENERAL_ERROR : ExitCodes::SUCCESS rescue Pvectl::Config::ConfigNotFoundError, Pvectl::Config::InvalidConfigError, Pvectl::Config::ContextNotFoundError, Pvectl::Config::ClusterNotFoundError, Pvectl::Config::UserNotFoundError => e $stderr.puts "Error: #{e.message}" ExitCodes::CONFIG_ERROR rescue StandardError => e $stderr.puts "Error: #{e.message}" ExitCodes::GENERAL_ERROR end |
#load_config ⇒ void
This method returns an undefined value.
Loads configuration.
205 206 207 208 209 |
# File 'lib/pvectl/commands/service.rb', line 205 def load_config service = Pvectl::Config::Service.new service.load(config: [:config]) @config = service.current_config end |
#output_result(result) ⇒ void
This method returns an undefined value.
Outputs the operation result using the configured formatter.
266 267 268 269 270 271 272 |
# File 'lib/pvectl/commands/service.rb', line 266 def output_result(result) format = [:output] || "table" color = [:color] formatter = Pvectl::Formatters::Registry.for(format) presenter = Pvectl::Presenters::NodeOperationResult.new @output.puts formatter.format([result], presenter, color: color) end |
#perform(service_name, node) ⇒ Models::NodeOperationResult
Performs the API call via ServiceLifecycle.
255 256 257 258 259 260 |
# File 'lib/pvectl/commands/service.rb', line 255 def perform(service_name, node) connection = Pvectl::Connection.new(@config) repository = Pvectl::Repositories::Service.new(connection) lifecycle = Pvectl::Services::ServiceLifecycle.new(service_repository: repository) lifecycle.execute(operation: @operation, node: node, service: service_name) end |
#resolve_node ⇒ String?
Resolves the node from --node option or default-node config.
214 215 216 |
# File 'lib/pvectl/commands/service.rb', line 214 def resolve_node [:node] || @config&.default_node end |
#service_warning(service_name) ⇒ String?
Returns a warning string for sensitive services, or nil.
241 242 243 244 245 246 247 248 |
# File 'lib/pvectl/commands/service.rb', line 241 def service_warning(service_name) case service_name when "pveproxy", "pvedaemon" "Warning: this may disconnect the current API session." when "corosync", "pve-cluster" "Warning: this can disrupt cluster membership and quorum." end end |
#usage_error(message) ⇒ Integer
Outputs a usage error.
278 279 280 281 |
# File 'lib/pvectl/commands/service.rb', line 278 def usage_error() $stderr.puts "Error: #{message}" ExitCodes::USAGE_ERROR end |