-
Notifications
You must be signed in to change notification settings - Fork 3
feat: add PiKVM module #247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
9dbc7d7 to
ad77246
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice changes, looks pretty clean overall. Just some thoughts:
- Maybe separate the power,virtual-media,keyboard functionalities into separate files for better readability
- 'combo' -> key-combo
Tell me what you think 😄
ad77246 to
ae564e7
Compare
I think thats a good idea :) |
ae564e7 to
4e04fe1
Compare
441f7ee to
2183a74
Compare
The PiKVM module provides comprehensive control of a DUT via a PiKVM device. It offers power management through ATX control, keyboard input simulation, and virtual media mounting capabilities. Power management commands: on, off, force-off, reset, force-reset, and status. Keyboard control commands: type, key, combo, and paste. Virtual media commands: mount, mount-url, unmount, and media-status. The module connects to a PiKVM device using HTTP/HTTPS with configurable host, user, password, and timeout. It supports both short and long ATX button presses for power and reset control, allows sending keyboard input and key combinations, and enables mounting ISO images from local files or URLs. Signed-off-by: llogen <[email protected]>
2183a74 to
9454670
Compare
jenstopp
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, full KVM feature set implemented 🚀 :)
| @@ -0,0 +1,167 @@ | |||
| # PiKVM | |||
|
|
|||
| This module provides comprehensive control of a DUT via a PiKVM device. It offers power management through ATX control, keyboard input simulation, and virtual media mounting capabilities. | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a link to PiKVM for further info? In case someone does not know this device. Also ist is desinged to support a special PiKVM device?
| key <keyname> Send a single key (e.g., Enter, Escape, F12) | ||
| key-combo <keys> Send key combination (e.g., Ctrl+Alt+Delete) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Write or refer to a full list of available 'key strings'
| This module interacts with the following PiKVM API endpoints: | ||
|
|
||
| - `/api/atx` - Get ATX power status | ||
| - `/api/atx/power` - Intelligent power management (on/off/off_hard/reset_hard) | ||
| - `/api/atx/click` - ATX button control (reset) | ||
| - `/api/hid/print` - Type text input | ||
| - `/api/hid/events/send_key` - Send keyboard keys and combinations | ||
| - `/api/msd` - Mass Storage Device (virtual media) status | ||
| - `/api/msd/write` - Upload images to PiKVM storage | ||
| - `/api/msd/write_remote` - Download images from URL to PiKVM | ||
| - `/api/msd/set_params` - Configure virtual media parameters | ||
| - `/api/msd/set_connected` - Mount/unmount media | ||
| - `/api/msd/remove` - Delete images from storage | ||
| - `/api/streamer/snapshot` - Capture screenshot |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would a user need this info for?
| | user | string | admin | Username for authentication | | ||
| | password | string | - | Password for authentication | | ||
| | timeout | string | 10s | Timeout for HTTP requests (e.g., "10s", "30s") | | ||
| | command | string | - | **Required**: Command type ("power", "keyboard", "media", "screenshot") | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is only one supported at a time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes you can only define one command per command. @RiSKeD and I talked about making the command parameter optional to also allow full control just via arguments.
| ### Basic Power Control | ||
|
|
||
| ```yaml | ||
| version: 0 | ||
| devices: | ||
| my-server: | ||
| desc: "Server controlled via PiKVM" | ||
| cmds: | ||
| power: | ||
| desc: "Power management: on|off|force-off|reset|force-reset|status" | ||
| modules: | ||
| - module: pikvm | ||
| main: true | ||
| options: | ||
| host: https://pikvm.local | ||
| user: admin | ||
| password: admin | ||
| command: power | ||
| ``` | ||
|
|
||
| ### Boot Menu Access | ||
|
|
||
| ```yaml | ||
| keyboard: | ||
| desc: "Keyboard control: type <text>|key <keyname>|key-combo <combo>" | ||
| modules: | ||
| - module: pikvm | ||
| main: true | ||
| options: | ||
| host: https://pikvm.local | ||
| user: admin | ||
| password: admin | ||
| command: keyboard | ||
|
|
||
| # Usage: dutctl my-server keyboard key F12 | ||
| ``` | ||
|
|
||
| ### ISO Mounting | ||
|
|
||
| ```yaml | ||
| media: | ||
| desc: "Virtual media control: mount <path>|mount-url <url>|unmount|media-status" | ||
| modules: | ||
| - module: pikvm | ||
| main: true | ||
| options: | ||
| host: https://pikvm.local | ||
| user: admin | ||
| password: admin | ||
| command: media | ||
|
|
||
| # Usage: dutctl my-server media mount-url https://releases.ubuntu.com/22.04/ubuntu-22.04-live-server-amd64.iso | ||
| ``` | ||
|
|
||
| ### Screenshot Capture | ||
|
|
||
| ```yaml | ||
| screenshot: | ||
| desc: "Capture a screenshot from PiKVM" | ||
| modules: | ||
| - module: pikvm | ||
| main: true | ||
| options: | ||
| host: https://pikvm.local | ||
| user: admin | ||
| password: admin | ||
| command: screenshot | ||
|
|
||
| # Usage: dutctl my-server screenshot | ||
| # The screenshot will be saved to the current directory | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put all examples in YAML files and link them here for better overview
| if p.Command == "" { | ||
| return fmt.Errorf("pikvm: command option is required (must be: %s, %s, %s, or %s)", | ||
| cmdTypePower, cmdTypeKeyboard, cmdTypeMedia, cmdTypeScreenshot) | ||
| } | ||
|
|
||
| if !isValidCommand(p.Command) { | ||
| return fmt.Errorf("pikvm: invalid command %q (must be: %s, %s, %s, or %s)", | ||
| p.Command, cmdTypePower, cmdTypeKeyboard, cmdTypeMedia, cmdTypeScreenshot) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combine in one function
| host := strings.TrimSpace(p.Host) | ||
| if !strings.HasPrefix(host, "http://") && !strings.HasPrefix(host, "https://") { | ||
| host = "https://" + host | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combine with the host validation part from above
| case cmdTypeScreenshot: | ||
| return p.handleScreenshot(ctx, s) | ||
| default: | ||
| return fmt.Errorf("pikvm: unknown command type %q", p.Command) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try to show that it is not the users fault:
| return fmt.Errorf("pikvm: unknown command type %q", p.Command) | |
| return fmt.Errorf("pikvm: misconfigured: unknown command type %q", p.Command) |
| return fmt.Errorf("pikvm: unknown command type %q", p.Command) | |
| return fmt.Errorf("pikvm: invalid configuration: unknown command type %q", p.Command) |
| func (p *PiKVM) doRequest(ctx context.Context, method, urlPath string, body io.Reader, contentType string) (*http.Response, error) { | ||
| return p.doRequestWithOptions(ctx, method, urlPath, body, contentType, requestOptions{}) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this for testing purposes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is for the purpose of not having 3 different methods and combine them within one with the requestOptions input
| func (p *PiKVM) buildRequestURL(urlPath string) (string, error) { | ||
| targetURL := *p.baseURL | ||
|
|
||
| parsedPath, err := url.Parse(urlPath) | ||
| if err != nil { | ||
| return "", fmt.Errorf("invalid URL path: %v", err) | ||
| } | ||
|
|
||
| targetURL.Path = path.Join(targetURL.Path, parsedPath.Path) | ||
| targetURL.RawQuery = parsedPath.RawQuery | ||
|
|
||
| return targetURL.String(), nil | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this done during init?
| ) | ||
|
|
||
| const ( | ||
| keyDelay = 500 * time.Millisecond |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe default this and make the delay an option instead?
| } | ||
|
|
||
| // Check for invalid characters | ||
| for _, c := range []string{invalidCharNul, invalidCharNewline, invalidCharReturn} { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would maybe suggest to use a precompiled regex instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or an array of values
| contentType string, | ||
| ) (*http.Response, error) { | ||
| return p.doRequestWithOptions(ctx, method, urlPath, body, contentType, requestOptions{noTimeout: true}) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of this wrappers, just call the function with options directly
The PiKVM module provides comprehensive control of a DUT via a PiKVM device. It offers power management through ATX control, keyboard input simulation, and virtual media mounting capabilities.
Power management commands: on, off, force-off, reset, force-reset,
and status. Keyboard control commands: type, key, combo, and paste.
Virtual media commands: mount, mount-url, unmount, and media-status.
The module connects to a PiKVM device using HTTP/HTTPS with configurable host, user, password, and timeout. It supports both short and long ATX button presses for power and reset control, allows sending keyboard input and key combinations, and enables mounting ISO images from local files or URLs.
resolves #246