Skip to content

Commit 3ece6db

Browse files
author
kp2099
authored
Merge pull request #386 from hashicorp/feat/attach-tools-iso
feat: attach tools iso
2 parents 2337e44 + 37e883d commit 3ece6db

22 files changed

Lines changed: 2393 additions & 118 deletions

.web-docs/components/builder/iso/README.md

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -275,21 +275,38 @@ JSON Example:
275275

276276
<!-- Code generated from the comments of the ToolsConfig struct in builder/vmware/common/tools_config.go; DO NOT EDIT MANUALLY -->
277277

278-
- `tools_upload_flavor` (string) - The flavor of VMware Tools to upload into the virtual machine based on
279-
the guest operating system. Allowed values are `darwin` (macOS), `linux`,
280-
and `windows`. Default is empty and no version will be uploaded.
281-
282-
- `tools_upload_path` (string) - The path in the VM to upload the VMware tools. This only takes effect if
283-
`tools_upload_flavor` is non-empty. This is a [configuration
284-
template](/packer/docs/templates/legacy_json_templates/engine) that has a
285-
single valid variable: `Flavor`, which will be the value of
286-
`tools_upload_flavor`. By default, the upload path is set to
287-
`{{.Flavor}}.iso`.
288-
289-
- `tools_source_path` (string) - The local path on your machine to the VMware Tools ISO file.
290-
291-
~> **Note:** If not set, but the `tools_upload_flavor` is set, the plugin
292-
will load the VMware Tools from the product installation directory.
278+
- `tools_mode` (string) - The mode for providing VMware Tools to the virtual machine. Must be
279+
explicitly specified when using any tools configuration. Allowed values are:
280+
- `upload`: Uploads VMware Tools ISO to the virtual machine during the build.
281+
Requires either `tools_upload_flavor` or `tools_source_path` to be specified.
282+
- `attach`: Attaches the VMware Tools ISO to the virtual machine as a CD-ROM
283+
device during the build and removes the device upon build completion.
284+
Requires `tools_source_path` to be specified.
285+
- `disable`: No VMware Tools ISO is provided to the virtual machine.
286+
Any other tools configuration fields are ignored.
287+
288+
- `tools_source_path` (string) - The absolute local path on your machine to the VMware Tools ISO file.
289+
Can be used with `tools_mode` set to `attach` or `upload`. When used with
290+
`upload` mode, cannot be used together with `tools_upload_flavor`.
291+
292+
Must be a path accessible during the build (e.g., "/path/to/vmware-tools.iso".)
293+
294+
- `tools_upload_flavor` (string) - The flavor of VMware Tools to upload into the virtual machine based on the
295+
guest operating system. Can only be used when `tools_mode` is set to
296+
`upload`. Cannot be used together with `tools_source_path`. Allowed
297+
values include: `darwin` (macOS), `linux`, and `windows`.
298+
299+
The plugin will load the VMware Tools ISO from the desktop hypervisor's
300+
default installation directory based on the specified flavor, if available.
301+
302+
- `tools_upload_path` (string) - The absolute path in the virtual machine guest operating system where the
303+
VMware Tools ISO will be uploaded. Only used when `tools_mode` is set to
304+
`upload`. This is a [configuration template](/packer/docs/templates/legacy_json_templates/engine)
305+
that has a single valid variable: `Flavor`, which will be the value of
306+
`tools_upload_flavor`. Defaults to `{{.Flavor}}.iso` when
307+
`tools_upload_flavor` is specified.
308+
309+
Must be an absolute path in the guest operating system (e.g., "/tmp/vmware-tools.iso").
293310

294311
<!-- End of code generated from the comments of the ToolsConfig struct in builder/vmware/common/tools_config.go; -->
295312

.web-docs/components/builder/vmx/README.md

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -141,21 +141,38 @@ JSON Example:
141141

142142
<!-- Code generated from the comments of the ToolsConfig struct in builder/vmware/common/tools_config.go; DO NOT EDIT MANUALLY -->
143143

144-
- `tools_upload_flavor` (string) - The flavor of VMware Tools to upload into the virtual machine based on
145-
the guest operating system. Allowed values are `darwin` (macOS), `linux`,
146-
and `windows`. Default is empty and no version will be uploaded.
147-
148-
- `tools_upload_path` (string) - The path in the VM to upload the VMware tools. This only takes effect if
149-
`tools_upload_flavor` is non-empty. This is a [configuration
150-
template](/packer/docs/templates/legacy_json_templates/engine) that has a
151-
single valid variable: `Flavor`, which will be the value of
152-
`tools_upload_flavor`. By default, the upload path is set to
153-
`{{.Flavor}}.iso`.
144+
- `tools_mode` (string) - The mode for providing VMware Tools to the virtual machine. Must be
145+
explicitly specified when using any tools configuration. Allowed values are:
146+
- `upload`: Uploads VMware Tools ISO to the virtual machine during the build.
147+
Requires either `tools_upload_flavor` or `tools_source_path` to be specified.
148+
- `attach`: Attaches the VMware Tools ISO to the virtual machine as a CD-ROM
149+
device during the build and removes the device upon build completion.
150+
Requires `tools_source_path` to be specified.
151+
- `disable`: No VMware Tools ISO is provided to the virtual machine.
152+
Any other tools configuration fields are ignored.
153+
154+
- `tools_source_path` (string) - The absolute local path on your machine to the VMware Tools ISO file.
155+
Can be used with `tools_mode` set to `attach` or `upload`. When used with
156+
`upload` mode, cannot be used together with `tools_upload_flavor`.
157+
158+
Must be a path accessible during the build (e.g., "/path/to/vmware-tools.iso".)
154159

155-
- `tools_source_path` (string) - The local path on your machine to the VMware Tools ISO file.
160+
- `tools_upload_flavor` (string) - The flavor of VMware Tools to upload into the virtual machine based on the
161+
guest operating system. Can only be used when `tools_mode` is set to
162+
`upload`. Cannot be used together with `tools_source_path`. Allowed
163+
values include: `darwin` (macOS), `linux`, and `windows`.
164+
165+
The plugin will load the VMware Tools ISO from the desktop hypervisor's
166+
default installation directory based on the specified flavor, if available.
167+
168+
- `tools_upload_path` (string) - The absolute path in the virtual machine guest operating system where the
169+
VMware Tools ISO will be uploaded. Only used when `tools_mode` is set to
170+
`upload`. This is a [configuration template](/packer/docs/templates/legacy_json_templates/engine)
171+
that has a single valid variable: `Flavor`, which will be the value of
172+
`tools_upload_flavor`. Defaults to `{{.Flavor}}.iso` when
173+
`tools_upload_flavor` is specified.
156174

157-
~> **Note:** If not set, but the `tools_upload_flavor` is set, the plugin
158-
will load the VMware Tools from the product installation directory.
175+
Must be an absolute path in the guest operating system (e.g., "/tmp/vmware-tools.iso").
159176

160177
<!-- End of code generated from the comments of the ToolsConfig struct in builder/vmware/common/tools_config.go; -->
161178

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package common
5+
6+
import (
7+
"fmt"
8+
"regexp"
9+
"strconv"
10+
"strings"
11+
)
12+
13+
// FindNextAvailableCDROMSlot locates the next available CD-ROM device slot for
14+
// the specified adapter type.
15+
func FindNextAvailableCDROMSlot(vmxData map[string]string, adapterType string) (string, error) {
16+
if adapterType == "" {
17+
return "", fmt.Errorf("adapter type cannot be empty")
18+
}
19+
20+
adapterType = strings.ToLower(adapterType)
21+
22+
validAdapters := []string{"ide", "sata", "scsi"}
23+
isValid := false
24+
for _, valid := range validAdapters {
25+
if adapterType == valid {
26+
isValid = true
27+
break
28+
}
29+
}
30+
if !isValid {
31+
return "", fmt.Errorf("invalid adapter type: %s; must be one of %v", adapterType, validAdapters)
32+
}
33+
34+
devicePattern := regexp.MustCompile(fmt.Sprintf(`^%s(\d+):(\d+)\.present$`, adapterType))
35+
usedSlots := make(map[string]map[int]bool) // bus -> slot -> used
36+
37+
for key := range vmxData {
38+
if matches := devicePattern.FindStringSubmatch(key); matches != nil {
39+
bus := matches[1]
40+
slot, err := strconv.Atoi(matches[2])
41+
if err != nil {
42+
continue
43+
}
44+
45+
if usedSlots[bus] == nil {
46+
usedSlots[bus] = make(map[int]bool)
47+
}
48+
usedSlots[bus][slot] = true
49+
}
50+
}
51+
52+
// Find the next available slot.
53+
for busNum := 0; busNum < 4; busNum++ { // VMware supports up to 4 buses for most adapter types
54+
busStr := strconv.Itoa(busNum)
55+
busSlots := usedSlots[busStr]
56+
57+
// Check slots 0-15 (or 0-6,8-15 for SCSI to skip reserved slot 7).
58+
maxSlots := 16
59+
if adapterType == "ide" {
60+
maxSlots = 2 // IDE typically supports only 2 devices per bus.
61+
}
62+
63+
for slot := 0; slot < maxSlots; slot++ {
64+
// Skip reserved slot 7 for SCSI adapters.
65+
if adapterType == "scsi" && slot == 7 {
66+
continue
67+
}
68+
69+
if busSlots == nil || !busSlots[slot] {
70+
return fmt.Sprintf("%s%d:%d", adapterType, busNum, slot), nil
71+
}
72+
}
73+
}
74+
75+
return "", fmt.Errorf("no available CD-ROM slots found for adapter type %s", adapterType)
76+
}
77+
78+
// AttachCDROMDevice adds CD-ROM device entries to VMX data for the specified
79+
// device path and ISO file.
80+
func AttachCDROMDevice(vmxData map[string]string, devicePath, isoPath, adapterType string) error {
81+
if vmxData == nil {
82+
return fmt.Errorf("vmxData cannot be nil")
83+
}
84+
if devicePath == "" {
85+
return fmt.Errorf("devicePath cannot be empty")
86+
}
87+
if isoPath == "" {
88+
return fmt.Errorf("isoPath cannot be empty")
89+
}
90+
if adapterType == "" {
91+
return fmt.Errorf("adapterType cannot be empty")
92+
}
93+
94+
adapterType = strings.ToLower(adapterType)
95+
devicePattern := regexp.MustCompile(`^(ide|sata|scsi)(\d+):(\d+)$`)
96+
matches := devicePattern.FindStringSubmatch(devicePath)
97+
if matches == nil {
98+
return fmt.Errorf("invalid device path format: %s; expected format like 'ide0:1'", devicePath)
99+
}
100+
101+
pathAdapterType := matches[1]
102+
busNum := matches[2]
103+
104+
if pathAdapterType != adapterType {
105+
return fmt.Errorf("device path adapter type %s does not match specified adapter type %s", pathAdapterType, adapterType)
106+
}
107+
108+
presentKey := fmt.Sprintf("%s.present", devicePath)
109+
if existing, exists := vmxData[presentKey]; exists && strings.ToLower(existing) == "true" {
110+
return fmt.Errorf("device %s is already in use", devicePath)
111+
}
112+
113+
adapterKey := fmt.Sprintf("%s%s.present", adapterType, busNum)
114+
vmxData[adapterKey] = "TRUE"
115+
116+
vmxData[fmt.Sprintf("%s.present", devicePath)] = "TRUE"
117+
vmxData[fmt.Sprintf("%s.filename", devicePath)] = isoPath
118+
vmxData[fmt.Sprintf("%s.devicetype", devicePath)] = "cdrom-image"
119+
120+
return nil
121+
}
122+
123+
// DetachCDROMDevice removes CD-ROM device entries from VMX data for the
124+
// specified device path.
125+
func DetachCDROMDevice(vmxData map[string]string, devicePath string) error {
126+
if vmxData == nil {
127+
return fmt.Errorf("vmxData cannot be nil")
128+
}
129+
if devicePath == "" {
130+
return fmt.Errorf("devicePath cannot be empty")
131+
}
132+
133+
devicePattern := regexp.MustCompile(`^(ide|sata|scsi)(\d+):(\d+)$`)
134+
if !devicePattern.MatchString(devicePath) {
135+
return fmt.Errorf("invalid device path format: %s; expected format like 'ide0:1'", devicePath)
136+
}
137+
138+
devicePrefix := devicePath + "."
139+
keysToDelete := make([]string, 0)
140+
141+
for key := range vmxData {
142+
if strings.HasPrefix(key, devicePrefix) {
143+
keysToDelete = append(keysToDelete, key)
144+
}
145+
}
146+
147+
for _, key := range keysToDelete {
148+
delete(vmxData, key)
149+
}
150+
151+
return nil
152+
}

0 commit comments

Comments
 (0)