Skip to content

Commit e674358

Browse files
authored
Merge pull request #176 from vatesfr/upgrade-boot-command-parser
Improve boot command parser
2 parents e4d0897 + 2674f1a commit e674358

File tree

5 files changed

+54
-105
lines changed

5 files changed

+54
-105
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ packer_cache/*
77
builder-*
88
dist/*
99
vendor/*
10-
packer-plugin-xenserver
10+
packer-plugin-xenserver
11+
packer-builder-xenserver

builder/xenserver/common/step_type_boot_command.go

Lines changed: 19 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,15 @@ import (
1111
"net"
1212
"strings"
1313
"time"
14-
"unicode"
15-
"unicode/utf8"
1614

15+
"github.com/hashicorp/packer-plugin-sdk/bootcommand"
1716
"github.com/hashicorp/packer-plugin-sdk/multistep"
1817
"github.com/hashicorp/packer-plugin-sdk/packer"
1918
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
2019
"github.com/mitchellh/go-vnc"
2120
)
2221

23-
const KeyLeftShift uint = 0xFFE1
22+
const KeyLeftShift uint32 = 0xFFE1
2423

2524
type bootCommandTemplateData struct {
2625
Name string
@@ -149,9 +148,13 @@ func (step *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateB
149148
uint(httpPort),
150149
}
151150

151+
d := bootcommand.NewVNCDriver(vncClient, time.Second/10)
152+
152153
ui.Say("Typing boot commands over VNC...")
153154
for _, command := range config.BootCommand {
154-
155+
if len(command) == 0 {
156+
continue
157+
}
155158
command, err := interpolate.Render(command, &step.Ctx)
156159
if err != nil {
157160
err := fmt.Errorf("Error preparing boot command: %s", err)
@@ -160,112 +163,24 @@ func (step *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateB
160163
return multistep.ActionHalt
161164
}
162165

163-
// Check for interrupts
164-
if _, ok := state.GetOk(multistep.StateCancelled); ok {
166+
seq, err := bootcommand.GenerateExpressionSequence(command)
167+
if err != nil {
168+
err := fmt.Errorf("Error generating boot command: %s", err)
169+
state.Put("error", err)
170+
ui.Error(err.Error())
171+
return multistep.ActionHalt
172+
}
173+
174+
if err := seq.Do(ctx, d); err != nil {
175+
err := fmt.Errorf("Error running boot command: %s", err)
176+
state.Put("error", err)
177+
ui.Error(err.Error())
165178
return multistep.ActionHalt
166179
}
167180

168-
vncSendString(vncClient, command)
169181
}
170182

171183
return multistep.ActionContinue
172184
}
173185

174186
func (step *StepTypeBootCommand) Cleanup(multistep.StateBag) {}
175-
176-
// Taken from qemu's builder plugin - not an exported function.
177-
func vncSendString(c *vnc.ClientConn, original string) {
178-
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
179-
special := make(map[string]uint32)
180-
special["<bs>"] = 0xFF08
181-
special["<del>"] = 0xFFFF
182-
special["<enter>"] = 0xFF0D
183-
special["<esc>"] = 0xFF1B
184-
special["<f1>"] = 0xFFBE
185-
special["<f2>"] = 0xFFBF
186-
special["<f3>"] = 0xFFC0
187-
special["<f4>"] = 0xFFC1
188-
special["<f5>"] = 0xFFC2
189-
special["<f6>"] = 0xFFC3
190-
special["<f7>"] = 0xFFC4
191-
special["<f8>"] = 0xFFC5
192-
special["<f9>"] = 0xFFC6
193-
special["<f10>"] = 0xFFC7
194-
special["<f11>"] = 0xFFC8
195-
special["<f12>"] = 0xFFC9
196-
special["<return>"] = 0xFF0D
197-
special["<tab>"] = 0xFF09
198-
special["<up>"] = 0xFF52
199-
special["<down>"] = 0xFF54
200-
special["<left>"] = 0xFF51
201-
special["<right>"] = 0xFF53
202-
special["<spacebar>"] = 0x020
203-
special["<insert>"] = 0xFF63
204-
special["<home>"] = 0xFF50
205-
special["<end>"] = 0xFF57
206-
special["<pageUp>"] = 0xFF55
207-
special["<pageDown>"] = 0xFF56
208-
209-
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
210-
211-
// TODO(mitchellh): Ripe for optimizations of some point, perhaps.
212-
for len(original) > 0 {
213-
var keyCode uint32
214-
keyShift := false
215-
216-
if strings.HasPrefix(original, "<wait>") {
217-
log.Printf("Special code '<wait>' found, sleeping one second")
218-
time.Sleep(1 * time.Second)
219-
original = original[len("<wait>"):]
220-
continue
221-
}
222-
223-
if strings.HasPrefix(original, "<wait5>") {
224-
log.Printf("Special code '<wait5>' found, sleeping 5 seconds")
225-
time.Sleep(5 * time.Second)
226-
original = original[len("<wait5>"):]
227-
continue
228-
}
229-
230-
if strings.HasPrefix(original, "<wait10>") {
231-
log.Printf("Special code '<wait10>' found, sleeping 10 seconds")
232-
time.Sleep(10 * time.Second)
233-
original = original[len("<wait10>"):]
234-
continue
235-
}
236-
237-
for specialCode, specialValue := range special {
238-
if strings.HasPrefix(original, specialCode) {
239-
log.Printf("Special code '%s' found, replacing with: %d", specialCode, specialValue)
240-
keyCode = specialValue
241-
original = original[len(specialCode):]
242-
break
243-
}
244-
}
245-
246-
if keyCode == 0 {
247-
r, size := utf8.DecodeRuneInString(original)
248-
original = original[size:]
249-
keyCode = uint32(r)
250-
keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
251-
252-
log.Printf("Sending char '%c', code %d, shift %v", r, keyCode, keyShift)
253-
}
254-
255-
if keyShift {
256-
c.KeyEvent(uint32(KeyLeftShift), true)
257-
}
258-
259-
c.KeyEvent(keyCode, true)
260-
time.Sleep(time.Second / 10)
261-
c.KeyEvent(keyCode, false)
262-
time.Sleep(time.Second / 10)
263-
264-
if keyShift {
265-
c.KeyEvent(uint32(KeyLeftShift), false)
266-
}
267-
268-
// no matter what, wait a small period
269-
time.Sleep(50 * time.Millisecond)
270-
}
271-
}

docs/builders/iso/xenserver-iso.html.markdown

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,9 +299,30 @@ will be replaced by the proper key:
299299

300300
* `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
301301

302+
* `<leftAlt> <rightAlt>` - Simulates pressing the alt key.
303+
304+
* `<leftCtrl> <rightCtrl>` - Simulates pressing the ctrl key.
305+
306+
* `<leftShift> <rightShift>` - Simulates pressing the shift key.
307+
308+
* `<leftSuper> <rightSuper>` - Simulates pressing the ⌘ or Windows key.
309+
302310
* `<wait>` `<wait5>` `<wait10>` - Adds a 1, 5 or 10 second pause before sending any additional keys. This
303311
is useful if you have to generally wait for the UI to update before typing more.
304312

313+
* `<waitXX>` - Add an arbitrary pause before sending any additional keys.
314+
The format of `XX` is a sequence of positive decimal numbers, each with
315+
optional fraction and a unit suffix, such as `300ms`, `1.5h` or `2h45m`.
316+
Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`. For
317+
example `<wait10m>` or `<wait1m20s>`.
318+
319+
* `<XXXOn> <XXXOff>` - Any printable keyboard character, and of these
320+
"special" expressions, with the exception of the `<wait>` types, can
321+
also be toggled on or off. For example, to simulate ctrl+c, use
322+
`<leftCtrlOn>c<leftCtrlOff>`. Be sure to release them, otherwise they
323+
will be held down until the machine reboots. To hold the `c` key down,
324+
you would use `<cOn>`. Likewise, `<cOff>` to release.
325+
305326
In addition to the special keys, each command to type is treated as a
306327
configuration template.
307328

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ require (
8181
github.com/ugorji/go/codec v1.2.6 // indirect
8282
github.com/ulikunitz/xz v0.5.10 // indirect
8383
go.opencensus.io v0.24.0 // indirect
84+
golang.org/x/mobile v0.0.0-20210901025245-1fde1d6c3ca1 // indirect
8485
golang.org/x/mod v0.17.0 // indirect
8586
golang.org/x/net v0.25.0 // indirect
8687
golang.org/x/oauth2 v0.7.0 // indirect

go.sum

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW
470470
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
471471
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
472472
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
473+
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
473474
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI=
474475
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
475476
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@@ -496,6 +497,7 @@ golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5D
496497
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
497498
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
498499
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
500+
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
499501
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
500502
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
501503
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
@@ -516,11 +518,14 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
516518
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
517519
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
518520
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
521+
golang.org/x/mobile v0.0.0-20210901025245-1fde1d6c3ca1 h1:t3ZHqovedSY8DEAUmZA99fPJhUhOb176PLACYA1sJ8Y=
522+
golang.org/x/mobile v0.0.0-20210901025245-1fde1d6c3ca1/go.mod h1:jFTmtFYCV0MFtXBU+J5V/+5AUeVS0ON/0WkE/KSrl6E=
519523
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
520524
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
521525
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
522526
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
523527
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
528+
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
524529
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
525530
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
526531
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -549,6 +554,7 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL
549554
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
550555
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
551556
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
557+
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
552558
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
553559
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
554560
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
@@ -565,6 +571,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
565571
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
566572
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
567573
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
574+
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
568575
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
569576
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
570577
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -605,8 +612,10 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
605612
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
606613
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
607614
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
615+
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
608616
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
609617
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
618+
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
610619
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
611620
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
612621
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -660,11 +669,13 @@ golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapK
660669
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
661670
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
662671
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
672+
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
663673
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
664674
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
665675
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
666676
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
667677
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
678+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
668679
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
669680
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
670681
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=

0 commit comments

Comments
 (0)