Boot command (#53)

This commit is contained in:
Michael Kuzmin 2018-02-18 05:13:56 +03:00 committed by GitHub
parent 03d9501673
commit b636eb2bf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 346 additions and 2 deletions

82
driver/vm_keyboard.go Normal file
View File

@ -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
}

View File

@ -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

View File

@ -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 /"]
}
]
}

9
glide.lock generated
View File

@ -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: []

View File

@ -6,3 +6,4 @@ import:
- package: github.com/vmware/govmomi
version: v0.15.0
- package: golang.org/x/crypto
- package: golang.org/x/mobile

View File

@ -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,

View File

@ -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"`

173
iso/step_boot_command.go Normal file
View File

@ -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) {}