From f38fa0a99b559c43fbeefdbdc798d4bed57e1fe3 Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Thu, 27 Jun 2024 00:43:39 -0400 Subject: [PATCH] refactor: update `esx5` to `esxi` Updates references to "ESX5" to "ESXi". Sets `remote_type` of `esxi` as preferred and `esx5` as deprecated. Signed-off-by: Ryan Johnson --- .web-docs/components/builder/iso/README.md | 74 +++--- .web-docs/components/builder/vmx/README.md | 74 +++--- builder/vmware/common/driver.go | 4 +- builder/vmware/common/driver_config.go | 70 ++--- builder/vmware/common/driver_config_test.go | 6 +- .../common/{driver_esx5.go => driver_esxi.go} | 243 +++++++++--------- ...river_esx5_test.go => driver_esxi_test.go} | 32 +-- builder/vmware/common/output_dir.go | 2 +- builder/vmware/common/step_export.go | 2 +- builder/vmware/common/step_export_test.go | 2 +- builder/vmware/common/step_output_dir_test.go | 2 +- builder/vmware/common/step_prepare_tools.go | 5 +- .../vmware/common/step_prepare_tools_test.go | 4 +- builder/vmware/common/step_remote_upload.go | 6 +- builder/vmware/common/step_upload_tools.go | 2 +- builder/vmware/common/step_upload_vmx.go | 2 +- builder/vmware/common/step_vnc_connect.go | 2 +- builder/vmware/common/tools_config.go | 13 +- builder/vmware/iso/builder_test.go | 46 ++-- builder/vmware/iso/config.go | 4 +- builder/vmware/vmx/config.go | 2 +- builder/vmware/vmx/config_test.go | 26 +- .../common/DriverConfig-not-required.mdx | 61 +++-- .../common/ToolsConfig-not-required.mdx | 13 +- 24 files changed, 365 insertions(+), 332 deletions(-) rename builder/vmware/common/{driver_esx5.go => driver_esxi.go} (72%) rename builder/vmware/common/{driver_esx5_test.go => driver_esxi_test.go} (76%) diff --git a/.web-docs/components/builder/iso/README.md b/.web-docs/components/builder/iso/README.md index 1a514934..0a8f291d 100644 --- a/.web-docs/components/builder/iso/README.md +++ b/.web-docs/components/builder/iso/README.md @@ -282,13 +282,14 @@ JSON Example: the guest operating system. Allowed values are `darwin` (macOS), `linux`, and `windows`. Default is empty and no version will be uploaded. -- `tools_upload_path` (string) - The path in the virtual machine to upload the VMware Tools. This only - takes effect if `tools_upload_flavor` is non-empty. This is a - [configuration template](/packer/docs/templates/legacy_json_templates/engine) - that has a single valid variable, `Flavor`, which will be the value of - `tools_upload_flavor` when the upload path is set to `{{.Flavor}}.iso`. +- `tools_upload_path` (string) - The path in the VM to upload the VMware tools. This only takes effect if + `tools_upload_flavor` is non-empty. This is a [configuration + template](/packer/docs/templates/legacy_json_templates/engine) that has a + single valid variable: `Flavor`, which will be the value of + `tools_upload_flavor`. By default the upload path is set to + `{{.Flavor}}.iso`. - ~> **Note:** This setting is not used when `remote_type` is `esx5`. + ~> **Note:** This setting is not used when `remote_type` is `esxi` or `esx5`. - `tools_source_path` (string) - The local path on your machine to the VMware Tools ISO file. @@ -723,44 +724,51 @@ wget http://{{ .HTTPIP }}:{{ .HTTPPort }}/foo/bar/preseed.cfg -- `fusion_app_path` (string) - Path to "VMware Fusion.app". By default this is - /Applications/VMware Fusion.app but this setting allows you to - customize this. +- `fusion_app_path` (string) - The installation path of the VMware Fusion application. Defaults to + `/Applications/VMware Fusion.app` + + ~> **Note:** This is only required if you are using VMware Fusion as a + local desktop hypervisor and have installed it in a non-default location. -- `remote_type` (string) - The type of remote machine that will be used to - build this VM rather than a local desktop product. The only value accepted - for this currently is esx5. If this is not set, a desktop product will - be used. By default, this is not set. +- `remote_type` (string) - The type of remote hypervisor that will be used. If set, the remote + hypervisor will be used for the build. If not set, a local desktop + hypervisor (VMware Fusion or VMware Workstation) will be used. + Available options include `esxi` and `esx5` for VMware ESXi. + + ~> **Note:** Use of `esxi` is recommended; `esx5` is deprecated. -- `remote_datastore` (string) - The path to the datastore where the VM will be stored - on the ESXi machine. +- `remote_datastore` (string) - The datastore on the remote hypervisor where the virtual machine will be + stored. -- `remote_cache_datastore` (string) - The path to the datastore where supporting files - will be stored during the build on the remote machine. +- `remote_cache_datastore` (string) - The datastore attached to the remote hypervisor to use for the build. + Supporting files such as ISOs and floppies are cached in this datastore + during the build. Defaults to `datastore1`. -- `remote_cache_directory` (string) - The path where the ISO and/or floppy files will - be stored during the build on the remote machine. The path is relative to - the remote_cache_datastore on the remote machine. +- `remote_cache_directory` (string) - The directory path on the remote cache datastore to use for the build. + Supporting files such as ISOs and floppies are cached in this directory, + relative to the `remote_cache_datastore`, during the build. Defaults to + `packer_cache`. -- `cleanup_remote_cache` (bool) - When set to true, Packer will cleanup the cache folder where the ISO file is stored during the build on the remote machine. - By default, this is set to false. +- `cleanup_remote_cache` (bool) - Remove items added to the remote cache after the build is complete. + Defaults to `false`. -- `remote_host` (string) - The host of the remote machine used for access. - This is only required if remote_type is enabled. +- `remote_host` (string) - The fully qualified domain name or IP address of the remote hypervisor + where the virtual machine is created. + + ~> **Note:** Required if `remote_type` is set. -- `remote_port` (int) - The SSH port of the remote machine +- `remote_port` (int) - The SSH port of the remote hypervisor. Defaults to `22`. -- `remote_username` (string) - The SSH username used to access the remote machine. +- `remote_username` (string) - The SSH username for access to the remote hypervisor. Defaults to `root`. -- `remote_password` (string) - The SSH password for access to the remote machine. +- `remote_password` (string) - The SSH password for access to the remote hypervisor. -- `remote_private_key_file` (string) - The SSH key for access to the remote machine. +- `remote_private_key_file` (string) - The SSH key for access to the remote hypervisor. -- `skip_validate_credentials` (bool) - When Packer is preparing to run a - remote hypervisor build, and export is not disable, by default it runs a no-op - ovftool command to make sure that the remote_username and remote_password - given are valid. If you set this flag to true, Packer will skip this - validation. Default: false. +- `skip_validate_credentials` (bool) - Skip the validation of the credentials for access to the remote + hypervisor. By default, export is enabled and the plugin will validate + the credentials ('remote_username' and 'remote_password'), for use by + VMware OVF Tool, before starting the build. Defaults to `false`. diff --git a/.web-docs/components/builder/vmx/README.md b/.web-docs/components/builder/vmx/README.md index f52db8e5..c06af5d9 100644 --- a/.web-docs/components/builder/vmx/README.md +++ b/.web-docs/components/builder/vmx/README.md @@ -152,13 +152,14 @@ JSON Example: the guest operating system. Allowed values are `darwin` (macOS), `linux`, and `windows`. Default is empty and no version will be uploaded. -- `tools_upload_path` (string) - The path in the virtual machine to upload the VMware Tools. This only - takes effect if `tools_upload_flavor` is non-empty. This is a - [configuration template](/packer/docs/templates/legacy_json_templates/engine) - that has a single valid variable, `Flavor`, which will be the value of - `tools_upload_flavor` when the upload path is set to `{{.Flavor}}.iso`. +- `tools_upload_path` (string) - The path in the VM to upload the VMware tools. This only takes effect if + `tools_upload_flavor` is non-empty. This is a [configuration + template](/packer/docs/templates/legacy_json_templates/engine) that has a + single valid variable: `Flavor`, which will be the value of + `tools_upload_flavor`. By default the upload path is set to + `{{.Flavor}}.iso`. - ~> **Note:** This setting is not used when `remote_type` is `esx5`. + ~> **Note:** This setting is not used when `remote_type` is `esxi` or `esx5`. - `tools_source_path` (string) - The local path on your machine to the VMware Tools ISO file. @@ -495,44 +496,51 @@ wget http://{{ .HTTPIP }}:{{ .HTTPPort }}/foo/bar/preseed.cfg -- `fusion_app_path` (string) - Path to "VMware Fusion.app". By default this is - /Applications/VMware Fusion.app but this setting allows you to - customize this. +- `fusion_app_path` (string) - The installation path of the VMware Fusion application. Defaults to + `/Applications/VMware Fusion.app` + + ~> **Note:** This is only required if you are using VMware Fusion as a + local desktop hypervisor and have installed it in a non-default location. -- `remote_type` (string) - The type of remote machine that will be used to - build this VM rather than a local desktop product. The only value accepted - for this currently is esx5. If this is not set, a desktop product will - be used. By default, this is not set. +- `remote_type` (string) - The type of remote hypervisor that will be used. If set, the remote + hypervisor will be used for the build. If not set, a local desktop + hypervisor (VMware Fusion or VMware Workstation) will be used. + Available options include `esxi` and `esx5` for VMware ESXi. + + ~> **Note:** Use of `esxi` is recommended; `esx5` is deprecated. -- `remote_datastore` (string) - The path to the datastore where the VM will be stored - on the ESXi machine. +- `remote_datastore` (string) - The datastore on the remote hypervisor where the virtual machine will be + stored. -- `remote_cache_datastore` (string) - The path to the datastore where supporting files - will be stored during the build on the remote machine. +- `remote_cache_datastore` (string) - The datastore attached to the remote hypervisor to use for the build. + Supporting files such as ISOs and floppies are cached in this datastore + during the build. Defaults to `datastore1`. -- `remote_cache_directory` (string) - The path where the ISO and/or floppy files will - be stored during the build on the remote machine. The path is relative to - the remote_cache_datastore on the remote machine. +- `remote_cache_directory` (string) - The directory path on the remote cache datastore to use for the build. + Supporting files such as ISOs and floppies are cached in this directory, + relative to the `remote_cache_datastore`, during the build. Defaults to + `packer_cache`. -- `cleanup_remote_cache` (bool) - When set to true, Packer will cleanup the cache folder where the ISO file is stored during the build on the remote machine. - By default, this is set to false. +- `cleanup_remote_cache` (bool) - Remove items added to the remote cache after the build is complete. + Defaults to `false`. -- `remote_host` (string) - The host of the remote machine used for access. - This is only required if remote_type is enabled. +- `remote_host` (string) - The fully qualified domain name or IP address of the remote hypervisor + where the virtual machine is created. + + ~> **Note:** Required if `remote_type` is set. -- `remote_port` (int) - The SSH port of the remote machine +- `remote_port` (int) - The SSH port of the remote hypervisor. Defaults to `22`. -- `remote_username` (string) - The SSH username used to access the remote machine. +- `remote_username` (string) - The SSH username for access to the remote hypervisor. Defaults to `root`. -- `remote_password` (string) - The SSH password for access to the remote machine. +- `remote_password` (string) - The SSH password for access to the remote hypervisor. -- `remote_private_key_file` (string) - The SSH key for access to the remote machine. +- `remote_private_key_file` (string) - The SSH key for access to the remote hypervisor. -- `skip_validate_credentials` (bool) - When Packer is preparing to run a - remote hypervisor build, and export is not disable, by default it runs a no-op - ovftool command to make sure that the remote_username and remote_password - given are valid. If you set this flag to true, Packer will skip this - validation. Default: false. +- `skip_validate_credentials` (bool) - Skip the validation of the credentials for access to the remote + hypervisor. By default, export is enabled and the plugin will validate + the credentials ('remote_username' and 'remote_password'), for use by + VMware OVF Tool, before starting the build. Defaults to `false`. diff --git a/builder/vmware/common/driver.go b/builder/vmware/common/driver.go index 72083941..c26fea95 100644 --- a/builder/vmware/common/driver.go +++ b/builder/vmware/common/driver.go @@ -110,11 +110,11 @@ func NewDriver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Driver, var drivers []Driver if dconfig.RemoteType != "" { - esx5Driver, err := NewESX5Driver(dconfig, config, vmName) + esxiDriver, err := NewEsxiDriver(dconfig, config, vmName) if err != nil { return nil, err } - drivers = []Driver{esx5Driver} + drivers = []Driver{esxiDriver} } else { switch runtime.GOOS { diff --git a/builder/vmware/common/driver_config.go b/builder/vmware/common/driver_config.go index b56514c6..29353116 100644 --- a/builder/vmware/common/driver_config.go +++ b/builder/vmware/common/driver_config.go @@ -13,44 +13,51 @@ import ( ) type DriverConfig struct { - // Path to "VMware Fusion.app". By default this is - // /Applications/VMware Fusion.app but this setting allows you to - // customize this. + // The installation path of the VMware Fusion application. Defaults to + // `/Applications/VMware Fusion.app` + // + // ~> **Note:** This is only required if you are using VMware Fusion as a + // local desktop hypervisor and have installed it in a non-default location. FusionAppPath string `mapstructure:"fusion_app_path" required:"false"` - // The type of remote machine that will be used to - // build this VM rather than a local desktop product. The only value accepted - // for this currently is esx5. If this is not set, a desktop product will - // be used. By default, this is not set. + // The type of remote hypervisor that will be used. If set, the remote + // hypervisor will be used for the build. If not set, a local desktop + // hypervisor (VMware Fusion or VMware Workstation) will be used. + // Available options include `esxi` and `esx5` for VMware ESXi. + // + // ~> **Note:** Use of `esxi` is recommended; `esx5` is deprecated. RemoteType string `mapstructure:"remote_type" required:"false"` - // The path to the datastore where the VM will be stored - // on the ESXi machine. + // The datastore on the remote hypervisor where the virtual machine will be + // stored. RemoteDatastore string `mapstructure:"remote_datastore" required:"false"` - // The path to the datastore where supporting files - // will be stored during the build on the remote machine. + // The datastore attached to the remote hypervisor to use for the build. + // Supporting files such as ISOs and floppies are cached in this datastore + // during the build. Defaults to `datastore1`. RemoteCacheDatastore string `mapstructure:"remote_cache_datastore" required:"false"` - // The path where the ISO and/or floppy files will - // be stored during the build on the remote machine. The path is relative to - // the remote_cache_datastore on the remote machine. + // The directory path on the remote cache datastore to use for the build. + // Supporting files such as ISOs and floppies are cached in this directory, + // relative to the `remote_cache_datastore`, during the build. Defaults to + // `packer_cache`. RemoteCacheDirectory string `mapstructure:"remote_cache_directory" required:"false"` - // When set to true, Packer will cleanup the cache folder where the ISO file is stored during the build on the remote machine. - // By default, this is set to false. + // Remove items added to the remote cache after the build is complete. + // Defaults to `false`. CleanUpRemoteCache bool `mapstructure:"cleanup_remote_cache" required:"false"` - // The host of the remote machine used for access. - // This is only required if remote_type is enabled. + // The fully qualified domain name or IP address of the remote hypervisor + // where the virtual machine is created. + // + // ~> **Note:** Required if `remote_type` is set. RemoteHost string `mapstructure:"remote_host" required:"false"` - // The SSH port of the remote machine + // The SSH port of the remote hypervisor. Defaults to `22`. RemotePort int `mapstructure:"remote_port" required:"false"` - // The SSH username used to access the remote machine. + // The SSH username for access to the remote hypervisor. Defaults to `root`. RemoteUser string `mapstructure:"remote_username" required:"false"` - // The SSH password for access to the remote machine. + // The SSH password for access to the remote hypervisor. RemotePassword string `mapstructure:"remote_password" required:"false"` - // The SSH key for access to the remote machine. + // The SSH key for access to the remote hypervisor. RemotePrivateKey string `mapstructure:"remote_private_key_file" required:"false"` - // When Packer is preparing to run a - // remote hypervisor build, and export is not disable, by default it runs a no-op - // ovftool command to make sure that the remote_username and remote_password - // given are valid. If you set this flag to true, Packer will skip this - // validation. Default: false. + // Skip the validation of the credentials for access to the remote + // hypervisor. By default, export is enabled and the plugin will validate + // the credentials ('remote_username' and 'remote_password'), for use by + // VMware OVF Tool, before starting the build. Defaults to `false`. SkipValidateCredentials bool `mapstructure:"skip_validate_credentials" required:"false"` } @@ -82,12 +89,12 @@ func (c *DriverConfig) Prepare(ctx *interpolate.Context) []error { if c.RemoteType != "" { if c.RemoteHost == "" { errs = append(errs, - fmt.Errorf("remote_host must be specified")) + fmt.Errorf("'remote_host' must be specified")) } - if c.RemoteType != "esx5" { + if c.RemoteType != "esxi" && c.RemoteType != "esx5" { errs = append(errs, - fmt.Errorf("only 'esx5' value is accepted for remote_type")) + fmt.Errorf("only 'esxi' and 'esx5' values are accepted for 'remote_type'")) } } @@ -100,8 +107,7 @@ func (c *DriverConfig) Validate(SkipExport bool) error { } if c.RemoteType != "" && c.RemotePassword == "" { - return fmt.Errorf("exporting the vm from esxi with ovftool requires " + - "that you set a value for remote_password") + return fmt.Errorf("export requires 'remote_password' to be set") } return nil diff --git a/builder/vmware/common/driver_config_test.go b/builder/vmware/common/driver_config_test.go index bd9d98e0..3ebcf3f8 100644 --- a/builder/vmware/common/driver_config_test.go +++ b/builder/vmware/common/driver_config_test.go @@ -57,15 +57,15 @@ func TestDriverConfigPrepare(t *testing.T) { RemoteHost: "host", }, expectedConfig: nil, - errs: []error{fmt.Errorf("only 'esx5' value is accepted for remote_type")}, + errs: []error{fmt.Errorf("only 'esxi' and 'esx5' values are accepted for 'remote_type'")}, }, { name: "Remote host not set", config: &DriverConfig{ - RemoteType: "esx5", + RemoteType: "esxi", }, expectedConfig: nil, - errs: []error{fmt.Errorf("remote_host must be specified")}, + errs: []error{fmt.Errorf("'remote_host' must be specified")}, }, } diff --git a/builder/vmware/common/driver_esx5.go b/builder/vmware/common/driver_esxi.go similarity index 72% rename from builder/vmware/common/driver_esx5.go rename to builder/vmware/common/driver_esxi.go index 8cce8a73..bf37fc0b 100644 --- a/builder/vmware/common/driver_esx5.go +++ b/builder/vmware/common/driver_esxi.go @@ -39,9 +39,8 @@ import ( gossh "golang.org/x/crypto/ssh" ) -// ESX5 driver talks to an ESXi5 hypervisor remotely over SSH to build -// virtual machines. This driver can only manage one machine at a time. -type ESX5Driver struct { +// EsxiDriver is a driver for building virtual machines on an ESXi host. +type EsxiDriver struct { base VmwareDriver Host string @@ -64,7 +63,7 @@ type ESX5Driver struct { vmId string } -func NewESX5Driver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Driver, error) { +func NewEsxiDriver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Driver, error) { ctx := context.TODO() vsphereUrl, err := url.Parse(fmt.Sprintf("https://%v/sdk", dconfig.RemoteHost)) @@ -98,7 +97,7 @@ func NewESX5Driver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Dri } finder.SetDatacenter(datacenter) - return &ESX5Driver{ + return &EsxiDriver{ Host: dconfig.RemoteHost, Port: dconfig.RemotePort, Username: dconfig.RemoteUser, @@ -115,7 +114,7 @@ func NewESX5Driver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Dri }, nil } -func (d *ESX5Driver) Clone(dst, src string, linked bool, snapshot string) error { +func (d *EsxiDriver) Clone(dst, src string, linked bool, snapshot string) error { linesToArray := func(lines string) []string { return strings.Split(strings.Trim(lines, "\r\n"), "\n") } @@ -126,37 +125,37 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool, snapshot string) error dstDir := path.Dir(dstVmx) log.Printf("Source: %s\n", srcVmx) - log.Printf("Dest: %s\n", dstVmx) + log.Printf("Destination: %s\n", dstVmx) err := d.MkdirAll() if err != nil { - return fmt.Errorf("failed to create the destination directory %s: %s", d.outputDir, err) + return fmt.Errorf("ferror creatig the destination directory %s: %s", d.outputDir, err) } err = d.sh("cp", strconv.Quote(srcVmx), strconv.Quote(dstVmx)) if err != nil { - return fmt.Errorf("failed to copy the vmx file %s: %s", srcVmx, err) + return fmt.Errorf("error copying the .vmx file %s: %s", srcVmx, err) } filesToClone, err := d.run(nil, "find", strconv.Quote(srcDir), "! -name '*.vmdk' ! -name '*.vmx' ! -name '*.vmxf' -type f ! -size 0") if err != nil { - return fmt.Errorf("failed to get the file list to copy: %s", err) + return fmt.Errorf("error returning the file list to copy: %s", err) } for _, f := range linesToArray(filesToClone) { - // TODO: linesToArray should really return [] if the string is empty. Instead it returns [""] + // TODO: Should return [] if the string is empty, but returns [""]. if f == "" { continue } err := d.sh("cp", strconv.Quote(f), strconv.Quote(dstDir)) if err != nil { - return fmt.Errorf("failed to copy %s to %s: %s", f, dstDir, err) + return fmt.Errorf("error copying %s to %s: %s", f, dstDir, err) } } disksToClone, err := d.run(nil, "sed -ne 's/.*file[Nn]ame = \"\\(.*vmdk\\)\"/\\1/p'", strconv.Quote(srcVmx)) if err != nil { - return fmt.Errorf("failed to get the vmdk list to clone %s", err) + return fmt.Errorf("error returning the .vmdk file list to clone:%s", err) } for _, disk := range linesToArray(disksToClone) { srcDisk := path.Join(srcDir, disk) @@ -166,29 +165,29 @@ func (d *ESX5Driver) Clone(dst, src string, linked bool, snapshot string) error destDisk := path.Join(dstDir, path.Base(disk)) err = d.sh("vmkfstools", "-d thin", "-i", strconv.Quote(srcDisk), strconv.Quote(destDisk)) if err != nil { - return fmt.Errorf("failed to clone disk %s: %s", srcDisk, err) + return fmt.Errorf("error cloning disk %s: %s", srcDisk, err) } } log.Printf("Successfully cloned %s to %s\n", src, dst) return nil } -func (d *ESX5Driver) CompactDisk(diskPathLocal string) error { +func (d *EsxiDriver) CompactDisk(diskPathLocal string) error { diskPath := d.datastorePath(diskPathLocal) return d.sh("vmkfstools", "--punchzero", strconv.Quote(diskPath)) } -func (d *ESX5Driver) CreateDisk(diskPathLocal string, size string, adapter_type string, typeId string) error { +func (d *EsxiDriver) CreateDisk(diskPathLocal string, size string, adapter_type string, typeId string) error { diskPath := strconv.Quote(d.datastorePath(diskPathLocal)) return d.sh("vmkfstools", "-c", size, "-d", typeId, "-a", adapter_type, diskPath) } -func (d *ESX5Driver) CreateSnapshot(vmxPath string, snapshotName string) error { +func (d *EsxiDriver) CreateSnapshot(vmxPath string, snapshotName string) error { _, err := d.run(nil, "vim-cmd", "vmsvc/snapshot.create", d.vmId, snapshotName) return err } -func (d *ESX5Driver) IsRunning(string) (bool, error) { +func (d *EsxiDriver) IsRunning(string) (bool, error) { state, err := d.run(nil, "vim-cmd", "vmsvc/power.getstate", d.vmId) if err != nil { return false, err @@ -196,7 +195,7 @@ func (d *ESX5Driver) IsRunning(string) (bool, error) { return strings.Contains(state, "Powered on"), nil } -func (d *ESX5Driver) ReloadVM() error { +func (d *EsxiDriver) ReloadVM() error { if d.vmId != "" { return d.sh("vim-cmd", "vmsvc/reload", d.vmId) } else { @@ -204,9 +203,10 @@ func (d *ESX5Driver) ReloadVM() error { } } -func (d *ESX5Driver) Start(vmxPathLocal string, headless bool) error { +func (d *EsxiDriver) Start(vmxPathLocal string, headless bool) error { for i := 0; i < 20; i++ { - //intentionally not checking for error since poweron may fail specially after initial VM registration + // Do not check for error. The power on operation may fail after the + // initial virtual machine registration. _ = d.sh("vim-cmd", "vmsvc/power.on", d.vmId) time.Sleep((time.Duration(i) * time.Second) + 1) running, err := d.IsRunning(vmxPathLocal) @@ -220,11 +220,11 @@ func (d *ESX5Driver) Start(vmxPathLocal string, headless bool) error { return errors.New("exceeded maximum retries to start the virtual machine") } -func (d *ESX5Driver) Stop(vmxPathLocal string) error { +func (d *EsxiDriver) Stop(vmxPathLocal string) error { return d.sh("vim-cmd", "vmsvc/power.off", d.vmId) } -func (d *ESX5Driver) Register(vmxPathLocal string) error { +func (d *EsxiDriver) Register(vmxPathLocal string) error { vmxPath := filepath.ToSlash(filepath.Join(d.outputDir, filepath.Base(vmxPathLocal))) if err := d.upload(vmxPath, vmxPathLocal, nil); err != nil { return err @@ -237,19 +237,19 @@ func (d *ESX5Driver) Register(vmxPathLocal string) error { return nil } -func (d *ESX5Driver) SuppressMessages(vmxPath string) error { +func (d *EsxiDriver) SuppressMessages(vmxPath string) error { return nil } -func (d *ESX5Driver) Unregister(vmxPathLocal string) error { +func (d *EsxiDriver) Unregister(vmxPathLocal string) error { return d.sh("vim-cmd", "vmsvc/unregister", d.vmId) } -func (d *ESX5Driver) Destroy() error { +func (d *EsxiDriver) Destroy() error { return d.sh("vim-cmd", "vmsvc/destroy", d.vmId) } -func (d *ESX5Driver) IsDestroyed() (bool, error) { +func (d *EsxiDriver) IsDestroyed() (bool, error) { err := d.sh("test", "!", "-e", strconv.Quote(d.outputDir)) if err != nil { return false, err @@ -257,7 +257,7 @@ func (d *ESX5Driver) IsDestroyed() (bool, error) { return true, err } -func (d *ESX5Driver) UploadISO(localPath string, checksum string, ui packersdk.Ui) (string, error) { +func (d *EsxiDriver) UploadISO(localPath string, checksum string, ui packersdk.Ui) (string, error) { finalPath := d.CachePath(localPath) if err := d.mkdir(filepath.ToSlash(filepath.Dir(finalPath))); err != nil { return "", err @@ -265,17 +265,17 @@ func (d *ESX5Driver) UploadISO(localPath string, checksum string, ui packersdk.U log.Printf("Verifying checksum of %s", finalPath) if d.VerifyChecksum(checksum, finalPath) { - log.Println("Initial checksum matched, no upload needed.") + log.Println("Checksum matched. Skipping upload.") return finalPath, nil } - log.Println("Initial checksum did not match, uploading.") + log.Println("Checksum did not match. Uploading...") if err := d.upload(finalPath, localPath, ui); err != nil { return "", err } if !d.VerifyChecksum(checksum, finalPath) { - e := fmt.Errorf("checksum verification failed for %s", finalPath) + e := fmt.Errorf("error verifying checksum for %s", finalPath) log.Println(e) return "", e } @@ -283,41 +283,42 @@ func (d *ESX5Driver) UploadISO(localPath string, checksum string, ui packersdk.U return finalPath, nil } -func (d *ESX5Driver) RemoveCache(localPath string) error { +func (d *EsxiDriver) RemoveCache(localPath string) error { finalPath := d.CachePath(localPath) - log.Printf("Removing remote cache path %s (local %s)", finalPath, localPath) + log.Printf("Removing remote cache path %s (local %s)...", finalPath, localPath) return d.sh("rm", "-f", strconv.Quote(finalPath)) } -func (d *ESX5Driver) ToolsIsoPath(string) string { +func (d *EsxiDriver) ToolsIsoPath(string) string { return "" } -func (d *ESX5Driver) ToolsInstall() error { +func (d *EsxiDriver) ToolsInstall() error { return d.sh("vim-cmd", "vmsvc/tools.install", d.vmId) } -func (d *ESX5Driver) Verify() error { - // Ensure that NetworkMapper is nil, since the mapping of device<->network - // is handled by ESXi and thus can't be performed by packer unless we - // query things. +func (d *EsxiDriver) Verify() error { + // Ensure that NetworkMapper is nil. The mapping of device<->network + // are handled by ESXi and thus can't be performed unless queried. - // FIXME: If we want to expose the network devices to the user, then we can - // probably use esxcli to enumerate the portgroup and switchId + // TODO: To expose the network devices to the user, esxcli may need to to be + // used to enumerate the portgroup and switchId. d.base.NetworkMapper = nil // Be safe/friendly and overwrite the rest of the utility functions with - // log functions despite the fact that these shouldn't be called anyways. + // log functions despite the fact that these should not be called. d.base.DhcpLeasesPath = func(device string) string { - log.Printf("Unexpected error, ESX5 driver attempted to call DhcpLeasesPath(%#v)\n", device) + log.Printf("unexpected error, ESXi driver attempted to call DhcpLeasesPath(%#v)\n", device) return "" } + d.base.DhcpConfPath = func(device string) string { - log.Printf("Unexpected error, ESX5 driver attempted to call DhcpConfPath(%#v)\n", device) + log.Printf("unexpected error: ESXi driver attempted to call DhcpConfPath(%#v)\n", device) return "" } + d.base.VmnetnatConfPath = func(device string) string { - log.Printf("Unexpected error, ESX5 driver attempted to call VmnetnatConfPath(%#v)\n", device) + log.Printf("unexpected error: ESXi driver attempted to call VmnetnatConfPath(%#v)\n", device) return "" } @@ -335,9 +336,9 @@ func (d *ESX5Driver) Verify() error { return nil } -func (d *ESX5Driver) VerifyOvfTool(SkipExport, skipValidateCredentials bool) error { - // We don't use ovftool if we aren't exporting a VM; return without error - // if ovftool isn't on path. +func (d *EsxiDriver) VerifyOvfTool(SkipExport, skipValidateCredentials bool) error { + // If we aren't exporting a virtual machine, we don't need to verify the + // ovftool is installed. if SkipExport { return nil } @@ -351,22 +352,19 @@ func (d *ESX5Driver) VerifyOvfTool(SkipExport, skipValidateCredentials bool) err return nil } - log.Printf("Verifying that ovftool credentials are valid...") - // check that password is valid by sending a dummy ovftool command - // now, so that we don't fail for a simple mistake after a long - // build + log.Printf("Verifying credentials for ovftool...") ovftool := GetOvfTool() if d.Password == "" { - return fmt.Errorf("exporting the vm from esxi with ovftool requires " + - "that you set a value for remote_password") + return fmt.Errorf("exporting the virtual machine from esxi with " + + "ovftool requires a value for 'remote_password'") } // Generate the uri of the host, with embedded credentials ovftool_uri := fmt.Sprintf("vi://%s", d.Host) u, err := url.Parse(ovftool_uri) if err != nil { - return fmt.Errorf("failed to generate uri for ovftool: %s", err) + return fmt.Errorf("error generating uri for ovftool: %s", err) } u.User = url.UserPassword(d.Username, d.Password) @@ -378,15 +376,14 @@ func (d *ESX5Driver) VerifyOvfTool(SkipExport, skipValidateCredentials bool) err cmd := exec.CommandContext(cmdCtx, ovftool, ovfToolArgs...) cmd.Stdout = &out - // Need to manually close stdin or else the ofvtool call will hang - // forever in a situation where the user has provided an invalid - // password or username + // Manually close stdin. If not, the process will hang if a user provides an + // invalid password or username. stdin, _ := cmd.StdinPipe() defer stdin.Close() if err := cmd.Run(); err != nil { outString := out.String() - // The command *should* fail with this error, if it + // The command *should* fail with the following error if it // authenticates properly. if !strings.Contains(outString, "Found wrong kind of object") { err := fmt.Errorf("ovftool validation error: %s; %s", @@ -402,7 +399,7 @@ func (d *ESX5Driver) VerifyOvfTool(SkipExport, skipValidateCredentials bool) err return nil } -func (d *ESX5Driver) HostIP(multistep.StateBag) (string, error) { +func (d *EsxiDriver) HostIP(multistep.StateBag) (string, error) { conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", d.Host, d.Port)) if err != nil { return "", err @@ -413,8 +410,8 @@ func (d *ESX5Driver) HostIP(multistep.StateBag) (string, error) { return host, err } -func (d *ESX5Driver) PotentialGuestIP(multistep.StateBag) ([]string, error) { - // GuestIP is defined by the user as d.Host..but let's validate it just to be sure +func (d *EsxiDriver) PotentialGuestIP(multistep.StateBag) ([]string, error) { + // Validate the user-defined address for the host. conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", d.Host, d.Port)) if err != nil { return []string{}, err @@ -425,7 +422,7 @@ func (d *ESX5Driver) PotentialGuestIP(multistep.StateBag) ([]string, error) { return []string{host}, err } -func (d *ESX5Driver) HostAddress(multistep.StateBag) (string, error) { +func (d *EsxiDriver) HostAddress(multistep.StateBag) (string, error) { // make a connection conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", d.Host, d.Port)) if err != nil { @@ -436,13 +433,13 @@ func (d *ESX5Driver) HostAddress(multistep.StateBag) (string, error) { // get the local address (the host) host, _, err := net.SplitHostPort(conn.LocalAddr().String()) if err != nil { - return "", fmt.Errorf("unable to determine host address : %v", err) + return "", fmt.Errorf("error determining host address: %v", err) } - // iterate through all the interfaces.. + // Interate through the interfaces. interfaces, err := net.Interfaces() if err != nil { - return "", fmt.Errorf("unable to enumerate host interfaces : %v", err) + return "", fmt.Errorf("error enumerating host interfaces: %v", err) } for _, intf := range interfaces { @@ -451,7 +448,7 @@ func (d *ESX5Driver) HostAddress(multistep.StateBag) (string, error) { continue } - // ..checking to see if any if it's addrs match the host address + // Check if the addresses match the host address for _, addr := range addrs { if addr.String() == host { return intf.HardwareAddr.String(), nil @@ -460,17 +457,17 @@ func (d *ESX5Driver) HostAddress(multistep.StateBag) (string, error) { } // ..unfortunately nothing was found - return "", fmt.Errorf("unable to locate interface matching host address : %v", host) + return "", fmt.Errorf("error locating an interface matching host address: %v", host) } -func (d *ESX5Driver) GuestAddress(multistep.StateBag) (string, error) { - // list all the interfaces on the ESXi host +func (d *EsxiDriver) GuestAddress(multistep.StateBag) (string, error) { + // Return all the interfaces on the host. r, err := d.esxcli("network", "ip", "interface", "list") if err != nil { - return "", fmt.Errorf("unable to retrieve host interfaces : %v", err) + return "", fmt.Errorf("error retrieving host interfaces: %v", err) } - // rip out the interface name and the MAC address from the csv output + // Extract the interface name and MAC address from the output. addrs := make(map[string]string) for record, err := r.read(); record != nil && err == nil; record, err = r.read() { if strings.ToUpper(record["Enabled"]) != "TRUE" { @@ -479,13 +476,13 @@ func (d *ESX5Driver) GuestAddress(multistep.StateBag) (string, error) { addrs[record["Name"]] = record["MAC Address"] } - // list all the addresses on the ESXi host + // Return all the IP addresses of the host. r, err = d.esxcli("network", "ip", "interface", "ipv4", "get") if err != nil { - return "", fmt.Errorf("unable to retrieve host addresses : %v", err) + return "", fmt.Errorf("error retrieving the host addresses : %v", err) } - // figure out the interface name that matches the specified d.Host address + // Find the interface name that matches the IP address of the host. var intf string intf = "" for record, err := r.read(); record != nil && err == nil; record, err = r.read() { @@ -495,28 +492,28 @@ func (d *ESX5Driver) GuestAddress(multistep.StateBag) (string, error) { } } if intf == "" { - return "", fmt.Errorf("unable to find matching address for guest") + return "", fmt.Errorf("error matching address for guest") } // find the MAC address according to the interface name result, ok := addrs[intf] if !ok { - return "", fmt.Errorf("unable to find address for guest interface") + return "", fmt.Errorf("error finding MAC address for interface %s", intf) } // ..and we're good return result, nil } -func (d *ESX5Driver) VNCAddress(ctx context.Context, _ string, portMin, portMax int) (string, int, error) { +func (d *EsxiDriver) VNCAddress(ctx context.Context, _ string, portMin, portMax int) (string, int, error) { var vncPort int - //Process ports ESXi is listening on to determine which are available - //This process does best effort to detect ports that are unavailable, - //it will ignore any ports listened to by only localhost + // Process ports ESXi is listening on to determine which are available. + // This process uses best effort to detect which are unavailable and will + // iignore any ports listened to by only localhost. r, err := d.esxcli("network", "ip", "connection", "list") if err != nil { - err = fmt.Errorf("unable to retrieve network information for host : %v", err) + err = fmt.Errorf("error retrieving network information for host: %v", err) return "", 0, err } @@ -536,7 +533,7 @@ func (d *ESX5Driver) VNCAddress(ctx context.Context, _ string, portMin, portMax envTimeout := os.Getenv("PACKER_ESXI_VNC_PROBE_TIMEOUT") if envTimeout != "" { if parsedTimeout, err := time.ParseDuration(envTimeout); err != nil { - log.Printf("Error parsing PACKER_ESXI_VNC_PROBE_TIMEOUT. Falling back to default (15s). %s", err) + log.Printf("Error parsing PACKER_ESXI_VNC_PROBE_TIMEOUT. Using the default timeout of %s: %v", vncTimeout, err) } else { vncTimeout = parsedTimeout } @@ -548,7 +545,7 @@ func (d *ESX5Driver) VNCAddress(ctx context.Context, _ string, portMin, portMax continue } address := fmt.Sprintf("%s:%d", d.Host, port) - log.Printf("Trying address: %s...", address) + log.Printf("Trying IP address: %s...", address) l, err := net.DialTimeout("tcp", address, vncTimeout) if err != nil { @@ -566,7 +563,7 @@ func (d *ESX5Driver) VNCAddress(ctx context.Context, _ string, portMin, portMax } if vncPort == 0 { - err := fmt.Errorf("unable to find available VNC port between %d and %d", + err := fmt.Errorf("error finding an available VNC port between %d and %d", portMin, portMax) return d.Host, vncPort, err } @@ -574,9 +571,9 @@ func (d *ESX5Driver) VNCAddress(ctx context.Context, _ string, portMin, portMax return d.Host, vncPort, nil } -// UpdateVMX, adds the VNC port to the VMX data. -func (ESX5Driver) UpdateVMX(_, password string, port int, data map[string]string) { - // Do not set remotedisplay.vnc.ip - this breaks ESXi. +// UpdateVMX, adds the VNC port to the .vmx file. +func (EsxiDriver) UpdateVMX(_, password string, port int, data map[string]string) { + // Important: Do not set 'remotedisplay.vnc.ip' on ESXi hosts. data["remotedisplay.vnc.enabled"] = "TRUE" data["remotedisplay.vnc.port"] = fmt.Sprintf("%d", port) if len(password) > 0 { @@ -584,7 +581,7 @@ func (ESX5Driver) UpdateVMX(_, password string, port int, data map[string]string } } -func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { +func (d *EsxiDriver) CommHost(state multistep.StateBag) (string, error) { sshc := state.Get("sshConfig").(*SSHConfig).Comm port := sshc.Port() @@ -602,21 +599,22 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { return "", err } - // The value in the Name field returned by 'esxcli network vm list' - // corresponds directly to the value of displayName set in the VMX file + // The value in the name field returned by 'esxcli network vm list' + // corresponds directly to the value of displayName set in the .vmx file. var displayName string if v, ok := state.GetOk("display_name"); ok { displayName = v.(string) } else { displayName = strings.Replace(d.VMName, " ", "_", -1) - log.Printf("No display_name set; falling back to using VMName %s "+ - "to look for SSH IP", displayName) + log.Printf("No 'display_name' set; using 'vmname' %s "+ + "to look for an IP address for SSH", displayName) } record, err := r.find("Name", displayName) if err != nil { return "", err } + wid := record["WorldID"] if wid == "" { return "", errors.New("unable to find worldid") @@ -627,7 +625,7 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { return "", err } - // Loop through interfaces + // Loop through interfaces. for { record, err = r.read() if err == io.EOF { @@ -641,8 +639,9 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { continue } - // if ssh is going through a bastion, we can't easily check if the nic is reachable on the network - // so just pick the first one that is not 0.0.0.0 + // if SSH is going through a bastion, it's not easy to check if the NIC + // is reachable on the network. Select the first NIC that is not equal + // to 0.0.0.0. if sshc.SSHBastionHost != "" { address := record["IPAddress"] state.Put("vm_address", address) @@ -650,7 +649,7 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { } // When multiple NICs are connected to the same network, choose - // one that has a route back. This Dial should ensure that. + // one that has a route back. conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", record["IPAddress"], port), 2*time.Second) if err != nil { if e, ok := err.(*net.OpError); ok { @@ -673,12 +672,12 @@ func (d *ESX5Driver) CommHost(state multistep.StateBag) (string, error) { // OutputDir implementation //------------------------------------------------------------------- -func (d *ESX5Driver) DirExists() (bool, error) { +func (d *EsxiDriver) DirExists() (bool, error) { err := d.sh("test", "-e", strconv.Quote(d.outputDir)) return err == nil, nil } -func (d *ESX5Driver) ListFiles() ([]string, error) { +func (d *EsxiDriver) ListFiles() ([]string, error) { stdout, err := d.ssh("ls -1p "+strconv.Quote(d.outputDir), nil) if err != nil { return nil, err @@ -701,36 +700,36 @@ func (d *ESX5Driver) ListFiles() ([]string, error) { return files, nil } -func (d *ESX5Driver) MkdirAll() error { +func (d *EsxiDriver) MkdirAll() error { return d.mkdir(d.outputDir) } -func (d *ESX5Driver) Remove(path string) error { +func (d *EsxiDriver) Remove(path string) error { return d.sh("rm", strconv.Quote(path)) } -func (d *ESX5Driver) RemoveAll() error { +func (d *EsxiDriver) RemoveAll() error { return d.sh("rm", "-rf", strconv.Quote(d.outputDir)) } -func (d *ESX5Driver) SetOutputDir(path string) { +func (d *EsxiDriver) SetOutputDir(path string) { d.outputDir = d.datastorePath(path) } -func (d *ESX5Driver) String() string { +func (d *EsxiDriver) String() string { return d.outputDir } -func (d *ESX5Driver) datastorePath(path string) string { +func (d *EsxiDriver) datastorePath(path string) string { dirPath := filepath.Dir(path) return filepath.ToSlash(filepath.Join("/vmfs/volumes", d.Datastore, dirPath, filepath.Base(path))) } -func (d *ESX5Driver) CachePath(path string) string { +func (d *EsxiDriver) CachePath(path string) string { return filepath.ToSlash(filepath.Join("/vmfs/volumes", d.CacheDatastore, d.CacheDirectory, filepath.Base(path))) } -func (d *ESX5Driver) connect() error { +func (d *EsxiDriver) connect() error { address := fmt.Sprintf("%s:%d", d.Host, d.Port) auth := []gossh.AuthMethod{ @@ -766,7 +765,7 @@ func (d *ESX5Driver) connect() error { return nil } -func (d *ESX5Driver) checkSystemVersion() error { +func (d *EsxiDriver) checkSystemVersion() error { r, err := d.esxcli("system", "version", "get") if err != nil { return err @@ -782,7 +781,7 @@ func (d *ESX5Driver) checkSystemVersion() error { return nil } -func (d *ESX5Driver) checkGuestIPHackEnabled() error { +func (d *EsxiDriver) checkGuestIPHackEnabled() error { r, err := d.esxcli("system", "settings", "advanced", "list", "-o", "/Net/GuestIPHack") if err != nil { return err @@ -795,19 +794,19 @@ func (d *ESX5Driver) checkGuestIPHackEnabled() error { if record["IntValue"] != "1" { return errors.New( - "guestiphack is required, enable by running this on the host :\n" + + "guestiphack is required, enable by running this on the host:\n" + "esxcli system settings advanced set -o /Net/GuestIPHack -i 1") } return nil } -func (d *ESX5Driver) mkdir(path string) error { +func (d *EsxiDriver) mkdir(path string) error { return d.sh("mkdir", "-p", strconv.Quote(path)) } -func (d *ESX5Driver) upload(dst, src string, ui packersdk.Ui) error { - // Get size so we can set up progress tracker +func (d *EsxiDriver) upload(dst, src string, ui packersdk.Ui) error { + // Get the size to setup the progress tracker. info, err := os.Stat(src) if err != nil { return err @@ -829,7 +828,7 @@ func (d *ESX5Driver) upload(dst, src string, ui packersdk.Ui) error { return d.comm.Upload(dst, f, nil) } -func (d *ESX5Driver) Download(src, dst string) error { +func (d *EsxiDriver) Download(src, dst string) error { file, err := os.Create(dst) if err != nil { return err @@ -838,12 +837,12 @@ func (d *ESX5Driver) Download(src, dst string) error { return d.comm.Download(d.datastorePath(src), file) } -func (d *ESX5Driver) Export(args []string) error { +func (d *EsxiDriver) Export(args []string) error { return d.base.Export(args) } // VerifyChecksum checks that file on the esxi instance matches hash -func (d *ESX5Driver) VerifyChecksum(hash string, file string) bool { +func (d *EsxiDriver) VerifyChecksum(hash string, file string) bool { if hash == "none" { if err := d.sh("stat", strconv.Quote(file)); err != nil { return false @@ -873,7 +872,7 @@ func (d *ESX5Driver) VerifyChecksum(hash string, file string) bool { return err == nil } -func (d *ESX5Driver) ssh(command string, stdin io.Reader) (*bytes.Buffer, error) { +func (d *EsxiDriver) ssh(command string, stdin io.Reader) (*bytes.Buffer, error) { ctx := context.TODO() var stdout, stderr bytes.Buffer @@ -900,7 +899,7 @@ func (d *ESX5Driver) ssh(command string, stdin io.Reader) (*bytes.Buffer, error) return &stdout, nil } -func (d *ESX5Driver) run(stdin io.Reader, args ...string) (string, error) { +func (d *EsxiDriver) run(stdin io.Reader, args ...string) (string, error) { stdout, err := d.ssh(strings.Join(args, " "), stdin) if err != nil { return "", err @@ -908,12 +907,12 @@ func (d *ESX5Driver) run(stdin io.Reader, args ...string) (string, error) { return stdout.String(), nil } -func (d *ESX5Driver) sh(args ...string) error { +func (d *EsxiDriver) sh(args ...string) error { _, err := d.run(nil, args...) return err } -func (d *ESX5Driver) esxcli(args ...string) (*esxcliReader, error) { +func (d *EsxiDriver) esxcli(args ...string) (*esxcliReader, error) { stdout, err := d.ssh("esxcli --formatter csv "+strings.Join(args, " "), nil) if err != nil { return nil, err @@ -926,7 +925,7 @@ func (d *ESX5Driver) esxcli(args ...string) (*esxcliReader, error) { return &esxcliReader{r, header}, nil } -func (d *ESX5Driver) GetVmwareDriver() VmwareDriver { +func (d *EsxiDriver) GetVmwareDriver() VmwareDriver { return d.base } @@ -962,7 +961,7 @@ func (r *esxcliReader) find(key, val string) (map[string]string, error) { } } -func (d *ESX5Driver) AcquireVNCOverWebsocketTicket() (*types.VirtualMachineTicket, error) { +func (d *EsxiDriver) AcquireVNCOverWebsocketTicket() (*types.VirtualMachineTicket, error) { vm, err := d.finder.VirtualMachine(d.ctx, d.VMName) if err != nil { return nil, err diff --git a/builder/vmware/common/driver_esx5_test.go b/builder/vmware/common/driver_esxi_test.go similarity index 76% rename from builder/vmware/common/driver_esx5_test.go rename to builder/vmware/common/driver_esxi_test.go index fde45922..b7ca048f 100644 --- a/builder/vmware/common/driver_esx5_test.go +++ b/builder/vmware/common/driver_esxi_test.go @@ -13,12 +13,12 @@ import ( "github.com/hashicorp/packer-plugin-sdk/template/config" ) -func TestESX5Driver_implDriver(t *testing.T) { - var _ Driver = new(ESX5Driver) +func TestEsxiDriver_implDriver(t *testing.T) { + var _ Driver = new(EsxiDriver) } -func TestESX5Driver_UpdateVMX(t *testing.T) { - var driver ESX5Driver +func TestEsxiDriver_UpdateVMX(t *testing.T) { + var driver EsxiDriver data := make(map[string]string) driver.UpdateVMX("0.0.0.0", "", 5900, data) if _, ok := data["remotedisplay.vnc.ip"]; ok { @@ -33,19 +33,19 @@ func TestESX5Driver_UpdateVMX(t *testing.T) { } } -func TestESX5Driver_implOutputDir(t *testing.T) { - var _ OutputDir = new(ESX5Driver) +func TestEsxiDriver_implOutputDir(t *testing.T) { + var _ OutputDir = new(EsxiDriver) } -func TestESX5Driver_implVNCAddressFinder(t *testing.T) { - var _ VNCAddressFinder = new(ESX5Driver) +func TestEsxiDriver_implVNCAddressFinder(t *testing.T) { + var _ VNCAddressFinder = new(EsxiDriver) } -func TestESX5Driver_implRemoteDriver(t *testing.T) { - var _ RemoteDriver = new(ESX5Driver) +func TestEsxiDriver_implRemoteDriver(t *testing.T) { + var _ RemoteDriver = new(EsxiDriver) } -func TestESX5Driver_HostIP(t *testing.T) { +func TestEsxiDriver_HostIP(t *testing.T) { expected_host := "127.0.0.1" //create mock SSH server @@ -53,7 +53,7 @@ func TestESX5Driver_HostIP(t *testing.T) { port := listen.Addr().(*net.TCPAddr).Port defer listen.Close() - driver := ESX5Driver{Host: "localhost", Port: port} + driver := EsxiDriver{Host: "localhost", Port: port} state := new(multistep.BasicStateBag) if host, _ := driver.HostIP(state); host != expected_host { @@ -61,7 +61,7 @@ func TestESX5Driver_HostIP(t *testing.T) { } } -func TestESX5Driver_CommHost(t *testing.T) { +func TestEsxiDriver_CommHost(t *testing.T) { const expected_host = "127.0.0.1" conf := make(map[string]interface{}) @@ -78,7 +78,7 @@ func TestESX5Driver_CommHost(t *testing.T) { state := new(multistep.BasicStateBag) sshConfig := SSHConfig{Comm: commConfig} state.Put("sshConfig", &sshConfig) - driver := ESX5Driver{CommConfig: sshConfig.Comm} + driver := EsxiDriver{CommConfig: sshConfig.Comm} host, err := driver.CommHost(state) if err != nil { @@ -96,8 +96,8 @@ func TestESX5Driver_CommHost(t *testing.T) { } } -func TestESX5Driver_VerifyOvfTool(t *testing.T) { - driver := ESX5Driver{} +func TestEsxiDriver_VerifyOvfTool(t *testing.T) { + driver := EsxiDriver{} // should always skip validation if export is skipped, so this should always // pass even when ovftool is not installed. err := driver.VerifyOvfTool(true, false) diff --git a/builder/vmware/common/output_dir.go b/builder/vmware/common/output_dir.go index cef36796..74f5bf64 100644 --- a/builder/vmware/common/output_dir.go +++ b/builder/vmware/common/output_dir.go @@ -7,7 +7,7 @@ package common // of the output directory for VMware-based products. The abstraction is made // so that the output directory can be properly made on remote (ESXi) based // VMware products as well as local. -// For remote builds, OutputDir interface is satisfied by the ESX5Driver. +// For remote builds, OutputDir interface is satisfied by the EsxiDriver. type OutputDir interface { DirExists() (bool, error) ListFiles() ([]string, error) diff --git a/builder/vmware/common/step_export.go b/builder/vmware/common/step_export.go index b2a9fa1e..21e610ff 100644 --- a/builder/vmware/common/step_export.go +++ b/builder/vmware/common/step_export.go @@ -94,7 +94,7 @@ func (s *StepExport) Run(ctx context.Context, state multistep.StateBag) multiste var args, ui_args []string ovftool := GetOvfTool() - if c.RemoteType == "esx5" { + if c.RemoteType == "esxi" || c.RemoteType == "esx5" { // Generate arguments for the ovftool command, but obfuscating the // password that we can log the command to the UI for debugging. ui_args, err := s.generateRemoteExportArgs(c, displayName, true, exportOutputPath) diff --git a/builder/vmware/common/step_export_test.go b/builder/vmware/common/step_export_test.go index 0f85a9f2..e4720a56 100644 --- a/builder/vmware/common/step_export_test.go +++ b/builder/vmware/common/step_export_test.go @@ -26,7 +26,7 @@ func remoteExportTestState(t *testing.T) multistep.StateBag { RemoteHost: "123.45.67.8", RemotePassword: "password", RemoteUser: "user", - RemoteType: "esx5", + RemoteType: "esxi", } state.Put("driverConfig", driverConfig) state.Put("display_name", "vm_name") diff --git a/builder/vmware/common/step_output_dir_test.go b/builder/vmware/common/step_output_dir_test.go index e33ebc70..a7729ff6 100644 --- a/builder/vmware/common/step_output_dir_test.go +++ b/builder/vmware/common/step_output_dir_test.go @@ -208,7 +208,7 @@ func TestStepOutputDir_Remote(t *testing.T) { step := &StepOutputDir{ OutputConfig: outconfig, VMName: "testVM", - RemoteType: "esx5", + RemoteType: "esxi", } // Delete the test output directory when done defer os.RemoveAll(td) diff --git a/builder/vmware/common/step_prepare_tools.go b/builder/vmware/common/step_prepare_tools.go index 7dc4b2ca..cd879753 100644 --- a/builder/vmware/common/step_prepare_tools.go +++ b/builder/vmware/common/step_prepare_tools.go @@ -6,6 +6,7 @@ package common import ( "context" "fmt" + "log" "os" "github.com/hashicorp/packer-plugin-sdk/multistep" @@ -20,7 +21,9 @@ type StepPrepareTools struct { func (c *StepPrepareTools) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { driver := state.Get("driver").(Driver) - if c.RemoteType == "esx5" { + if c.RemoteType == "esxi" || c.RemoteType == "esx5" { + // Log a deprecation warning for the 'esx5' remote type. + log.Println("The 'esx5' remote type is deprecated. Please use 'esxi' instead.") return multistep.ActionContinue } diff --git a/builder/vmware/common/step_prepare_tools_test.go b/builder/vmware/common/step_prepare_tools_test.go index 86be2dbc..1507599d 100644 --- a/builder/vmware/common/step_prepare_tools_test.go +++ b/builder/vmware/common/step_prepare_tools_test.go @@ -60,10 +60,10 @@ func TestStepPrepareTools(t *testing.T) { } } -func TestStepPrepareTools_esx5(t *testing.T) { +func TestStepPrepareTools_esxi(t *testing.T) { state := testState(t) step := &StepPrepareTools{ - RemoteType: "esx5", + RemoteType: "esxi", ToolsUploadFlavor: "foo", } diff --git a/builder/vmware/common/step_remote_upload.go b/builder/vmware/common/step_remote_upload.go index f6fe5ee1..6f9a7eab 100644 --- a/builder/vmware/common/step_remote_upload.go +++ b/builder/vmware/common/step_remote_upload.go @@ -35,10 +35,10 @@ func (s *StepRemoteUpload) Run(ctx context.Context, state multistep.StateBag) mu return multistep.ActionContinue } - if esx5, ok := remote.(*ESX5Driver); ok { - remotePath := esx5.CachePath(path) + if esxi, ok := remote.(*EsxiDriver); ok { + remotePath := esxi.CachePath(path) - if esx5.VerifyChecksum(s.Checksum, remotePath) { + if esxi.VerifyChecksum(s.Checksum, remotePath) { ui.Say("Remote cache was verified skipping remote upload...") state.Put(s.Key, remotePath) return multistep.ActionContinue diff --git a/builder/vmware/common/step_upload_tools.go b/builder/vmware/common/step_upload_tools.go index cee95be7..836ab43a 100644 --- a/builder/vmware/common/step_upload_tools.go +++ b/builder/vmware/common/step_upload_tools.go @@ -31,7 +31,7 @@ func (c *StepUploadTools) Run(ctx context.Context, state multistep.StateBag) mul return multistep.ActionContinue } - if c.RemoteType == "esx5" { + if c.RemoteType == "esxi" || c.RemoteType == "esx5" { if err := driver.ToolsInstall(); err != nil { state.Put("error", fmt.Errorf("unable to mount VMware Tools ISO, check the 'guest_os_type'")) } diff --git a/builder/vmware/common/step_upload_vmx.go b/builder/vmware/common/step_upload_vmx.go index 7e29ef92..c754708d 100644 --- a/builder/vmware/common/step_upload_vmx.go +++ b/builder/vmware/common/step_upload_vmx.go @@ -23,7 +23,7 @@ func (c *StepUploadVMX) Run(ctx context.Context, state multistep.StateBag) multi ui := state.Get("ui").(packersdk.Ui) vmxPath := state.Get("vmx_path").(string) - if c.RemoteType == "esx5" { + if c.RemoteType == "esxi" || c.RemoteType == "esx5" { remoteDriver, ok := driver.(RemoteDriver) if ok { remoteVmxPath := filepath.ToSlash(filepath.Join(fmt.Sprintf("%s", remoteDriver), filepath.Base(vmxPath))) diff --git a/builder/vmware/common/step_vnc_connect.go b/builder/vmware/common/step_vnc_connect.go index 18d66dca..00648dc7 100644 --- a/builder/vmware/common/step_vnc_connect.go +++ b/builder/vmware/common/step_vnc_connect.go @@ -52,7 +52,7 @@ func (s *StepVNCConnect) Run(ctx context.Context, state multistep.StateBag) mult } func (s *StepVNCConnect) ConnectVNCOverWebsocketClient(state multistep.StateBag) (*vnc.ClientConn, error) { - driver := state.Get("driver").(*ESX5Driver) + driver := state.Get("driver").(*EsxiDriver) // Acquire websocket ticket ticket, err := driver.AcquireVNCOverWebsocketTicket() diff --git a/builder/vmware/common/tools_config.go b/builder/vmware/common/tools_config.go index 5d187128..0a0f3afa 100644 --- a/builder/vmware/common/tools_config.go +++ b/builder/vmware/common/tools_config.go @@ -29,13 +29,14 @@ type ToolsConfig struct { // the guest operating system. Allowed values are `darwin` (macOS), `linux`, // and `windows`. Default is empty and no version will be uploaded. ToolsUploadFlavor string `mapstructure:"tools_upload_flavor" required:"false"` - // The path in the virtual machine to upload the VMware Tools. This only - // takes effect if `tools_upload_flavor` is non-empty. This is a - // [configuration template](/packer/docs/templates/legacy_json_templates/engine) - // that has a single valid variable, `Flavor`, which will be the value of - // `tools_upload_flavor` when the upload path is set to `{{.Flavor}}.iso`. + // The path in the VM to upload the VMware tools. This only takes effect if + // `tools_upload_flavor` is non-empty. This is a [configuration + // template](/packer/docs/templates/legacy_json_templates/engine) that has a + // single valid variable: `Flavor`, which will be the value of + // `tools_upload_flavor`. By default the upload path is set to + // `{{.Flavor}}.iso`. // - // ~> **Note:** This setting is not used when `remote_type` is `esx5`. + // ~> **Note:** This setting is not used when `remote_type` is `esxi` or `esx5`. ToolsUploadPath string `mapstructure:"tools_upload_path" required:"false"` // The local path on your machine to the VMware Tools ISO file. // diff --git a/builder/vmware/iso/builder_test.go b/builder/vmware/iso/builder_test.go index 0dc8dd78..92ccfa0b 100644 --- a/builder/vmware/iso/builder_test.go +++ b/builder/vmware/iso/builder_test.go @@ -144,8 +144,8 @@ func TestBuilderPrepare_RemoteType(t *testing.T) { config := testConfig() config["format"] = "ovf" - config["remote_host"] = "foobar.example.com" - config["remote_password"] = "supersecret" + config["remote_host"] = "esxi-01.example.com" + config["remote_password"] = "VMware1!" config["skip_validate_credentials"] = true // Bad config["remote_type"] = "foobar" @@ -157,7 +157,7 @@ func TestBuilderPrepare_RemoteType(t *testing.T) { t.Fatal("should have error") } - config["remote_type"] = "esx5" + config["remote_type"] = "esxi" // Bad config["remote_host"] = "" b = Builder{} @@ -185,9 +185,9 @@ func TestBuilderPrepare_RemoteType(t *testing.T) { } // Good - config["remote_type"] = "esx5" - config["remote_host"] = "foobar.example.com" - config["remote_password"] = "supersecret" + config["remote_type"] = "esxi" + config["remote_host"] = "esxi-01.example.com" + config["remote_password"] = "VMware1!" b = Builder{} _, warns, err = b.Prepare(config) if len(warns) > 0 { @@ -219,20 +219,20 @@ func TestBuilderPrepare_Export(t *testing.T) { }, { InputConfigVals: map[string]string{ - "remote_type": "esx5", + "remote_type": "esxi", "format": "", - "remote_host": "fakehost.com", - "remote_password": "fakepassword", - "remote_username": "fakeuser", + "remote_host": "esxi-01.example.com", + "remote_username": "root", + "remote_password": "VMware1!", }, ExpectedSkipExportValue: false, ExpectedFormat: "ovf", ExpectedErr: false, - Reason: "should have defaulted format to ovf with remote set to esx5.", + Reason: "should have defaulted format to ovf with remote set to esxi.", }, { InputConfigVals: map[string]string{ - "remote_type": "esx5", + "remote_type": "esxi", "format": "", }, ExpectedSkipExportValue: false, @@ -244,9 +244,9 @@ func TestBuilderPrepare_Export(t *testing.T) { InputConfigVals: map[string]string{ "remote_type": "invalid", "format": "", - "remote_host": "fakehost.com", - "remote_password": "fakepassword", - "remote_username": "fakeuser", + "remote_host": "esxi-01.example.com", + "remote_username": "root", + "remote_password": "VMware1!", }, ExpectedSkipExportValue: false, ExpectedFormat: "ovf", @@ -275,11 +275,11 @@ func TestBuilderPrepare_Export(t *testing.T) { }, { InputConfigVals: map[string]string{ - "remote_type": "esx5", + "remote_type": "esxi", "format": "ova", - "remote_host": "fakehost.com", - "remote_password": "fakepassword", - "remote_username": "fakeuser", + "remote_host": "esxi-01.example.com", + "remote_username": "root", + "remote_password": "VMware1!", }, ExpectedSkipExportValue: false, ExpectedFormat: "ova", @@ -319,8 +319,8 @@ func TestBuilderPrepare_RemoteExport(t *testing.T) { var b Builder config := testConfig() - config["remote_type"] = "esx5" - config["remote_host"] = "foobar.example.com" + config["remote_type"] = "esxi" + config["remote_host"] = "esxi-01.example.com" config["skip_validate_credentials"] = true // Bad config["remote_password"] = "" @@ -333,7 +333,7 @@ func TestBuilderPrepare_RemoteExport(t *testing.T) { } // Good - config["remote_password"] = "supersecret" + config["remote_password"] = "VMware1!" b = Builder{} _, warns, err = b.Prepare(config) if len(warns) != 0 { @@ -363,7 +363,7 @@ func TestBuilderPrepare_Format(t *testing.T) { for _, format := range goodFormats { // Good config["format"] = format - config["remote_type"] = "esx5" + config["remote_type"] = "esxi" config["remote_host"] = "hosty.hostface" config["remote_password"] = "password" config["skip_validate_credentials"] = true diff --git a/builder/vmware/iso/config.go b/builder/vmware/iso/config.go index f441f562..96f85370 100644 --- a/builder/vmware/iso/config.go +++ b/builder/vmware/iso/config.go @@ -137,13 +137,13 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { // Default is growable virtual disk split in 2GB files. c.DiskTypeId = "1" - if c.RemoteType == "esx5" { + if c.RemoteType == "esxi" || c.RemoteType == "esx5" { c.DiskTypeId = "zeroedthick" c.SkipCompaction = true } } - if c.RemoteType == "esx5" { + if c.RemoteType == "esxi" || c.RemoteType == "esx5" { if c.DiskTypeId != "thin" && !c.SkipCompaction { errs = packersdk.MultiErrorAppend( errs, fmt.Errorf("skip_compaction must be 'true' for disk_type_id: %s", c.DiskTypeId)) diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index eeefc4b8..274067a9 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -129,7 +129,7 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) { // Default is growable virtual disk split in 2GB files. c.DiskTypeId = "1" - if c.RemoteType == "esx5" { + if c.RemoteType == "esxi" || c.RemoteType == "esx5" { c.DiskTypeId = "zeroedthick" } } diff --git a/builder/vmware/vmx/config_test.go b/builder/vmware/vmx/config_test.go index f108b126..ff50a2c0 100644 --- a/builder/vmware/vmx/config_test.go +++ b/builder/vmware/vmx/config_test.go @@ -82,20 +82,20 @@ func TestNewConfig_exportConfig(t *testing.T) { }, { InputConfigVals: map[string]string{ - "remote_type": "esx5", + "remote_type": "esxi", "format": "", - "remote_host": "fakehost.com", - "remote_password": "fakepassword", - "remote_username": "fakeuser", + "remote_host": "esxi-01.example.com", + "remote_username": "root", + "remote_password": "VMware1!", }, ExpectedSkipExportValue: false, ExpectedFormat: "ovf", ExpectedErr: false, - Reason: "should have defaulted format to ovf with remote set to esx5.", + Reason: "should have defaulted format to ovf with remote set to esxi.", }, { InputConfigVals: map[string]string{ - "remote_type": "esx5", + "remote_type": "esxi", "format": "", }, ExpectedSkipExportValue: false, @@ -107,9 +107,9 @@ func TestNewConfig_exportConfig(t *testing.T) { InputConfigVals: map[string]string{ "remote_type": "invalid", "format": "", - "remote_host": "fakehost.com", - "remote_password": "fakepassword", - "remote_username": "fakeuser", + "remote_host": "esxi-01.example.com", + "remote_username": "root", + "remote_password": "VMware1!", }, ExpectedSkipExportValue: false, ExpectedFormat: "ovf", @@ -138,11 +138,11 @@ func TestNewConfig_exportConfig(t *testing.T) { }, { InputConfigVals: map[string]string{ - "remote_type": "esx5", + "remote_type": "esxi", "format": "ova", - "remote_host": "fakehost.com", - "remote_password": "fakepassword", - "remote_username": "fakeuser", + "remote_host": "esxi-01.example.com", + "remote_username": "root", + "remote_password": "VMware1!", }, ExpectedSkipExportValue: false, ExpectedFormat: "ova", diff --git a/docs-partials/builder/vmware/common/DriverConfig-not-required.mdx b/docs-partials/builder/vmware/common/DriverConfig-not-required.mdx index dd2e13c3..1b2d48a3 100644 --- a/docs-partials/builder/vmware/common/DriverConfig-not-required.mdx +++ b/docs-partials/builder/vmware/common/DriverConfig-not-required.mdx @@ -1,42 +1,49 @@ -- `fusion_app_path` (string) - Path to "VMware Fusion.app". By default this is - /Applications/VMware Fusion.app but this setting allows you to - customize this. +- `fusion_app_path` (string) - The installation path of the VMware Fusion application. Defaults to + `/Applications/VMware Fusion.app` + + ~> **Note:** This is only required if you are using VMware Fusion as a + local desktop hypervisor and have installed it in a non-default location. -- `remote_type` (string) - The type of remote machine that will be used to - build this VM rather than a local desktop product. The only value accepted - for this currently is esx5. If this is not set, a desktop product will - be used. By default, this is not set. +- `remote_type` (string) - The type of remote hypervisor that will be used. If set, the remote + hypervisor will be used for the build. If not set, a local desktop + hypervisor (VMware Fusion or VMware Workstation) will be used. + Available options include `esxi` and `esx5` for VMware ESXi. + + ~> **Note:** Use of `esxi` is recommended; `esx5` is deprecated. -- `remote_datastore` (string) - The path to the datastore where the VM will be stored - on the ESXi machine. +- `remote_datastore` (string) - The datastore on the remote hypervisor where the virtual machine will be + stored. -- `remote_cache_datastore` (string) - The path to the datastore where supporting files - will be stored during the build on the remote machine. +- `remote_cache_datastore` (string) - The datastore attached to the remote hypervisor to use for the build. + Supporting files such as ISOs and floppies are cached in this datastore + during the build. Defaults to `datastore1`. -- `remote_cache_directory` (string) - The path where the ISO and/or floppy files will - be stored during the build on the remote machine. The path is relative to - the remote_cache_datastore on the remote machine. +- `remote_cache_directory` (string) - The directory path on the remote cache datastore to use for the build. + Supporting files such as ISOs and floppies are cached in this directory, + relative to the `remote_cache_datastore`, during the build. Defaults to + `packer_cache`. -- `cleanup_remote_cache` (bool) - When set to true, Packer will cleanup the cache folder where the ISO file is stored during the build on the remote machine. - By default, this is set to false. +- `cleanup_remote_cache` (bool) - Remove items added to the remote cache after the build is complete. + Defaults to `false`. -- `remote_host` (string) - The host of the remote machine used for access. - This is only required if remote_type is enabled. +- `remote_host` (string) - The fully qualified domain name or IP address of the remote hypervisor + where the virtual machine is created. + + ~> **Note:** Required if `remote_type` is set. -- `remote_port` (int) - The SSH port of the remote machine +- `remote_port` (int) - The SSH port of the remote hypervisor. Defaults to `22`. -- `remote_username` (string) - The SSH username used to access the remote machine. +- `remote_username` (string) - The SSH username for access to the remote hypervisor. Defaults to `root`. -- `remote_password` (string) - The SSH password for access to the remote machine. +- `remote_password` (string) - The SSH password for access to the remote hypervisor. -- `remote_private_key_file` (string) - The SSH key for access to the remote machine. +- `remote_private_key_file` (string) - The SSH key for access to the remote hypervisor. -- `skip_validate_credentials` (bool) - When Packer is preparing to run a - remote hypervisor build, and export is not disable, by default it runs a no-op - ovftool command to make sure that the remote_username and remote_password - given are valid. If you set this flag to true, Packer will skip this - validation. Default: false. +- `skip_validate_credentials` (bool) - Skip the validation of the credentials for access to the remote + hypervisor. By default, export is enabled and the plugin will validate + the credentials ('remote_username' and 'remote_password'), for use by + VMware OVF Tool, before starting the build. Defaults to `false`. diff --git a/docs-partials/builder/vmware/common/ToolsConfig-not-required.mdx b/docs-partials/builder/vmware/common/ToolsConfig-not-required.mdx index 09ed7e2c..7bad8c21 100644 --- a/docs-partials/builder/vmware/common/ToolsConfig-not-required.mdx +++ b/docs-partials/builder/vmware/common/ToolsConfig-not-required.mdx @@ -4,13 +4,14 @@ the guest operating system. Allowed values are `darwin` (macOS), `linux`, and `windows`. Default is empty and no version will be uploaded. -- `tools_upload_path` (string) - The path in the virtual machine to upload the VMware Tools. This only - takes effect if `tools_upload_flavor` is non-empty. This is a - [configuration template](/packer/docs/templates/legacy_json_templates/engine) - that has a single valid variable, `Flavor`, which will be the value of - `tools_upload_flavor` when the upload path is set to `{{.Flavor}}.iso`. +- `tools_upload_path` (string) - The path in the VM to upload the VMware tools. This only takes effect if + `tools_upload_flavor` is non-empty. This is a [configuration + template](/packer/docs/templates/legacy_json_templates/engine) that has a + single valid variable: `Flavor`, which will be the value of + `tools_upload_flavor`. By default the upload path is set to + `{{.Flavor}}.iso`. - ~> **Note:** This setting is not used when `remote_type` is `esx5`. + ~> **Note:** This setting is not used when `remote_type` is `esxi` or `esx5`. - `tools_source_path` (string) - The local path on your machine to the VMware Tools ISO file.