File: step_set_first_boot_device.go

package info (click to toggle)
packer 1.6.6%2Bds2-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 33,156 kB
  • sloc: sh: 1,154; python: 619; makefile: 251; ruby: 205; xml: 97
file content (160 lines) | stat: -rw-r--r-- 4,147 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package common

import (
	"context"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"github.com/hashicorp/packer/packer-plugin-sdk/multistep"
	packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer"
)

type StepSetFirstBootDevice struct {
	Generation      uint
	FirstBootDevice string
}

func ParseBootDeviceIdentifier(deviceIdentifier string, generation uint) (string, uint, uint, error) {

	// all input strings are forced to upperCase for comparison, I believe this is
	// safe as all of our values are 7bit ASCII clean.

	lookupDeviceIdentifier := strings.ToUpper(deviceIdentifier)

	if generation == 1 {

		// Gen1 values are a simple set of if/then/else values, which we coalesce into a map
		// here for simplicity

		lookupTable := map[string]string{
			"FLOPPY": "FLOPPY",
			"IDE":    "IDE",
			"NET":    "NET",
			"CD":     "CD",
			"DVD":    "CD",
		}

		controllerType, isDefined := lookupTable[lookupDeviceIdentifier]
		if !isDefined {

			return "", 0, 0, fmt.Errorf("The value %q is not a properly formatted device group identifier.", deviceIdentifier)

		}

		// success
		return controllerType, 0, 0, nil
	}

	// everything else is treated as generation 2... the first set of lookups covers
	// the simple options..

	lookupTable := map[string]string{
		"CD":  "CD",
		"DVD": "CD",
		"NET": "NET",
	}

	controllerType, isDefined := lookupTable[lookupDeviceIdentifier]
	if isDefined {

		// these types do not require controllerNumber or controllerLocation
		return controllerType, 0, 0, nil

	}

	// not a simple option, check for a controllerType:controllerNumber:controllerLocation formatted
	// device..

	r, err := regexp.Compile(`^(IDE|SCSI):(\d+):(\d+)$`)
	if err != nil {
		return "", 0, 0, err
	}

	controllerMatch := r.FindStringSubmatch(lookupDeviceIdentifier)
	if controllerMatch != nil {

		var controllerLocation int64
		var controllerNumber int64

		// NOTE: controllerNumber and controllerLocation cannot be negative, the regex expression
		// would not have matched if either number was signed

		controllerNumber, err = strconv.ParseInt(controllerMatch[2], 10, 8)
		if err == nil {

			controllerLocation, err = strconv.ParseInt(controllerMatch[3], 10, 8)
			if err == nil {

				return controllerMatch[1], uint(controllerNumber), uint(controllerLocation), nil

			}

		}

		return "", 0, 0, err

	}

	return "", 0, 0, fmt.Errorf("The value %q is not a properly formatted device identifier.", deviceIdentifier)
}

func (s *StepSetFirstBootDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {

	driver := state.Get("driver").(Driver)
	ui := state.Get("ui").(packersdk.Ui)
	vmName := state.Get("vmName").(string)

	if s.FirstBootDevice != "" {

		controllerType, controllerNumber, controllerLocation, err := ParseBootDeviceIdentifier(s.FirstBootDevice, s.Generation)
		if err == nil {

			switch {

			case controllerType == "CD":
				{
					// the "DVD" controller is special, we only apply the setting if we actually mounted
					// an ISO and only if that was mounted as the "IsoUrl" not a secondary ISO.

					dvdControllerState := state.Get("os.dvd.properties")
					if dvdControllerState == nil {

						ui.Say("First Boot Device is DVD, but no primary ISO mounted. Ignoring.")
						return multistep.ActionContinue

					}

					ui.Say(fmt.Sprintf("Setting boot device to %q", s.FirstBootDevice))
					dvdController := dvdControllerState.(DvdControllerProperties)
					err = driver.SetFirstBootDevice(vmName, controllerType, dvdController.ControllerNumber, dvdController.ControllerLocation, s.Generation)

				}

			default:
				{
					// anything else, we just pass as is..
					ui.Say(fmt.Sprintf("Setting boot device to %q", s.FirstBootDevice))
					err = driver.SetFirstBootDevice(vmName, controllerType, controllerNumber, controllerLocation, s.Generation)
				}
			}

		}

		if err != nil {
			err := fmt.Errorf("Error setting first boot device: %s", err)
			state.Put("error", err)
			ui.Error(err.Error())
			return multistep.ActionHalt

		}

	}

	return multistep.ActionContinue
}

func (s *StepSetFirstBootDevice) Cleanup(state multistep.StateBag) {
	// do nothing
}