diff --git a/builder/vmware/common/driver_workstation9.go b/builder/vmware/common/driver_workstation9.go index 7a75bdb8..d834ec81 100644 --- a/builder/vmware/common/driver_workstation9.go +++ b/builder/vmware/common/driver_workstation9.go @@ -6,15 +6,52 @@ package common import ( "errors" "fmt" + "html/template" "log" "os" "os/exec" "path/filepath" + "runtime" + "sort" "strings" "github.com/hashicorp/packer-plugin-sdk/multistep" ) +// Template for the network mapper configuration file, 'netmap.conf'. +// This file is used to map network devices to their respective network names. +// This template is used to generate the file if if the default file does not +// exist on the system. +const netmapTemplate = ` +# This file is automatically generated. +# Hand-editing this file is not recommended. +network0.name = "Bridged" +network0.device = "vmnet0" +network1.name = "HostOnly" +network1.device = "vmnet1" +{{- range . }} +{{- if gt .Index 1 }} +{{- if lt .Index 8 }} +network{{ .Index }}.name = "VMNet{{ .Index }}" +network{{ .Index }}.device = "vmnet{{ .Index }}" +{{- else if eq .Index 8 }} +network8.name = "NAT" +network8.device = "vmnet8" +{{- else }} +network{{ .Index }}.name = "VMNet{{ .Index }}" +network{{ .Index }}.device = "vmnet{{ .Index }}" +{{- end }} +{{- end }} +{{- end }} +` + +// NetmapConfig is a struct that represents a network mapper configuration. +type NetmapConfig struct { + Index int + Name string + Device string +} + // Workstation9Driver is a driver that can run VMware Workstation 9 type Workstation9Driver struct { VmwareDriver @@ -173,33 +210,12 @@ func (d *Workstation9Driver) Verify() error { } d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) { - pathNetmap := workstationNetmapConfPath() - - // Check that the file for the networkmapper configuration exists. If there's no - // error, then the file exists and we can proceed to read the configuration out of it. - if _, err := os.Stat(pathNetmap); err == nil { - log.Printf("Located networkmapper configuration file: %s", pathNetmap) - return ReadNetmapConfig(pathNetmap) - } - - // If we weren't able to find the networkmapper configuration file, then fall back - // to the networking file which might also be in the configuration directory. - libpath, _ := workstationVMwareRoot() - pathNetworking := filepath.Join(libpath, "networking") - if _, err := os.Stat(pathNetworking); err != nil { - return nil, fmt.Errorf("error determining network mappings from files in path: %s", libpath) - } - - // We were able to successfully stat the file.. So, now we can open a handle to it. - log.Printf("Located networking configuration file: %s", pathNetworking) - fd, err := os.Open(pathNetworking) - if err != nil { - return nil, err + // Check if the network mapper configuration file exists. + mapper, err := checkNetmapConfExists() // Capture both the mapper and the error + if err != nil { // Check the error instead of the mapper being nil + return nil, fmt.Errorf("error reading network mapper configuration: %v", err) } - defer fd.Close() - - // Then we pass the handle to the networking configuration parser. - return ReadNetworkingConfig(fd) + return mapper, nil } return nil } @@ -215,3 +231,112 @@ func (d *Workstation9Driver) ToolsInstall() error { func (d *Workstation9Driver) GetVmwareDriver() VmwareDriver { return d.VmwareDriver } + +// checkNetmapConfExists checks if the network mapper configuration file exists. +// If not, if attempts to generate the file using generateNetmapConfig. +func checkNetmapConfExists() (NetworkNameMapper, error) { + pathNetmap := workstationNetmapConfPath() + + // Check if the default network mapper configuration file exists. + if _, err := os.Stat(pathNetmap); os.IsNotExist(err) { + log.Printf("A network mapper configuration file does not exist in the default path: %s", pathNetmap) + + // The file does not exist, check the alternate configuration path. + libpath, _ := workstationVMwareRoot() + pathNetworking := filepath.Join(libpath, "networking") + log.Printf("Checking alternate path for network mapper configuration file: %s", pathNetworking) + if _, err := os.Stat(pathNetworking); err == nil { + // Alternate networking configuration path exists. Using this path. + log.Printf("Located the network mapper configuration file: %s", pathNetworking) + fd, err := os.Open(pathNetworking) + if err != nil { + return nil, err + } + defer fd.Close() + + // Then we pass the handle to the networking configuration parser. + return ReadNetworkingConfig(fd) + } else if runtime.GOOS == "windows" { + // Neither the default nor alternate exists. + // Generate the network mapper configuration file in the default path. + log.Printf("Generating a network mapper configuration file in the default path: %s", pathNetmap) + pathNetmap, err := generateNetmapConfig() + if err != nil { + log.Fatalf("error generating the absent network configuration file: %v", err) + return nil, err + } + // If the file exists, attempt to read the newly generated + // network mapper configuration file. + return ReadNetmapConfig(pathNetmap) + } else { + // For non-Windows, if neither the default nor alternate + // configuration paths exist, return an error. + return nil, fmt.Errorf("error determining network mappings from files in path: %s", libpath) + } + } else if err != nil { + // There was an error other than the file not existing. + return nil, err + } + + // If the default network mapper configuration file exists, read the configuration. + log.Printf("Located the network mapper configuration file: %s", pathNetmap) + return ReadNetmapConfig(pathNetmap) +} + +// generateNetmapConfig creates the network mapper configuration file in the +// default path using the netmapTemplate and returns the path of the created file. +func generateNetmapConfig() (string, error) { + var paths []string + if os.Getenv("ProgramData") != "" { + paths = append(paths, filepath.Join(os.Getenv("ProgramData"), "VMware")) + } + + networks := []NetmapConfig{ + {0, "Bridged", "vmnet0"}, + {1, "HostOnly", "vmnet1"}, + {8, "NAT", "vmnet8"}, + } + for i := 2; i <= 19; i++ { + if i != 8 { + networks = append(networks, NetmapConfig{i, fmt.Sprintf("VMNet%d", i), fmt.Sprintf("vmnet%d", i)}) + } + } + + sort.Slice(networks, func(i, j int) bool { + return networks[i].Index < networks[j].Index + }) + + t, err := template.New("netmap").Parse(netmapTemplate) + if err != nil { + return "", err + } + + var sb strings.Builder + if err := t.Execute(&sb, networks); err != nil { + return "", err + } + netmapContent := []byte(sb.String()) + + var pathNetmap string + for _, basePath := range paths { + path := filepath.Join(basePath, "netmap.conf") + if err := os.MkdirAll(basePath, 0755); err != nil { + continue // Skip to the next path on error. + } + if err := os.WriteFile(path, netmapContent, 0644); err != nil { + continue // Skip to the next path on error. + } + + // If the file exists, set pathNetmap and break the loop. + if _, err := os.Stat(path); err == nil { + pathNetmap = path + break // Exit the loop since the file was successfully created. + } + } + + if pathNetmap != "" { + return pathNetmap, nil + } else { + return "", fmt.Errorf("no valid path found for generating the network mapper configuration file") + } +}