Boot command (#53)
This commit is contained in:
parent
03d9501673
commit
b636eb2bf1
|
@ -0,0 +1,82 @@
|
|||
package driver
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"golang.org/x/mobile/event/key"
|
||||
)
|
||||
|
||||
type KeyInput struct {
|
||||
Message string
|
||||
Scancode key.Code
|
||||
Alt bool
|
||||
Ctrl bool
|
||||
Shift bool
|
||||
}
|
||||
|
||||
var scancodeMap = make(map[rune]key.Code)
|
||||
|
||||
func init() {
|
||||
scancodeIndex := make(map[string]key.Code)
|
||||
scancodeIndex["abcdefghijklmnopqrstuvwxyz"] = key.CodeA
|
||||
scancodeIndex["ABCDEFGHIJKLMNOPQRSTUVWXYZ"] = key.CodeA
|
||||
scancodeIndex["1234567890"] = key.Code1
|
||||
scancodeIndex["!@#$%^&*()"] = key.Code1
|
||||
scancodeIndex[" "] = key.CodeSpacebar
|
||||
scancodeIndex["-=[]\\"] = key.CodeHyphenMinus
|
||||
scancodeIndex["_+{}|" ] = key.CodeHyphenMinus
|
||||
scancodeIndex[ ";'`,./" ] = key.CodeSemicolon
|
||||
scancodeIndex[":\"~<>?" ] = key.CodeSemicolon
|
||||
|
||||
for chars, start := range scancodeIndex {
|
||||
for i, r := range chars {
|
||||
scancodeMap[r] = start + key.Code(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const shiftedChars = "!@#$%^&*()_+{}|:\"~<>?"
|
||||
|
||||
func (vm *VirtualMachine) TypeOnKeyboard(input KeyInput) (int32, error) {
|
||||
var spec types.UsbScanCodeSpec
|
||||
|
||||
for _, r := range input.Message {
|
||||
scancode := scancodeMap[r]
|
||||
shift := input.Shift || unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
spec.KeyEvents = append(spec.KeyEvents, types.UsbScanCodeSpecKeyEvent{
|
||||
// https://github.com/lamw/vghetto-scripts/blob/f74bc8ba20064f46592bcce5a873b161a7fa3d72/powershell/VMKeystrokes.ps1#L130
|
||||
UsbHidCode: int32(scancode)<<16 | 7,
|
||||
Modifiers: &types.UsbScanCodeSpecModifierType{
|
||||
LeftControl: &input.Ctrl,
|
||||
LeftAlt: &input.Alt,
|
||||
LeftShift: &shift,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if input.Scancode != 0 {
|
||||
spec.KeyEvents = append(spec.KeyEvents, types.UsbScanCodeSpecKeyEvent{
|
||||
UsbHidCode: int32(input.Scancode)<<16 | 7,
|
||||
Modifiers: &types.UsbScanCodeSpecModifierType{
|
||||
LeftControl: &input.Ctrl,
|
||||
LeftAlt: &input.Alt,
|
||||
LeftShift: &input.Shift,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
req := &types.PutUsbScanCodes{
|
||||
This: vm.vm.Reference(),
|
||||
Spec: spec,
|
||||
}
|
||||
|
||||
resp, err := methods.PutUsbScanCodes(vm.driver.ctx, vm.driver.client.RoundTripper, req)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return resp.Returnval, nil
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
d-i passwd/user-fullname string jetbrains
|
||||
d-i passwd/username string jetbrains
|
||||
d-i passwd/user-password password jetbrains
|
||||
d-i passwd/user-password-again password jetbrains
|
||||
d-i user-setup/allow-password-weak boolean true
|
||||
|
||||
d-i partman-auto/disk string /dev/sda
|
||||
d-i partman-auto/method string regular
|
||||
d-i partman-partitioning/confirm_write_new_label boolean true
|
||||
d-i partman/choose_partition select finish
|
||||
d-i partman/confirm boolean true
|
||||
d-i partman/confirm_nooverwrite boolean true
|
||||
|
||||
d-i pkgsel/include string open-vm-tools openssh-server
|
||||
|
||||
d-i finish-install/reboot_in_progress note
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"builders": [
|
||||
{
|
||||
"type": "vsphere-iso",
|
||||
|
||||
"vcenter_server": "vcenter.vsphere65.test",
|
||||
"username": "root",
|
||||
"password": "jetbrains",
|
||||
"insecure_connection": "true",
|
||||
|
||||
"vm_name": "example-ubuntu",
|
||||
"host": "esxi-1.vsphere65.test",
|
||||
|
||||
"guest_os_type": "ubuntu64Guest",
|
||||
|
||||
"ssh_username": "jetbrains",
|
||||
"ssh_password": "jetbrains",
|
||||
|
||||
"CPUs": 1,
|
||||
"RAM": 1024,
|
||||
"RAM_reserve_all": true,
|
||||
|
||||
"disk_controller_type": "pvscsi",
|
||||
"disk_size": 32,
|
||||
"disk_thin_provisioned": true,
|
||||
|
||||
"network_card": "vmxnet3",
|
||||
|
||||
"iso_paths": [
|
||||
"[datastore1] ISO/ubuntu-16.04.3-server-amd64.iso"
|
||||
],
|
||||
"floppy_files": [
|
||||
"{{template_dir}}/preseed.cfg"
|
||||
],
|
||||
"boot_command": [
|
||||
"<enter><wait><f6><esc><wait>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs>",
|
||||
"/install/vmlinuz",
|
||||
" initrd=/install/initrd.gz",
|
||||
" priority=critical",
|
||||
" locale=en_US",
|
||||
" file=/media/preseed.cfg",
|
||||
"<enter>"
|
||||
],
|
||||
"boot_order": "disk,cdrom"
|
||||
}
|
||||
],
|
||||
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": ["ls /"]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
hash: 709325293541fff31ae48288c6f5873f67f90ae6ab69d9ae4465fcdf0626d843
|
||||
updated: 2017-06-27T10:42:45.867528732+03:00
|
||||
hash: 9dff375b5720b1ffb1164694bb6aba1f546c9e2a93509000eb9919e5241610f1
|
||||
updated: 2018-02-17T22:26:59.87828+03:00
|
||||
imports:
|
||||
- name: github.com/Azure/go-ntlmssp
|
||||
version: 29affced641074a59483ed003b5ef73a8bd3593c
|
||||
|
@ -26,6 +26,7 @@ imports:
|
|||
- communicator/none
|
||||
- communicator/ssh
|
||||
- communicator/winrm
|
||||
- helper/builder/testing
|
||||
- helper/communicator
|
||||
- helper/config
|
||||
- packer
|
||||
|
@ -105,4 +106,8 @@ imports:
|
|||
- md4
|
||||
- ssh
|
||||
- ssh/agent
|
||||
- name: golang.org/x/mobile
|
||||
version: 4bfed40defcb2fed54d60707c13cec9d7888a483
|
||||
subpackages:
|
||||
- event/key
|
||||
testImports: []
|
||||
|
|
|
@ -6,3 +6,4 @@ import:
|
|||
- package: github.com/vmware/govmomi
|
||||
version: v0.15.0
|
||||
- package: golang.org/x/crypto
|
||||
- package: golang.org/x/mobile
|
||||
|
|
|
@ -57,6 +57,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
|||
&common.StepRun{
|
||||
Config: &b.config.RunConfig,
|
||||
},
|
||||
&StepBootCommand{
|
||||
Config: &b.config.BootConfig,
|
||||
},
|
||||
&common.StepWaitForIp{},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.Comm,
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
type Config struct {
|
||||
packerCommon.PackerConfig `mapstructure:",squash"`
|
||||
common.RunConfig `mapstructure:",squash"`
|
||||
BootConfig `mapstructure:",squash"`
|
||||
common.ConnectConfig `mapstructure:",squash"`
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
common.ShutdownConfig `mapstructure:",squash"`
|
||||
|
|
|
@ -0,0 +1,173 @@
|
|||
package iso
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/jetbrains-infra/packer-builder-vsphere/driver"
|
||||
"github.com/mitchellh/multistep"
|
||||
"fmt"
|
||||
"time"
|
||||
"strings"
|
||||
"golang.org/x/mobile/event/key"
|
||||
"unicode/utf8"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"os"
|
||||
"log"
|
||||
)
|
||||
|
||||
type BootConfig struct {
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
}
|
||||
|
||||
func (c *BootConfig) Prepare() []error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type StepBootCommand struct {
|
||||
Config *BootConfig
|
||||
}
|
||||
|
||||
var special = map[string]key.Code{
|
||||
"<enter>": key.CodeReturnEnter,
|
||||
"<esc>": key.CodeEscape,
|
||||
"<bs>": key.CodeDeleteBackspace,
|
||||
"<del>": key.CodeDeleteForward,
|
||||
"<tab>": key.CodeTab,
|
||||
"<f1>": key.CodeF1,
|
||||
"<f2>": key.CodeF2,
|
||||
"<f3>": key.CodeF3,
|
||||
"<f4>": key.CodeF4,
|
||||
"<f5>": key.CodeF5,
|
||||
"<f6>": key.CodeF6,
|
||||
"<f7>": key.CodeF7,
|
||||
"<f8>": key.CodeF8,
|
||||
"<f9>": key.CodeF9,
|
||||
"<f10>": key.CodeF10,
|
||||
"<f11>": key.CodeF11,
|
||||
"<f12>": key.CodeF12,
|
||||
"<insert>": key.CodeInsert,
|
||||
"<home>": key.CodeHome,
|
||||
"<end>": key.CodeEnd,
|
||||
"<pageUp>": key.CodePageUp,
|
||||
"<pageDown>": key.CodePageDown,
|
||||
"<left>": key.CodeLeftArrow,
|
||||
"<right>": key.CodeRightArrow,
|
||||
"<up>": key.CodeUpArrow,
|
||||
"<down>": key.CodeDownArrow,
|
||||
}
|
||||
|
||||
var keyInterval = common.PackerKeyDefault
|
||||
|
||||
func init() {
|
||||
if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil {
|
||||
keyInterval = delay
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StepBootCommand) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||
|
||||
ui.Say("Typing boot command...")
|
||||
|
||||
var keyAlt bool
|
||||
var keyCtrl bool
|
||||
var keyShift bool
|
||||
|
||||
for _, message := range s.Config.BootCommand {
|
||||
for len(message) > 0 {
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait>") {
|
||||
log.Printf("Waiting 1 second")
|
||||
time.Sleep(1 * time.Second)
|
||||
message = message[len("<wait>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait5>") {
|
||||
log.Printf("Waiting 5 seconds")
|
||||
time.Sleep(5 * time.Second)
|
||||
message = message[len("<wait5>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<wait10>") {
|
||||
log.Printf("Waiting 10 seconds")
|
||||
time.Sleep(10 * time.Second)
|
||||
message = message[len("<wait10>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftAltOn>") {
|
||||
keyAlt = true
|
||||
message = message[len("<leftAltOn>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftAltOff>") {
|
||||
keyAlt = false
|
||||
message = message[len("<leftAltOff>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftCtrlOn>") {
|
||||
keyCtrl = true
|
||||
message = message[len("<leftCtrlOn>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftCtrlOff>") {
|
||||
keyCtrl = false
|
||||
message = message[len("<leftCtrlOff>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftShiftOn>") {
|
||||
keyShift = true
|
||||
message = message[len("<leftShiftOn>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "<leftShiftOff>") {
|
||||
keyShift = false
|
||||
message = message[len("<leftShiftOff>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
var scancode key.Code
|
||||
for specialCode, specialValue := range special {
|
||||
if strings.HasPrefix(message, specialCode) {
|
||||
scancode = specialValue
|
||||
log.Printf("Special code '%s' found, replacing with: %s", specialCode, specialValue)
|
||||
message = message[len(specialCode):]
|
||||
}
|
||||
}
|
||||
|
||||
var char rune
|
||||
if scancode == 0 {
|
||||
var size int
|
||||
char, size = utf8.DecodeRuneInString(message)
|
||||
message = message[size:]
|
||||
}
|
||||
|
||||
_, err := vm.TypeOnKeyboard(driver.KeyInput{
|
||||
Message: string(char),
|
||||
Scancode: scancode,
|
||||
Ctrl: keyCtrl,
|
||||
Alt: keyAlt,
|
||||
Shift: keyShift,
|
||||
})
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("error typing a boot command: %v", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
time.Sleep(keyInterval)
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepBootCommand) Cleanup(state multistep.StateBag) {}
|
Loading…
Reference in New Issue