generated from hashicorp/packer-plugin-scaffolding
/
step_configure_vmx.go
227 lines (198 loc) · 7.17 KB
/
step_configure_vmx.go
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package common
import (
"context"
"fmt"
"log"
"regexp"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
// This step configures a VMX by setting some default settings as well
// as taking in custom data to set, attaching a floppy if it exists, etc.
//
// Uses: vmx_path string
// Produces: display_name string - Value of the displayName key set in the VMX file
type StepConfigureVMX struct {
CustomData map[string]string
DisplayName string
SkipDevices bool
VMName string
DiskAdapterType string
CDROMAdapterType string
}
func (s *StepConfigureVMX) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
log.Printf("Configuring VMX...\n")
var err error
ui := state.Get("ui").(packersdk.Ui)
vmxPath := state.Get("vmx_path").(string)
vmxData, err := ReadVMX(vmxPath)
if err != nil {
err := fmt.Errorf("Error reading VMX file: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Set this so that no dialogs ever appear from Packer.
vmxData["msg.autoanswer"] = "true"
// Create a new UUID for this VM, since it is a new VM
vmxData["uuid.action"] = "create"
// Delete any generated addresses since we want to regenerate
// them. Conflicting MAC addresses is a bad time.
addrRegex := regexp.MustCompile(`(?i)^ethernet\d+\.generatedAddress`)
for k := range vmxData {
if addrRegex.MatchString(k) {
delete(vmxData, k)
}
}
// Set custom data
for k, v := range s.CustomData {
log.Printf("Setting VMX: '%s' = '%s'", k, v)
k = strings.ToLower(k)
vmxData[k] = v
}
// StepConfigureVMX runs both before and after provisioning (for VmxDataPost),
// the latter time shouldn't create temporary devices
if !s.SkipDevices {
// Grab list of temporary builder devices so we can append to it
tmpBuildDevices := state.Get("temporaryDevices").([]string)
// Set a floppy disk if we have one
if floppyPathRaw, ok := state.GetOk("floppy_path"); ok {
log.Println("Floppy path present, setting in VMX")
vmxData["floppy0.present"] = "TRUE"
vmxData["floppy0.filetype"] = "file"
vmxData["floppy0.filename"] = floppyPathRaw.(string)
// Add it to our list of build devices to later remove
tmpBuildDevices = append(tmpBuildDevices, "floppy0")
}
// Add our custom CD, if it exists
if cdPath, ok := state.GetOk("cd_path"); ok {
if cdPath != "" {
diskAndCDConfigData := DefaultDiskAndCDROMTypes(s.DiskAdapterType, s.CDROMAdapterType)
cdromPrefix := diskAndCDConfigData.CDROMType + "1:" + diskAndCDConfigData.CDROMType_PrimarySecondary
vmxData[cdromPrefix+".present"] = "TRUE"
vmxData[cdromPrefix+".filename"] = cdPath.(string)
vmxData[cdromPrefix+".devicetype"] = "cdrom-image"
// Add it to our list of build devices to later remove
tmpBuildDevices = append(tmpBuildDevices, cdromPrefix)
}
}
// Build the list back in our statebag
state.Put("temporaryDevices", tmpBuildDevices)
}
// If the build is taking place on a remote ESX server, the displayName
// will be needed for discovery of the VM's IP address and for export
// of the VM. The displayName key should always be set in the VMX file,
// so error if we don't find it and the user has not set it in the config.
if s.DisplayName != "" {
vmxData["displayname"] = s.DisplayName
state.Put("display_name", s.DisplayName)
} else {
displayName, ok := vmxData["displayname"]
if !ok { // Packer converts key names to lowercase!
err := fmt.Errorf("Error: Could not get value of displayName from VMX data")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
} else {
state.Put("display_name", displayName)
}
}
// Set the extendedConfigFile setting for the .vmxf filename to the VMName
// if displayName is not set. This is needed so that when VMWare creates
// the .vmxf file it matches the displayName if it is set. When just using
// the sisplayName if it was empty VMWare would make a file named ".vmxf".
// The ".vmxf" file would not get deleted when the VM got deleted.
if s.DisplayName != "" {
vmxData["extendedconfigfile"] = fmt.Sprintf("%s.vmxf", s.DisplayName)
} else {
vmxData["extendedconfigfile"] = fmt.Sprintf("%s.vmxf", s.VMName)
}
err = WriteVMX(vmxPath, vmxData)
if err != nil {
err := fmt.Errorf("Error writing VMX file: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
type DiskAndCDConfigData struct {
SCSI_Present string
SCSI_diskAdapterType string
SATA_Present string
NVME_Present string
DiskType string
CDROMType string
CDROMType_PrimarySecondary string
CDROM_PATH string
}
// DefaultDiskAndCDROMTypes takes the disk adapter type and cdrom adapter type from the config and converts them
// into template interpolation data for creating or configuring a vmx.
func DefaultDiskAndCDROMTypes(diskAdapterType string, cdromAdapterType string) DiskAndCDConfigData {
diskData := DiskAndCDConfigData{
SCSI_Present: "FALSE",
SCSI_diskAdapterType: "lsilogic",
SATA_Present: "FALSE",
NVME_Present: "FALSE",
DiskType: "scsi",
CDROMType: "ide",
CDROMType_PrimarySecondary: "0",
}
/// Use the disk adapter type that the user specified to tweak the .vmx
// Also sync the cdrom adapter type according to what's common for that disk type.
// XXX: If the cdrom type is modified, make sure to update common/step_clean_vmx.go
// so that it will regex the correct cdrom device for removal.
diskAdapterType = strings.ToLower(diskAdapterType)
switch diskAdapterType {
case "ide":
diskData.DiskType = "ide"
diskData.CDROMType = "ide"
diskData.CDROMType_PrimarySecondary = "1"
case "sata":
diskData.SATA_Present = "TRUE"
diskData.DiskType = "sata"
diskData.CDROMType = "sata"
diskData.CDROMType_PrimarySecondary = "1"
case "nvme":
diskData.NVME_Present = "TRUE"
diskData.DiskType = "nvme"
diskData.SATA_Present = "TRUE"
diskData.CDROMType = "sata"
diskData.CDROMType_PrimarySecondary = "0"
case "scsi":
diskAdapterType = "lsilogic"
fallthrough
default:
diskData.SCSI_Present = "TRUE"
diskData.SCSI_diskAdapterType = diskAdapterType // defaults to lsilogic
diskData.DiskType = "scsi"
diskData.CDROMType = "ide"
diskData.CDROMType_PrimarySecondary = "0"
}
/// Handle the cdrom adapter type. If the disk adapter type and the
// cdrom adapter type are the same, then ensure that the cdrom is the
// secondary device on whatever bus the disk adapter is on.
if cdromAdapterType == "" {
cdromAdapterType = diskData.CDROMType
} else if cdromAdapterType == diskAdapterType {
diskData.CDROMType_PrimarySecondary = "1"
} else {
diskData.CDROMType_PrimarySecondary = "0"
}
switch cdromAdapterType {
case "ide":
diskData.CDROMType = "ide"
case "sata":
diskData.SATA_Present = "TRUE"
diskData.CDROMType = "sata"
case "scsi":
diskData.SCSI_Present = "TRUE"
diskData.CDROMType = "scsi"
}
return diskData
}
func (s *StepConfigureVMX) Cleanup(state multistep.StateBag) {
}