vendor vsphere plugin

This commit is contained in:
sylviamoss 2021-04-09 17:58:56 +02:00 committed by Megan Marsh
parent f6854f5528
commit 41c66d6935
123 changed files with 12354 additions and 344 deletions

View File

@ -20,6 +20,10 @@ import (
dockerpushpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-push"
dockersavepostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-save"
dockertagpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-tag"
vsphereclonebuilder "github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/clone"
vsphereisobuilder "github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/iso"
vspherepostprocessor "github.com/hashicorp/packer-plugin-vsphere/post-processor/vsphere"
vspheretemplatepostprocessor "github.com/hashicorp/packer-plugin-vsphere/post-processor/vsphere-template"
)
// VendoredDatasources are datasource components that were once bundled with the
@ -38,6 +42,8 @@ var VendoredBuilders = map[string]packersdk.Builder{
"amazon-ebssurrogate": new(amazonebssurrogatebuilder.Builder),
"amazon-ebsvolume": new(amazonebsvolumebuilder.Builder),
"amazon-instance": new(amazoninstancebuilder.Builder),
"vsphere-clone": new(vsphereclonebuilder.Builder),
"vsphere-iso": new(vsphereisobuilder.Builder),
}
// VendoredProvisioners are provisioner components that were once bundled with the
@ -47,12 +53,14 @@ var VendoredProvisioners = map[string]packersdk.Provisioner{}
// VendoredPostProcessors are post-processor components that were once bundled with the
// Packer core, but are now being imported from their counterpart plugin repos
var VendoredPostProcessors = map[string]packersdk.PostProcessor{
"docker-import": new(dockerimportpostprocessor.PostProcessor),
"docker-push": new(dockerpushpostprocessor.PostProcessor),
"docker-save": new(dockersavepostprocessor.PostProcessor),
"docker-tag": new(dockertagpostprocessor.PostProcessor),
"exoscale-import": new(exoscaleimportpostprocessor.PostProcessor),
"amazon-import": new(anazibimportpostprocessor.PostProcessor),
"docker-import": new(dockerimportpostprocessor.PostProcessor),
"docker-push": new(dockerpushpostprocessor.PostProcessor),
"docker-save": new(dockersavepostprocessor.PostProcessor),
"docker-tag": new(dockertagpostprocessor.PostProcessor),
"exoscale-import": new(exoscaleimportpostprocessor.PostProcessor),
"amazon-import": new(anazibimportpostprocessor.PostProcessor),
"vsphere": new(vspherepostprocessor.PostProcessor),
"vsphere-template": new(vspheretemplatepostprocessor.PostProcessor),
}
// Upon init lets load up any plugins that were vendored manually into the default

4
go.mod
View File

@ -53,6 +53,7 @@ require (
github.com/hashicorp/packer-plugin-amazon v0.0.1
github.com/hashicorp/packer-plugin-docker v0.0.7
github.com/hashicorp/packer-plugin-sdk v0.1.3-0.20210407132324-af39c7839daf
github.com/hashicorp/packer-plugin-vsphere v0.0.0-20210409154313-881202ca3ffe
github.com/hashicorp/vault/api v1.0.4
github.com/hetznercloud/hcloud-go v1.15.1
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4
@ -83,7 +84,8 @@ require (
github.com/ucloud/ucloud-sdk-go v0.16.3
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6
github.com/ulikunitz/xz v0.5.6
github.com/vmware/govmomi v0.23.1
github.com/ulikunitz/xz v0.5.5
github.com/vmware/govmomi v0.24.1
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0
github.com/yandex-cloud/go-genproto v0.0.0-20200915125933-33de72a328bd
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c

8
go.sum
View File

@ -450,6 +450,8 @@ github.com/hashicorp/packer-plugin-sdk v0.1.1/go.mod h1:1d3nqB9LUsXMQaNUiL67Q+WY
github.com/hashicorp/packer-plugin-sdk v0.1.2/go.mod h1:KRjczE1/c9NV5Re+PXt3myJsVTI/FxEHpZjRjOH0Fug=
github.com/hashicorp/packer-plugin-sdk v0.1.3-0.20210407132324-af39c7839daf h1:0DBlIExTDefzbfkOl213QtgJsVJXWdgW/aIQIvYUMzs=
github.com/hashicorp/packer-plugin-sdk v0.1.3-0.20210407132324-af39c7839daf/go.mod h1:xePpgQgQYv/bamiypx3hH9ukidxDdcN8q0R0wLi8IEQ=
github.com/hashicorp/packer-plugin-vsphere v0.0.0-20210409154313-881202ca3ffe h1:qUU0ctLV5Pjq+RqxaKlVmKhE1BQtWZvqV4a05cF93PY=
github.com/hashicorp/packer-plugin-vsphere v0.0.0-20210409154313-881202ca3ffe/go.mod h1:ZmA5O2CDiIOGW/hvHbhxNzq2hEXDYyUVj+knuoxyWu0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0=
github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
@ -695,8 +697,9 @@ github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmware/govmomi v0.23.1 h1:vU09hxnNR/I7e+4zCJvW+5vHu5dO64Aoe2Lw7Yi/KRg=
github.com/vmware/govmomi v0.23.1/go.mod h1:Y+Wq4lst78L85Ge/F8+ORXIWiKYqaro1vhAulACy9Lc=
github.com/vmware/govmomi v0.24.1 h1:ecVvrxF28/5g738gLTiYgc62fpGfIPRKheQ1Dj1p35w=
github.com/vmware/govmomi v0.24.1/go.mod h1:Y+Wq4lst78L85Ge/F8+ORXIWiKYqaro1vhAulACy9Lc=
github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9oS4Wk2s2u4tS29nEaDLdzvuHdB19CvSGJjPgkZJNk=
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0 h1:NJrcIkdzq0C3I8ypAZwFE9RHtGbfp+mJvqIcoFATZuk=
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0/go.mod h1:sBh287mCRwCz6zyXHMmw7sSZGPohVpnx+o+OY4M+i3A=
@ -773,8 +776,9 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20191130191448-5c0e7e404af8/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
golang.org/x/mobile v0.0.0-20201208152944-da85bec010a2 h1:3HADozU50HyrJ2jklLtr3xr0itFkz9u4LxCJhqKVdjI=
golang.org/x/mobile v0.0.0-20201208152944-da85bec010a2/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 h1:h+GZ3ubjuWaQjGe8owMGcmMVCqs0xYJtRG5y2bpHaqU=
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=

View File

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@ -0,0 +1,175 @@
package clone
import (
"context"
"fmt"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type Builder struct {
config Config
runner multistep.Runner
}
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
warnings, errs := b.config.Prepare(raws...)
if errs != nil {
return nil, warnings, errs
}
return nil, warnings, nil
}
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
state := new(multistep.BasicStateBag)
state.Put("debug", b.config.PackerDebug)
state.Put("hook", hook)
state.Put("ui", ui)
var steps []multistep.Step
steps = append(steps,
&common.StepConnect{
Config: &b.config.ConnectConfig,
},
&commonsteps.StepCreateCD{
Files: b.config.CDConfig.CDFiles,
Label: b.config.CDConfig.CDLabel,
},
&common.StepRemoteUpload{
Datastore: b.config.Datastore,
Host: b.config.Host,
SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads,
},
&StepCloneVM{
Config: &b.config.CloneConfig,
Location: &b.config.LocationConfig,
Force: b.config.PackerConfig.PackerForce,
},
&common.StepConfigureHardware{
Config: &b.config.HardwareConfig,
},
&common.StepAddCDRom{
Config: &b.config.CDRomConfig,
},
&common.StepConfigParams{
Config: &b.config.ConfigParamsConfig,
},
)
if b.config.CustomizeConfig != nil {
steps = append(steps, &StepCustomize{
Config: b.config.CustomizeConfig,
})
}
if b.config.Comm.Type != "none" {
steps = append(steps,
&commonsteps.StepCreateFloppy{
Files: b.config.FloppyFiles,
Directories: b.config.FloppyDirectories,
Label: b.config.FloppyLabel,
},
&common.StepAddFloppy{
Config: &b.config.FloppyConfig,
Datastore: b.config.Datastore,
Host: b.config.Host,
SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads,
},
&common.StepHTTPIPDiscover{
HTTPIP: b.config.BootConfig.HTTPIP,
Network: b.config.WaitIpConfig.GetIPNet(),
},
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
&common.StepSshKeyPair{
Debug: b.config.PackerDebug,
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
Comm: &b.config.Comm,
},
&common.StepRun{
Config: &b.config.RunConfig,
SetOrder: false,
},
&common.StepBootCommand{
Config: &b.config.BootConfig,
Ctx: b.config.ctx,
VMName: b.config.VMName,
},
&common.StepWaitForIp{
Config: &b.config.WaitIpConfig,
},
&communicator.StepConnect{
Config: &b.config.Comm,
Host: common.CommHost(b.config.Comm.Host()),
SSHConfig: b.config.Comm.SSHConfigFunc(),
},
&commonsteps.StepProvision{},
&common.StepShutdown{
Config: &b.config.ShutdownConfig,
},
&common.StepRemoveFloppy{
Datastore: b.config.Datastore,
Host: b.config.Host,
},
)
}
steps = append(steps,
&common.StepRemoveCDRom{
Config: &b.config.RemoveCDRomConfig,
},
&common.StepCreateSnapshot{
CreateSnapshot: b.config.CreateSnapshot,
},
&common.StepConvertToTemplate{
ConvertToTemplate: b.config.ConvertToTemplate,
},
)
if b.config.ContentLibraryDestinationConfig != nil {
steps = append(steps, &common.StepImportToContentLibrary{
ContentLibConfig: b.config.ContentLibraryDestinationConfig,
})
}
if b.config.Export != nil {
steps = append(steps, &common.StepExport{
Name: b.config.Export.Name,
Force: b.config.Export.Force,
Images: b.config.Export.Images,
Manifest: b.config.Export.Manifest,
OutputDir: b.config.Export.OutputDir.OutputDir,
Options: b.config.Export.Options,
})
}
b.runner = commonsteps.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
b.runner.Run(ctx, state)
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
if _, ok := state.GetOk("vm"); !ok {
return nil, nil
}
artifact := &common.Artifact{
Name: b.config.VMName,
VM: state.Get("vm").(*driver.VirtualMachineDriver),
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
}
if b.config.Export != nil {
artifact.Outconfig = &b.config.Export.OutputDir
}
return artifact, nil
}

View File

@ -0,0 +1,104 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type Config
package clone
import (
packerCommon "github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
)
type Config struct {
packerCommon.PackerConfig `mapstructure:",squash"`
commonsteps.HTTPConfig `mapstructure:",squash"`
commonsteps.CDConfig `mapstructure:",squash"`
common.ConnectConfig `mapstructure:",squash"`
CloneConfig `mapstructure:",squash"`
common.LocationConfig `mapstructure:",squash"`
common.HardwareConfig `mapstructure:",squash"`
common.ConfigParamsConfig `mapstructure:",squash"`
common.CDRomConfig `mapstructure:",squash"`
common.RemoveCDRomConfig `mapstructure:",squash"`
common.FloppyConfig `mapstructure:",squash"`
common.RunConfig `mapstructure:",squash"`
common.BootConfig `mapstructure:",squash"`
common.WaitIpConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
common.ShutdownConfig `mapstructure:",squash"`
// Create a snapshot when set to `true`, so the VM can be used as a base
// for linked clones. Defaults to `false`.
CreateSnapshot bool `mapstructure:"create_snapshot"`
// Convert VM to a template. Defaults to `false`.
ConvertToTemplate bool `mapstructure:"convert_to_template"`
// Configuration for exporting VM to an ovf file.
// The VM will not be exported if no [Export Configuration](#export-configuration) is specified.
Export *common.ExportConfig `mapstructure:"export"`
// Configuration for importing a VM template or OVF template to a Content Library.
// The template will not be imported if no [Content Library Import Configuration](#content-library-import-configuration) is specified.
// The import doesn't work if [convert_to_template](#convert_to_template) is set to true.
ContentLibraryDestinationConfig *common.ContentLibraryDestinationConfig `mapstructure:"content_library_destination"`
// Customize the cloned VM to configure host, network, or licensing settings. See the [customization options](#customization).
CustomizeConfig *CustomizeConfig `mapstructure:"customize"`
ctx interpolate.Context
}
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
err := config.Decode(c, &config.DecodeOpts{
PluginType: common.BuilderId,
Interpolate: true,
InterpolateContext: &c.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"boot_command",
},
},
}, raws...)
if err != nil {
return nil, err
}
// warnings := make([]string, 0)
errs := new(packersdk.MultiError)
errs = packersdk.MultiErrorAppend(errs, c.ConnectConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.CloneConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.LocationConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.HardwareConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
errs = packersdk.MultiErrorAppend(errs, c.CDRomConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.CDConfig.Prepare(&c.ctx)...)
errs = packersdk.MultiErrorAppend(errs, c.BootConfig.Prepare(&c.ctx)...)
errs = packersdk.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
_, shutdownErrs := c.ShutdownConfig.Prepare(c.Comm)
// shutdownWarnings, shutdownErrs := c.ShutdownConfig.Prepare(c.Comm)
// warnings = append(warnings, shutdownWarnings...)
errs = packersdk.MultiErrorAppend(errs, shutdownErrs...)
if c.Export != nil {
errs = packersdk.MultiErrorAppend(errs, c.Export.Prepare(&c.ctx, &c.LocationConfig, &c.PackerConfig)...)
}
if c.ContentLibraryDestinationConfig != nil {
errs = packersdk.MultiErrorAppend(errs, c.ContentLibraryDestinationConfig.Prepare(&c.LocationConfig)...)
}
if c.CustomizeConfig != nil {
errs = packersdk.MultiErrorAppend(errs, c.CustomizeConfig.Prepare()...)
}
if len(errs.Errors) > 0 {
return nil, errs
}
return nil, nil
}

View File

@ -0,0 +1,282 @@
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
package clone
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"`
CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"`
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server" hcl:"vcenter_server"`
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
InsecureConnection *bool `mapstructure:"insecure_connection" cty:"insecure_connection" hcl:"insecure_connection"`
Datacenter *string `mapstructure:"datacenter" cty:"datacenter" hcl:"datacenter"`
Template *string `mapstructure:"template" cty:"template" hcl:"template"`
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
LinkedClone *bool `mapstructure:"linked_clone" cty:"linked_clone" hcl:"linked_clone"`
Network *string `mapstructure:"network" cty:"network" hcl:"network"`
MacAddress *string `mapstructure:"mac_address" cty:"mac_address" hcl:"mac_address"`
Notes *string `mapstructure:"notes" cty:"notes" hcl:"notes"`
VAppConfig *FlatvAppConfig `mapstructure:"vapp" cty:"vapp" hcl:"vapp"`
DiskControllerType []string `mapstructure:"disk_controller_type" cty:"disk_controller_type" hcl:"disk_controller_type"`
Storage []common.FlatDiskConfig `mapstructure:"storage" cty:"storage" hcl:"storage"`
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
Folder *string `mapstructure:"folder" cty:"folder" hcl:"folder"`
Cluster *string `mapstructure:"cluster" cty:"cluster" hcl:"cluster"`
Host *string `mapstructure:"host" cty:"host" hcl:"host"`
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool" hcl:"resource_pool"`
Datastore *string `mapstructure:"datastore" cty:"datastore" hcl:"datastore"`
SetHostForDatastoreUploads *bool `mapstructure:"set_host_for_datastore_uploads" cty:"set_host_for_datastore_uploads" hcl:"set_host_for_datastore_uploads"`
CPUs *int32 `mapstructure:"CPUs" cty:"CPUs" hcl:"CPUs"`
CpuCores *int32 `mapstructure:"cpu_cores" cty:"cpu_cores" hcl:"cpu_cores"`
CPUReservation *int64 `mapstructure:"CPU_reservation" cty:"CPU_reservation" hcl:"CPU_reservation"`
CPULimit *int64 `mapstructure:"CPU_limit" cty:"CPU_limit" hcl:"CPU_limit"`
CpuHotAddEnabled *bool `mapstructure:"CPU_hot_plug" cty:"CPU_hot_plug" hcl:"CPU_hot_plug"`
RAM *int64 `mapstructure:"RAM" cty:"RAM" hcl:"RAM"`
RAMReservation *int64 `mapstructure:"RAM_reservation" cty:"RAM_reservation" hcl:"RAM_reservation"`
RAMReserveAll *bool `mapstructure:"RAM_reserve_all" cty:"RAM_reserve_all" hcl:"RAM_reserve_all"`
MemoryHotAddEnabled *bool `mapstructure:"RAM_hot_plug" cty:"RAM_hot_plug" hcl:"RAM_hot_plug"`
VideoRAM *int64 `mapstructure:"video_ram" cty:"video_ram" hcl:"video_ram"`
VGPUProfile *string `mapstructure:"vgpu_profile" cty:"vgpu_profile" hcl:"vgpu_profile"`
NestedHV *bool `mapstructure:"NestedHV" cty:"NestedHV" hcl:"NestedHV"`
Firmware *string `mapstructure:"firmware" cty:"firmware" hcl:"firmware"`
ForceBIOSSetup *bool `mapstructure:"force_bios_setup" cty:"force_bios_setup" hcl:"force_bios_setup"`
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters" hcl:"configuration_parameters"`
ToolsSyncTime *bool `mapstructure:"tools_sync_time" cty:"tools_sync_time" hcl:"tools_sync_time"`
ToolsUpgradePolicy *bool `mapstructure:"tools_upgrade_policy" cty:"tools_upgrade_policy" hcl:"tools_upgrade_policy"`
CdromType *string `mapstructure:"cdrom_type" cty:"cdrom_type" hcl:"cdrom_type"`
ISOPaths []string `mapstructure:"iso_paths" cty:"iso_paths" hcl:"iso_paths"`
RemoveCdrom *bool `mapstructure:"remove_cdrom" cty:"remove_cdrom" hcl:"remove_cdrom"`
FloppyIMGPath *string `mapstructure:"floppy_img_path" cty:"floppy_img_path" hcl:"floppy_img_path"`
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
BootOrder *string `mapstructure:"boot_order" cty:"boot_order" hcl:"boot_order"`
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
HTTPIP *string `mapstructure:"http_ip" cty:"http_ip" hcl:"http_ip"`
WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout" hcl:"ip_wait_timeout"`
SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout" hcl:"ip_settle_timeout"`
WaitAddress *string `mapstructure:"ip_wait_address" cty:"ip_wait_address" hcl:"ip_wait_address"`
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
Command *string `mapstructure:"shutdown_command" cty:"shutdown_command" hcl:"shutdown_command"`
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout" hcl:"shutdown_timeout"`
DisableShutdown *bool `mapstructure:"disable_shutdown" cty:"disable_shutdown" hcl:"disable_shutdown"`
CreateSnapshot *bool `mapstructure:"create_snapshot" cty:"create_snapshot" hcl:"create_snapshot"`
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template" hcl:"convert_to_template"`
Export *common.FlatExportConfig `mapstructure:"export" cty:"export" hcl:"export"`
ContentLibraryDestinationConfig *common.FlatContentLibraryDestinationConfig `mapstructure:"content_library_destination" cty:"content_library_destination" hcl:"content_library_destination"`
CustomizeConfig *FlatCustomizeConfig `mapstructure:"customize" cty:"customize" hcl:"customize"`
}
// FlatMapstructure returns a new FlatConfig.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
"http_interface": &hcldec.AttrSpec{Name: "http_interface", Type: cty.String, Required: false},
"cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false},
"cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false},
"vcenter_server": &hcldec.AttrSpec{Name: "vcenter_server", Type: cty.String, Required: false},
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
"insecure_connection": &hcldec.AttrSpec{Name: "insecure_connection", Type: cty.Bool, Required: false},
"datacenter": &hcldec.AttrSpec{Name: "datacenter", Type: cty.String, Required: false},
"template": &hcldec.AttrSpec{Name: "template", Type: cty.String, Required: false},
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
"linked_clone": &hcldec.AttrSpec{Name: "linked_clone", Type: cty.Bool, Required: false},
"network": &hcldec.AttrSpec{Name: "network", Type: cty.String, Required: false},
"mac_address": &hcldec.AttrSpec{Name: "mac_address", Type: cty.String, Required: false},
"notes": &hcldec.AttrSpec{Name: "notes", Type: cty.String, Required: false},
"vapp": &hcldec.BlockSpec{TypeName: "vapp", Nested: hcldec.ObjectSpec((*FlatvAppConfig)(nil).HCL2Spec())},
"disk_controller_type": &hcldec.AttrSpec{Name: "disk_controller_type", Type: cty.List(cty.String), Required: false},
"storage": &hcldec.BlockListSpec{TypeName: "storage", Nested: hcldec.ObjectSpec((*common.FlatDiskConfig)(nil).HCL2Spec())},
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
"folder": &hcldec.AttrSpec{Name: "folder", Type: cty.String, Required: false},
"cluster": &hcldec.AttrSpec{Name: "cluster", Type: cty.String, Required: false},
"host": &hcldec.AttrSpec{Name: "host", Type: cty.String, Required: false},
"resource_pool": &hcldec.AttrSpec{Name: "resource_pool", Type: cty.String, Required: false},
"datastore": &hcldec.AttrSpec{Name: "datastore", Type: cty.String, Required: false},
"set_host_for_datastore_uploads": &hcldec.AttrSpec{Name: "set_host_for_datastore_uploads", Type: cty.Bool, Required: false},
"CPUs": &hcldec.AttrSpec{Name: "CPUs", Type: cty.Number, Required: false},
"cpu_cores": &hcldec.AttrSpec{Name: "cpu_cores", Type: cty.Number, Required: false},
"CPU_reservation": &hcldec.AttrSpec{Name: "CPU_reservation", Type: cty.Number, Required: false},
"CPU_limit": &hcldec.AttrSpec{Name: "CPU_limit", Type: cty.Number, Required: false},
"CPU_hot_plug": &hcldec.AttrSpec{Name: "CPU_hot_plug", Type: cty.Bool, Required: false},
"RAM": &hcldec.AttrSpec{Name: "RAM", Type: cty.Number, Required: false},
"RAM_reservation": &hcldec.AttrSpec{Name: "RAM_reservation", Type: cty.Number, Required: false},
"RAM_reserve_all": &hcldec.AttrSpec{Name: "RAM_reserve_all", Type: cty.Bool, Required: false},
"RAM_hot_plug": &hcldec.AttrSpec{Name: "RAM_hot_plug", Type: cty.Bool, Required: false},
"video_ram": &hcldec.AttrSpec{Name: "video_ram", Type: cty.Number, Required: false},
"vgpu_profile": &hcldec.AttrSpec{Name: "vgpu_profile", Type: cty.String, Required: false},
"NestedHV": &hcldec.AttrSpec{Name: "NestedHV", Type: cty.Bool, Required: false},
"firmware": &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false},
"force_bios_setup": &hcldec.AttrSpec{Name: "force_bios_setup", Type: cty.Bool, Required: false},
"configuration_parameters": &hcldec.AttrSpec{Name: "configuration_parameters", Type: cty.Map(cty.String), Required: false},
"tools_sync_time": &hcldec.AttrSpec{Name: "tools_sync_time", Type: cty.Bool, Required: false},
"tools_upgrade_policy": &hcldec.AttrSpec{Name: "tools_upgrade_policy", Type: cty.Bool, Required: false},
"cdrom_type": &hcldec.AttrSpec{Name: "cdrom_type", Type: cty.String, Required: false},
"iso_paths": &hcldec.AttrSpec{Name: "iso_paths", Type: cty.List(cty.String), Required: false},
"remove_cdrom": &hcldec.AttrSpec{Name: "remove_cdrom", Type: cty.Bool, Required: false},
"floppy_img_path": &hcldec.AttrSpec{Name: "floppy_img_path", Type: cty.String, Required: false},
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false},
"boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.String, Required: false},
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
"http_ip": &hcldec.AttrSpec{Name: "http_ip", Type: cty.String, Required: false},
"ip_wait_timeout": &hcldec.AttrSpec{Name: "ip_wait_timeout", Type: cty.String, Required: false},
"ip_settle_timeout": &hcldec.AttrSpec{Name: "ip_settle_timeout", Type: cty.String, Required: false},
"ip_wait_address": &hcldec.AttrSpec{Name: "ip_wait_address", Type: cty.String, Required: false},
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
"winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false},
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"disable_shutdown": &hcldec.AttrSpec{Name: "disable_shutdown", Type: cty.Bool, Required: false},
"create_snapshot": &hcldec.AttrSpec{Name: "create_snapshot", Type: cty.Bool, Required: false},
"convert_to_template": &hcldec.AttrSpec{Name: "convert_to_template", Type: cty.Bool, Required: false},
"export": &hcldec.BlockSpec{TypeName: "export", Nested: hcldec.ObjectSpec((*common.FlatExportConfig)(nil).HCL2Spec())},
"content_library_destination": &hcldec.BlockSpec{TypeName: "content_library_destination", Nested: hcldec.ObjectSpec((*common.FlatContentLibraryDestinationConfig)(nil).HCL2Spec())},
"customize": &hcldec.BlockSpec{TypeName: "customize", Nested: hcldec.ObjectSpec((*FlatCustomizeConfig)(nil).HCL2Spec())},
}
return s
}

View File

@ -0,0 +1,136 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type CloneConfig,vAppConfig
package clone
import (
"context"
"fmt"
"path"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type vAppConfig struct {
// Set values for the available vApp Properties to supply configuration parameters to a virtual machine cloned from
// a template that came from an imported OVF or OVA file.
//
// -> **Note:** The only supported usage path for vApp properties is for existing user-configurable keys.
// These generally come from an existing template that was created from an imported OVF or OVA file.
// You cannot set values for vApp properties on virtual machines created from scratch,
// virtual machines lacking a vApp configuration, or on property keys that do not exist.
Properties map[string]string `mapstructure:"properties"`
}
type CloneConfig struct {
// Name of source VM. Path is optional.
Template string `mapstructure:"template"`
// The size of the disk in MB.
DiskSize int64 `mapstructure:"disk_size"`
// Create VM as a linked clone from latest snapshot. Defaults to `false`.
LinkedClone bool `mapstructure:"linked_clone"`
// Set the network in which the VM will be connected to. If no network is
// specified, `host` must be specified to allow Packer to look for the
// available network. If the network is inside a network folder in vCenter,
// you need to provide the full path to the network.
Network string `mapstructure:"network"`
// Sets a custom Mac Address to the network adapter. If set, the [network](#network) must be also specified.
MacAddress string `mapstructure:"mac_address"`
// VM notes.
Notes string `mapstructure:"notes"`
// Set the vApp Options to a virtual machine.
// See the [vApp Options Configuration](/docs/builders/vmware/vsphere-clone#vapp-options-configuration)
// to know the available options and how to use it.
VAppConfig vAppConfig `mapstructure:"vapp"`
StorageConfig common.StorageConfig `mapstructure:",squash"`
}
func (c *CloneConfig) Prepare() []error {
var errs []error
errs = append(errs, c.StorageConfig.Prepare()...)
if c.Template == "" {
errs = append(errs, fmt.Errorf("'template' is required"))
}
if c.LinkedClone == true && c.DiskSize != 0 {
errs = append(errs, fmt.Errorf("'linked_clone' and 'disk_size' cannot be used together"))
}
if c.MacAddress != "" && c.Network == "" {
errs = append(errs, fmt.Errorf("'network' is required when 'mac_address' is specified"))
}
return errs
}
type StepCloneVM struct {
Config *CloneConfig
Location *common.LocationConfig
Force bool
}
func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
d := state.Get("driver").(driver.Driver)
vmPath := path.Join(s.Location.Folder, s.Location.VMName)
err := d.PreCleanVM(ui, vmPath, s.Force)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
ui.Say("Cloning VM...")
template, err := d.FindVM(s.Config.Template)
if err != nil {
state.Put("error", fmt.Errorf("Error finding vm to clone: %s", err))
return multistep.ActionHalt
}
var disks []driver.Disk
for _, disk := range s.Config.StorageConfig.Storage {
disks = append(disks, driver.Disk{
DiskSize: disk.DiskSize,
DiskEagerlyScrub: disk.DiskEagerlyScrub,
DiskThinProvisioned: disk.DiskThinProvisioned,
ControllerIndex: disk.DiskControllerIndex,
})
}
vm, err := template.Clone(ctx, &driver.CloneConfig{
Name: s.Location.VMName,
Folder: s.Location.Folder,
Cluster: s.Location.Cluster,
Host: s.Location.Host,
ResourcePool: s.Location.ResourcePool,
Datastore: s.Location.Datastore,
LinkedClone: s.Config.LinkedClone,
Network: s.Config.Network,
MacAddress: s.Config.MacAddress,
Annotation: s.Config.Notes,
VAppProperties: s.Config.VAppConfig.Properties,
PrimaryDiskSize: s.Config.DiskSize,
StorageConfig: driver.StorageConfig{
DiskControllerType: s.Config.StorageConfig.DiskControllerType,
Storage: disks,
},
})
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
if vm == nil {
return multistep.ActionHalt
}
state.Put("vm", vm)
return multistep.ActionContinue
}
func (s *StepCloneVM) Cleanup(state multistep.StateBag) {
common.CleanupVM(state)
}

View File

@ -0,0 +1,71 @@
// Code generated by "mapstructure-to-hcl2 -type CloneConfig,vAppConfig"; DO NOT EDIT.
package clone
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
"github.com/zclconf/go-cty/cty"
)
// FlatCloneConfig is an auto-generated flat version of CloneConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatCloneConfig struct {
Template *string `mapstructure:"template" cty:"template" hcl:"template"`
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
LinkedClone *bool `mapstructure:"linked_clone" cty:"linked_clone" hcl:"linked_clone"`
Network *string `mapstructure:"network" cty:"network" hcl:"network"`
MacAddress *string `mapstructure:"mac_address" cty:"mac_address" hcl:"mac_address"`
Notes *string `mapstructure:"notes" cty:"notes" hcl:"notes"`
VAppConfig *FlatvAppConfig `mapstructure:"vapp" cty:"vapp" hcl:"vapp"`
DiskControllerType []string `mapstructure:"disk_controller_type" cty:"disk_controller_type" hcl:"disk_controller_type"`
Storage []common.FlatDiskConfig `mapstructure:"storage" cty:"storage" hcl:"storage"`
}
// FlatMapstructure returns a new FlatCloneConfig.
// FlatCloneConfig is an auto-generated flat version of CloneConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*CloneConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatCloneConfig)
}
// HCL2Spec returns the hcl spec of a CloneConfig.
// This spec is used by HCL to read the fields of CloneConfig.
// The decoded values from this spec will then be applied to a FlatCloneConfig.
func (*FlatCloneConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"template": &hcldec.AttrSpec{Name: "template", Type: cty.String, Required: false},
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
"linked_clone": &hcldec.AttrSpec{Name: "linked_clone", Type: cty.Bool, Required: false},
"network": &hcldec.AttrSpec{Name: "network", Type: cty.String, Required: false},
"mac_address": &hcldec.AttrSpec{Name: "mac_address", Type: cty.String, Required: false},
"notes": &hcldec.AttrSpec{Name: "notes", Type: cty.String, Required: false},
"vapp": &hcldec.BlockSpec{TypeName: "vapp", Nested: hcldec.ObjectSpec((*FlatvAppConfig)(nil).HCL2Spec())},
"disk_controller_type": &hcldec.AttrSpec{Name: "disk_controller_type", Type: cty.List(cty.String), Required: false},
"storage": &hcldec.BlockListSpec{TypeName: "storage", Nested: hcldec.ObjectSpec((*common.FlatDiskConfig)(nil).HCL2Spec())},
}
return s
}
// FlatvAppConfig is an auto-generated flat version of vAppConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatvAppConfig struct {
Properties map[string]string `mapstructure:"properties" cty:"properties" hcl:"properties"`
}
// FlatMapstructure returns a new FlatvAppConfig.
// FlatvAppConfig is an auto-generated flat version of vAppConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*vAppConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatvAppConfig)
}
// HCL2Spec returns the hcl spec of a vAppConfig.
// This spec is used by HCL to read the fields of vAppConfig.
// The decoded values from this spec will then be applied to a FlatvAppConfig.
func (*FlatvAppConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"properties": &hcldec.AttrSpec{Name: "properties", Type: cty.Map(cty.String), Required: false},
}
return s
}

View File

@ -0,0 +1,276 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type CustomizeConfig,LinuxOptions,NetworkInterfaces,NetworkInterface,GlobalDnsSettings,GlobalRoutingSettings
package clone
import (
"context"
"fmt"
"io/ioutil"
"net"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
"github.com/vmware/govmomi/vim25/types"
)
// A cloned virtual machine can be [customized](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vm_admin.doc/GUID-58E346FF-83AE-42B8-BE58-253641D257BC.html)
// to configure host, network, or licensing settings.
//
// To perform virtual machine customization as a part of the clone process, specify the customize block with the
// respective customization options. Windows guests are customized using Sysprep, which will result in the machine SID being reset.
// Before using customization, check that your source VM meets the [requirements](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vm_admin.doc/GUID-E63B6FAA-8D35-428D-B40C-744769845906.html)
// for guest OS customization on vSphere.
// See the [customization example](#customization-example) for a usage synopsis.
//
// The settings for customize are as follows:
type CustomizeConfig struct {
// Settings to Linux guest OS customization. See [Linux customization settings](#linux-customization-settings).
LinuxOptions *LinuxOptions `mapstructure:"linux_options"`
// Supply your own sysprep.xml file to allow full control of the customization process out-of-band of vSphere.
WindowsSysPrepFile string `mapstructure:"windows_sysprep_file"`
// Configure network interfaces on a per-interface basis that should matched up to the network adapters present in the VM.
// To use DHCP, declare an empty network_interface for each adapter being configured. This field is required.
// See [Network interface settings](#network-interface-settings).
NetworkInterfaces NetworkInterfaces `mapstructure:"network_interface"`
GlobalRoutingSettings `mapstructure:",squash"`
GlobalDnsSettings `mapstructure:",squash"`
}
type LinuxOptions struct {
// The domain name for this machine. This, along with [host_name](#host_name), make up the FQDN of this virtual machine.
Domain string `mapstructure:"domain"`
// The host name for this machine. This, along with [domain](#domain), make up the FQDN of this virtual machine.
Hostname string `mapstructure:"host_name"`
// Tells the operating system that the hardware clock is set to UTC. Default: true.
HWClockUTC config.Trilean `mapstructure:"hw_clock_utc"`
// Sets the time zone. The default is UTC.
Timezone string `mapstructure:"time_zone"`
}
type NetworkInterface struct {
// Network interface-specific DNS server settings for Windows operating systems.
// Ignored on Linux and possibly other operating systems - for those systems, please see the [global DNS settings](#global-dns-settings) section.
DnsServerList []string `mapstructure:"dns_server_list"`
// Network interface-specific DNS search domain for Windows operating systems.
// Ignored on Linux and possibly other operating systems - for those systems, please see the [global DNS settings](#global-dns-settings) section.
DnsDomain string `mapstructure:"dns_domain"`
// The IPv4 address assigned to this network adapter. If left blank or not included, DHCP is used.
Ipv4Address string `mapstructure:"ipv4_address"`
// The IPv4 subnet mask, in bits (example: 24 for 255.255.255.0).
Ipv4NetMask int `mapstructure:"ipv4_netmask"`
// The IPv6 address assigned to this network adapter. If left blank or not included, auto-configuration is used.
Ipv6Address string `mapstructure:"ipv6_address"`
// The IPv6 subnet mask, in bits (example: 32).
Ipv6NetMask int `mapstructure:"ipv6_netmask"`
}
type NetworkInterfaces []NetworkInterface
// The settings here must match the IP/mask of at least one network_interface supplied to customization.
type GlobalRoutingSettings struct {
// The IPv4 default gateway when using network_interface customization on the virtual machine.
Ipv4Gateway string `mapstructure:"ipv4_gateway"`
// The IPv6 default gateway when using network_interface customization on the virtual machine.
Ipv6Gateway string `mapstructure:"ipv6_gateway"`
}
// The following settings configure DNS globally, generally for Linux systems. For Windows systems,
// this is done per-interface, see [network interface](#network_interface) settings.
type GlobalDnsSettings struct {
// The list of DNS servers to configure on a virtual machine.
DnsServerList []string `mapstructure:"dns_server_list"`
// A list of DNS search domains to add to the DNS configuration on the virtual machine.
DnsSuffixList []string `mapstructure:"dns_suffix_list"`
}
type StepCustomize struct {
Config *CustomizeConfig
}
func (c *CustomizeConfig) Prepare() []error {
var errs []error
if c.LinuxOptions == nil && c.WindowsSysPrepFile == "" {
errs = append(errs, fmt.Errorf("customize is empty"))
}
if c.LinuxOptions != nil && c.WindowsSysPrepFile != "" {
errs = append(errs, fmt.Errorf("`linux_options` and `windows_sysprep_text` both set - one must not be included if the other is specified"))
}
if c.LinuxOptions != nil {
if c.LinuxOptions.Hostname == "" {
errs = append(errs, fmt.Errorf("linux options `host_name` is empty"))
}
if c.LinuxOptions.Domain == "" {
errs = append(errs, fmt.Errorf("linux options `domain` is empty"))
}
if c.LinuxOptions.HWClockUTC == config.TriUnset {
c.LinuxOptions.HWClockUTC = config.TriTrue
}
if c.LinuxOptions.Timezone == "" {
c.LinuxOptions.Timezone = "UTC"
}
}
if len(c.NetworkInterfaces) == 0 {
errs = append(errs, fmt.Errorf("one or more `network_interface` must be provided"))
}
return errs
}
func (s *StepCustomize) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
vm := state.Get("vm").(*driver.VirtualMachineDriver)
ui := state.Get("ui").(packersdk.Ui)
identity, err := s.identitySettings()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
nicSettingsMap := s.nicSettingsMap()
globalIpSettings := s.globalIpSettings()
spec := types.CustomizationSpec{
Identity: identity,
NicSettingMap: nicSettingsMap,
GlobalIPSettings: globalIpSettings,
}
ui.Say("Customizing VM...")
err = vm.Customize(spec)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepCustomize) identitySettings() (types.BaseCustomizationIdentitySettings, error) {
if s.Config.LinuxOptions != nil {
return &types.CustomizationLinuxPrep{
HostName: &types.CustomizationFixedName{
Name: s.Config.LinuxOptions.Hostname,
},
Domain: s.Config.LinuxOptions.Domain,
TimeZone: s.Config.LinuxOptions.Timezone,
HwClockUTC: s.Config.LinuxOptions.HWClockUTC.ToBoolPointer(),
}, nil
}
if s.Config.WindowsSysPrepFile != "" {
sysPrep, err := ioutil.ReadFile(s.Config.WindowsSysPrepFile)
if err != nil {
return nil, fmt.Errorf("error on reading %s: %s", s.Config.WindowsSysPrepFile, err)
}
return &types.CustomizationSysprepText{
Value: string(sysPrep),
}, nil
}
return nil, fmt.Errorf("no customization identity found")
}
func (s *StepCustomize) nicSettingsMap() []types.CustomizationAdapterMapping {
result := make([]types.CustomizationAdapterMapping, len(s.Config.NetworkInterfaces))
var ipv4gwFound, ipv6gwFound bool
for i := range s.Config.NetworkInterfaces {
var adapter types.CustomizationIPSettings
adapter, ipv4gwFound, ipv6gwFound = s.ipSettings(i, !ipv4gwFound, !ipv6gwFound)
obj := types.CustomizationAdapterMapping{
Adapter: adapter,
}
result[i] = obj
}
return result
}
func (s *StepCustomize) ipSettings(n int, ipv4gwAdd bool, ipv6gwAdd bool) (types.CustomizationIPSettings, bool, bool) {
var v4gwFound, v6gwFound bool
var obj types.CustomizationIPSettings
ipv4Address := s.Config.NetworkInterfaces[n].Ipv4Address
if ipv4Address != "" {
ipv4mask := s.Config.NetworkInterfaces[n].Ipv4NetMask
ipv4Gateway := s.Config.Ipv4Gateway
obj.Ip = &types.CustomizationFixedIp{
IpAddress: ipv4Address,
}
obj.SubnetMask = v4CIDRMaskToDotted(ipv4mask)
// Check for the gateway
if ipv4gwAdd && ipv4Gateway != "" && matchGateway(ipv4Address, ipv4mask, ipv4Gateway) {
obj.Gateway = []string{ipv4Gateway}
v4gwFound = true
}
} else {
obj.Ip = &types.CustomizationDhcpIpGenerator{}
}
obj.DnsServerList = s.Config.NetworkInterfaces[n].DnsServerList
obj.DnsDomain = s.Config.NetworkInterfaces[n].DnsDomain
obj.IpV6Spec, v6gwFound = s.IPSettingsIPV6Address(n, ipv6gwAdd)
return obj, v4gwFound, v6gwFound
}
func v4CIDRMaskToDotted(mask int) string {
m := net.CIDRMask(mask, 32)
a := int(m[0])
b := int(m[1])
c := int(m[2])
d := int(m[3])
return fmt.Sprintf("%d.%d.%d.%d", a, b, c, d)
}
func (s *StepCustomize) IPSettingsIPV6Address(n int, gwAdd bool) (*types.CustomizationIPSettingsIpV6AddressSpec, bool) {
addr := s.Config.NetworkInterfaces[n].Ipv6Address
var gwFound bool
if addr == "" {
return nil, gwFound
}
mask := s.Config.NetworkInterfaces[n].Ipv6NetMask
gw := s.Config.Ipv6Gateway
obj := &types.CustomizationIPSettingsIpV6AddressSpec{
Ip: []types.BaseCustomizationIpV6Generator{
&types.CustomizationFixedIpV6{
IpAddress: addr,
SubnetMask: int32(mask),
},
},
}
if gwAdd && gw != "" && matchGateway(addr, mask, gw) {
obj.Gateway = []string{gw}
gwFound = true
}
return obj, gwFound
}
// matchGateway take an IP, mask, and gateway, and checks to see if the gateway
// is reachable from the IP address.
func matchGateway(a string, m int, g string) bool {
ip := net.ParseIP(a)
gw := net.ParseIP(g)
var mask net.IPMask
if ip.To4() != nil {
mask = net.CIDRMask(m, 32)
} else {
mask = net.CIDRMask(m, 128)
}
if ip.Mask(mask).Equal(gw.Mask(mask)) {
return true
}
return false
}
func (s *StepCustomize) globalIpSettings() types.CustomizationGlobalIPSettings {
return types.CustomizationGlobalIPSettings{
DnsServerList: s.Config.DnsServerList,
DnsSuffixList: s.Config.DnsSuffixList,
}
}
func (s *StepCustomize) Cleanup(_ multistep.StateBag) {}

View File

@ -0,0 +1,155 @@
// Code generated by "mapstructure-to-hcl2 -type CustomizeConfig,LinuxOptions,NetworkInterfaces,NetworkInterface,GlobalDnsSettings,GlobalRoutingSettings"; DO NOT EDIT.
package clone
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatCustomizeConfig is an auto-generated flat version of CustomizeConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatCustomizeConfig struct {
LinuxOptions *FlatLinuxOptions `mapstructure:"linux_options" cty:"linux_options" hcl:"linux_options"`
WindowsSysPrepFile *string `mapstructure:"windows_sysprep_file" cty:"windows_sysprep_file" hcl:"windows_sysprep_file"`
NetworkInterfaces []FlatNetworkInterface `mapstructure:"network_interface" cty:"network_interface" hcl:"network_interface"`
Ipv4Gateway *string `mapstructure:"ipv4_gateway" cty:"ipv4_gateway" hcl:"ipv4_gateway"`
Ipv6Gateway *string `mapstructure:"ipv6_gateway" cty:"ipv6_gateway" hcl:"ipv6_gateway"`
DnsServerList []string `mapstructure:"dns_server_list" cty:"dns_server_list" hcl:"dns_server_list"`
DnsSuffixList []string `mapstructure:"dns_suffix_list" cty:"dns_suffix_list" hcl:"dns_suffix_list"`
}
// FlatMapstructure returns a new FlatCustomizeConfig.
// FlatCustomizeConfig is an auto-generated flat version of CustomizeConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*CustomizeConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatCustomizeConfig)
}
// HCL2Spec returns the hcl spec of a CustomizeConfig.
// This spec is used by HCL to read the fields of CustomizeConfig.
// The decoded values from this spec will then be applied to a FlatCustomizeConfig.
func (*FlatCustomizeConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"linux_options": &hcldec.BlockSpec{TypeName: "linux_options", Nested: hcldec.ObjectSpec((*FlatLinuxOptions)(nil).HCL2Spec())},
"windows_sysprep_file": &hcldec.AttrSpec{Name: "windows_sysprep_file", Type: cty.String, Required: false},
"network_interface": &hcldec.BlockListSpec{TypeName: "network_interface", Nested: hcldec.ObjectSpec((*FlatNetworkInterface)(nil).HCL2Spec())},
"ipv4_gateway": &hcldec.AttrSpec{Name: "ipv4_gateway", Type: cty.String, Required: false},
"ipv6_gateway": &hcldec.AttrSpec{Name: "ipv6_gateway", Type: cty.String, Required: false},
"dns_server_list": &hcldec.AttrSpec{Name: "dns_server_list", Type: cty.List(cty.String), Required: false},
"dns_suffix_list": &hcldec.AttrSpec{Name: "dns_suffix_list", Type: cty.List(cty.String), Required: false},
}
return s
}
// FlatGlobalDnsSettings is an auto-generated flat version of GlobalDnsSettings.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatGlobalDnsSettings struct {
DnsServerList []string `mapstructure:"dns_server_list" cty:"dns_server_list" hcl:"dns_server_list"`
DnsSuffixList []string `mapstructure:"dns_suffix_list" cty:"dns_suffix_list" hcl:"dns_suffix_list"`
}
// FlatMapstructure returns a new FlatGlobalDnsSettings.
// FlatGlobalDnsSettings is an auto-generated flat version of GlobalDnsSettings.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*GlobalDnsSettings) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatGlobalDnsSettings)
}
// HCL2Spec returns the hcl spec of a GlobalDnsSettings.
// This spec is used by HCL to read the fields of GlobalDnsSettings.
// The decoded values from this spec will then be applied to a FlatGlobalDnsSettings.
func (*FlatGlobalDnsSettings) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"dns_server_list": &hcldec.AttrSpec{Name: "dns_server_list", Type: cty.List(cty.String), Required: false},
"dns_suffix_list": &hcldec.AttrSpec{Name: "dns_suffix_list", Type: cty.List(cty.String), Required: false},
}
return s
}
// FlatGlobalRoutingSettings is an auto-generated flat version of GlobalRoutingSettings.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatGlobalRoutingSettings struct {
Ipv4Gateway *string `mapstructure:"ipv4_gateway" cty:"ipv4_gateway" hcl:"ipv4_gateway"`
Ipv6Gateway *string `mapstructure:"ipv6_gateway" cty:"ipv6_gateway" hcl:"ipv6_gateway"`
}
// FlatMapstructure returns a new FlatGlobalRoutingSettings.
// FlatGlobalRoutingSettings is an auto-generated flat version of GlobalRoutingSettings.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*GlobalRoutingSettings) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatGlobalRoutingSettings)
}
// HCL2Spec returns the hcl spec of a GlobalRoutingSettings.
// This spec is used by HCL to read the fields of GlobalRoutingSettings.
// The decoded values from this spec will then be applied to a FlatGlobalRoutingSettings.
func (*FlatGlobalRoutingSettings) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"ipv4_gateway": &hcldec.AttrSpec{Name: "ipv4_gateway", Type: cty.String, Required: false},
"ipv6_gateway": &hcldec.AttrSpec{Name: "ipv6_gateway", Type: cty.String, Required: false},
}
return s
}
// FlatLinuxOptions is an auto-generated flat version of LinuxOptions.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatLinuxOptions struct {
Domain *string `mapstructure:"domain" cty:"domain" hcl:"domain"`
Hostname *string `mapstructure:"host_name" cty:"host_name" hcl:"host_name"`
HWClockUTC *bool `mapstructure:"hw_clock_utc" cty:"hw_clock_utc" hcl:"hw_clock_utc"`
Timezone *string `mapstructure:"time_zone" cty:"time_zone" hcl:"time_zone"`
}
// FlatMapstructure returns a new FlatLinuxOptions.
// FlatLinuxOptions is an auto-generated flat version of LinuxOptions.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*LinuxOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatLinuxOptions)
}
// HCL2Spec returns the hcl spec of a LinuxOptions.
// This spec is used by HCL to read the fields of LinuxOptions.
// The decoded values from this spec will then be applied to a FlatLinuxOptions.
func (*FlatLinuxOptions) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"domain": &hcldec.AttrSpec{Name: "domain", Type: cty.String, Required: false},
"host_name": &hcldec.AttrSpec{Name: "host_name", Type: cty.String, Required: false},
"hw_clock_utc": &hcldec.AttrSpec{Name: "hw_clock_utc", Type: cty.Bool, Required: false},
"time_zone": &hcldec.AttrSpec{Name: "time_zone", Type: cty.String, Required: false},
}
return s
}
// FlatNetworkInterface is an auto-generated flat version of NetworkInterface.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatNetworkInterface struct {
DnsServerList []string `mapstructure:"dns_server_list" cty:"dns_server_list" hcl:"dns_server_list"`
DnsDomain *string `mapstructure:"dns_domain" cty:"dns_domain" hcl:"dns_domain"`
Ipv4Address *string `mapstructure:"ipv4_address" cty:"ipv4_address" hcl:"ipv4_address"`
Ipv4NetMask *int `mapstructure:"ipv4_netmask" cty:"ipv4_netmask" hcl:"ipv4_netmask"`
Ipv6Address *string `mapstructure:"ipv6_address" cty:"ipv6_address" hcl:"ipv6_address"`
Ipv6NetMask *int `mapstructure:"ipv6_netmask" cty:"ipv6_netmask" hcl:"ipv6_netmask"`
}
// FlatMapstructure returns a new FlatNetworkInterface.
// FlatNetworkInterface is an auto-generated flat version of NetworkInterface.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*NetworkInterface) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatNetworkInterface)
}
// HCL2Spec returns the hcl spec of a NetworkInterface.
// This spec is used by HCL to read the fields of NetworkInterface.
// The decoded values from this spec will then be applied to a FlatNetworkInterface.
func (*FlatNetworkInterface) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"dns_server_list": &hcldec.AttrSpec{Name: "dns_server_list", Type: cty.List(cty.String), Required: false},
"dns_domain": &hcldec.AttrSpec{Name: "dns_domain", Type: cty.String, Required: false},
"ipv4_address": &hcldec.AttrSpec{Name: "ipv4_address", Type: cty.String, Required: false},
"ipv4_netmask": &hcldec.AttrSpec{Name: "ipv4_netmask", Type: cty.Number, Required: false},
"ipv6_address": &hcldec.AttrSpec{Name: "ipv6_address", Type: cty.String, Required: false},
"ipv6_netmask": &hcldec.AttrSpec{Name: "ipv6_netmask", Type: cty.Number, Required: false},
}
return s
}

View File

@ -0,0 +1,50 @@
package common
import (
"os"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
const BuilderId = "jetbrains.vsphere"
type Artifact struct {
Outconfig *OutputConfig
Name string
VM *driver.VirtualMachineDriver
// StateData should store data such as GeneratedData
// to be shared with post-processors
StateData map[string]interface{}
}
func (a *Artifact) BuilderId() string {
return BuilderId
}
func (a *Artifact) Files() []string {
if a.Outconfig != nil {
files, _ := a.Outconfig.ListFiles()
return files
}
return []string{}
}
func (a *Artifact) Id() string {
return a.Name
}
func (a *Artifact) String() string {
return a.Name
}
func (a *Artifact) State(name string) interface{} {
return a.StateData[name]
}
func (a *Artifact) Destroy() error {
if a.Outconfig != nil {
os.RemoveAll(a.Outconfig.OutputDir)
}
return a.VM.Destroy()
}

View File

@ -0,0 +1,30 @@
package common
import (
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
func CleanupVM(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
_, destroy := state.GetOk("destroy_vm")
if !cancelled && !halted && !destroy {
return
}
ui := state.Get("ui").(packersdk.Ui)
st := state.Get("vm")
if st == nil {
return
}
vm := st.(driver.VirtualMachine)
ui.Say("Destroying VM...")
err := vm.Destroy()
if err != nil {
ui.Error(err.Error())
}
}

View File

@ -0,0 +1,53 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type LocationConfig
package common
import (
"fmt"
"path"
"strings"
)
type LocationConfig struct {
// Name of the new VM to create.
VMName string `mapstructure:"vm_name"`
// VM folder to create the VM in.
Folder string `mapstructure:"folder"`
// ESXi cluster where target VM is created. See the
// [Working With Clusters And Hosts](#working-with-clusters-and-hosts)
// section above for more details.
Cluster string `mapstructure:"cluster"`
// ESXi host where target VM is created. A full path must be specified if
// the host is in a folder. For example `folder/host`. See the
// [Working With Clusters And Hosts](#working-with-clusters-and-hosts)
// section above for more details.
Host string `mapstructure:"host"`
// VMWare resource pool. If not set, it will look for the root resource
// pool of the `host` or `cluster`. If a root resource is not found, it
// will then look for a default resource pool.
ResourcePool string `mapstructure:"resource_pool"`
// VMWare datastore. Required if `host` is a cluster, or if `host` has
// multiple datastores.
Datastore string `mapstructure:"datastore"`
// Set this to true if packer should use the host for uploading files
// to the datastore. Defaults to false.
SetHostForDatastoreUploads bool `mapstructure:"set_host_for_datastore_uploads"`
}
func (c *LocationConfig) Prepare() []error {
var errs []error
if c.VMName == "" {
errs = append(errs, fmt.Errorf("'vm_name' is required"))
}
if c.Cluster == "" && c.Host == "" {
errs = append(errs, fmt.Errorf("'host' or 'cluster' is required"))
}
// clean Folder path and remove leading slash as folders are relative within vsphere
c.Folder = path.Clean(c.Folder)
c.Folder = strings.TrimLeft(c.Folder, "/")
return errs
}

View File

@ -0,0 +1,43 @@
// Code generated by "mapstructure-to-hcl2 -type LocationConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatLocationConfig is an auto-generated flat version of LocationConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatLocationConfig struct {
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
Folder *string `mapstructure:"folder" cty:"folder" hcl:"folder"`
Cluster *string `mapstructure:"cluster" cty:"cluster" hcl:"cluster"`
Host *string `mapstructure:"host" cty:"host" hcl:"host"`
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool" hcl:"resource_pool"`
Datastore *string `mapstructure:"datastore" cty:"datastore" hcl:"datastore"`
SetHostForDatastoreUploads *bool `mapstructure:"set_host_for_datastore_uploads" cty:"set_host_for_datastore_uploads" hcl:"set_host_for_datastore_uploads"`
}
// FlatMapstructure returns a new FlatLocationConfig.
// FlatLocationConfig is an auto-generated flat version of LocationConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*LocationConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatLocationConfig)
}
// HCL2Spec returns the hcl spec of a LocationConfig.
// This spec is used by HCL to read the fields of LocationConfig.
// The decoded values from this spec will then be applied to a FlatLocationConfig.
func (*FlatLocationConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
"folder": &hcldec.AttrSpec{Name: "folder", Type: cty.String, Required: false},
"cluster": &hcldec.AttrSpec{Name: "cluster", Type: cty.String, Required: false},
"host": &hcldec.AttrSpec{Name: "host", Type: cty.String, Required: false},
"resource_pool": &hcldec.AttrSpec{Name: "resource_pool", Type: cty.String, Required: false},
"datastore": &hcldec.AttrSpec{Name: "datastore", Type: cty.String, Required: false},
"set_host_for_datastore_uploads": &hcldec.AttrSpec{Name: "set_host_for_datastore_uploads", Type: cty.Bool, Required: false},
}
return s
}

View File

@ -0,0 +1,15 @@
package common
import (
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func CommHost(host string) func(multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
if host != "" {
return host, nil
} else {
return state.Get("ip").(string), nil
}
}
}

View File

@ -0,0 +1,59 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type OutputConfig
package common
import (
"fmt"
"os"
"path/filepath"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
type OutputConfig struct {
// This setting specifies the directory that
// artifacts from the build, such as the virtual machine files and disks,
// will be output to. The path to the directory may be relative or
// absolute. If relative, the path is relative to the working directory
// packer is executed from. This directory must not exist or, if
// created, must be empty prior to running the builder. By default this is
// "output-BUILDNAME" where "BUILDNAME" is the name of the build.
OutputDir string `mapstructure:"output_directory" required:"false"`
// The permissions to apply to the "output_directory", and to any parent
// directories that get created for output_directory. By default this is
// "0750". You should express the permission as quoted string with a
// leading zero such as "0755" in JSON file, because JSON does not support
// octal value. In Unix-like OS, the actual permission may differ from
// this value because of umask.
DirPerm os.FileMode `mapstructure:"directory_permission" required:"false"`
}
func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) []error {
if c.OutputDir == "" {
c.OutputDir = fmt.Sprintf("output-%s", pc.PackerBuildName)
}
if c.DirPerm == 0 {
c.DirPerm = 0750
}
return nil
}
// Stolen from output_dir_local.go in vmware builder.
func (c *OutputConfig) ListFiles() ([]string, error) {
files := make([]string, 0, 10)
visit := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
files = append(files, path)
}
return nil
}
return files, filepath.Walk(c.OutputDir, visit)
}

View File

@ -0,0 +1,35 @@
// Code generated by "mapstructure-to-hcl2 -type OutputConfig"; DO NOT EDIT.
package common
import (
"io/fs"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatOutputConfig is an auto-generated flat version of OutputConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatOutputConfig struct {
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"`
DirPerm *fs.FileMode `mapstructure:"directory_permission" required:"false" cty:"directory_permission" hcl:"directory_permission"`
}
// FlatMapstructure returns a new FlatOutputConfig.
// FlatOutputConfig is an auto-generated flat version of OutputConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*OutputConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatOutputConfig)
}
// HCL2Spec returns the hcl spec of a OutputConfig.
// This spec is used by HCL to read the fields of OutputConfig.
// The decoded values from this spec will then be applied to a FlatOutputConfig.
func (*FlatOutputConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
"directory_permission": &hcldec.AttrSpec{Name: "directory_permission", Type: cty.Number, Required: false},
}
return s
}

View File

@ -0,0 +1,81 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type CDRomConfig
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type CDRomConfig struct {
// Which controller to use. Example: `sata`. Defaults to `ide`.
CdromType string `mapstructure:"cdrom_type"`
// List of Datastore or Content Library paths to ISO files that will be mounted to the VM.
// Here's an HCL2 example:
// ```hcl
// iso_paths = [
// "[datastore1] ISO/ubuntu.iso",
// "Packer Library Test/ubuntu-16.04.6-server-amd64/ubuntu-16.04.6-server-amd64.iso"
// ]
// ```
ISOPaths []string `mapstructure:"iso_paths"`
}
type StepAddCDRom struct {
Config *CDRomConfig
}
func (c *CDRomConfig) Prepare() []error {
var errs []error
if c.CdromType != "" && c.CdromType != "ide" && c.CdromType != "sata" {
errs = append(errs, fmt.Errorf("'cdrom_type' must be 'ide' or 'sata'"))
}
return errs
}
func (s *StepAddCDRom) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(driver.VirtualMachine)
if s.Config.CdromType == "sata" {
if _, err := vm.FindSATAController(); err == driver.ErrNoSataController {
ui.Say("Adding SATA controller...")
if err := vm.AddSATAController(); err != nil {
state.Put("error", fmt.Errorf("error adding SATA controller: %v", err))
return multistep.ActionHalt
}
}
}
ui.Say("Mounting ISO images...")
if path, ok := state.GetOk("iso_remote_path"); ok {
if err := vm.AddCdrom(s.Config.CdromType, path.(string)); err != nil {
state.Put("error", fmt.Errorf("error mounting an image '%v': %v", path, err))
return multistep.ActionHalt
}
}
// Add our custom CD, if it exists
if cd_path, _ := state.Get("cd_path").(string); cd_path != "" {
s.Config.ISOPaths = append(s.Config.ISOPaths, cd_path)
}
if len(s.Config.ISOPaths) > 0 {
for _, path := range s.Config.ISOPaths {
if err := vm.AddCdrom(s.Config.CdromType, path); err != nil {
state.Put("error", fmt.Errorf("error mounting an image '%v': %v", path, err))
return multistep.ActionHalt
}
}
}
return multistep.ActionContinue
}
func (s *StepAddCDRom) Cleanup(state multistep.StateBag) {}

View File

@ -0,0 +1,33 @@
// Code generated by "mapstructure-to-hcl2 -type CDRomConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatCDRomConfig is an auto-generated flat version of CDRomConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatCDRomConfig struct {
CdromType *string `mapstructure:"cdrom_type" cty:"cdrom_type" hcl:"cdrom_type"`
ISOPaths []string `mapstructure:"iso_paths" cty:"iso_paths" hcl:"iso_paths"`
}
// FlatMapstructure returns a new FlatCDRomConfig.
// FlatCDRomConfig is an auto-generated flat version of CDRomConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*CDRomConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatCDRomConfig)
}
// HCL2Spec returns the hcl spec of a CDRomConfig.
// This spec is used by HCL to read the fields of CDRomConfig.
// The decoded values from this spec will then be applied to a FlatCDRomConfig.
func (*FlatCDRomConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"cdrom_type": &hcldec.AttrSpec{Name: "cdrom_type", Type: cty.String, Required: false},
"iso_paths": &hcldec.AttrSpec{Name: "iso_paths", Type: cty.List(cty.String), Required: false},
}
return s
}

View File

@ -0,0 +1,111 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type FloppyConfig
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type FloppyConfig struct {
// Datastore path to a floppy image that will be mounted to the VM.
// Example: `[datastore1] ISO/pvscsi-Windows8.flp`.
FloppyIMGPath string `mapstructure:"floppy_img_path"`
// List of local files to be mounted to the VM floppy drive. Can be used to
// make Debian preseed or RHEL kickstart files available to the VM.
FloppyFiles []string `mapstructure:"floppy_files"`
// List of directories to copy files from.
FloppyDirectories []string `mapstructure:"floppy_dirs"`
// The label to use for the floppy disk that
// is attached when the VM is booted. This is most useful for cloud-init,
// Kickstart or other early initialization tools, which can benefit from labelled floppy disks.
// By default, the floppy label will be 'packer'.
FloppyLabel string `mapstructure:"floppy_label"`
}
type StepAddFloppy struct {
Config *FloppyConfig
Datastore string
Host string
SetHostForDatastoreUploads bool
}
func (s *StepAddFloppy) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(driver.VirtualMachine)
d := state.Get("driver").(driver.Driver)
if floppyPath, ok := state.GetOk("floppy_path"); ok {
ui.Say("Uploading created floppy image")
ds, err := d.FindDatastore(s.Datastore, s.Host)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
vmDir, err := vm.GetDir()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
uploadPath := fmt.Sprintf("%v/packer-tmp-created-floppy.flp", vmDir)
if err := ds.UploadFile(floppyPath.(string), uploadPath, s.Host, s.SetHostForDatastoreUploads); err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("uploaded_floppy_path", uploadPath)
ui.Say("Adding generated Floppy...")
floppyIMGPath := ds.ResolvePath(uploadPath)
err = vm.AddFloppy(floppyIMGPath)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
}
if s.Config.FloppyIMGPath != "" {
ui.Say("Adding Floppy image...")
err := vm.AddFloppy(s.Config.FloppyIMGPath)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *StepAddFloppy) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
ui := state.Get("ui").(packersdk.Ui)
d := state.Get("driver").(driver.Driver)
if UploadedFloppyPath, ok := state.GetOk("uploaded_floppy_path"); ok {
ui.Say("Deleting Floppy image ...")
ds, err := d.FindDatastore(s.Datastore, s.Host)
if err != nil {
state.Put("error", err)
return
}
err = ds.Delete(UploadedFloppyPath.(string))
if err != nil {
state.Put("error", err)
return
}
}
}

View File

@ -0,0 +1,37 @@
// Code generated by "mapstructure-to-hcl2 -type FloppyConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatFloppyConfig is an auto-generated flat version of FloppyConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatFloppyConfig struct {
FloppyIMGPath *string `mapstructure:"floppy_img_path" cty:"floppy_img_path" hcl:"floppy_img_path"`
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
}
// FlatMapstructure returns a new FlatFloppyConfig.
// FlatFloppyConfig is an auto-generated flat version of FloppyConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*FloppyConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatFloppyConfig)
}
// HCL2Spec returns the hcl spec of a FloppyConfig.
// This spec is used by HCL to read the fields of FloppyConfig.
// The decoded values from this spec will then be applied to a FlatFloppyConfig.
func (*FlatFloppyConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"floppy_img_path": &hcldec.AttrSpec{Name: "floppy_img_path", Type: cty.String, Required: false},
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false},
}
return s
}

View File

@ -0,0 +1,152 @@
//go:generate struct-markdown
package common
import (
"context"
"fmt"
"time"
"github.com/hashicorp/packer-plugin-sdk/bootcommand"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
"golang.org/x/mobile/event/key"
)
type BootConfig struct {
bootcommand.BootConfig `mapstructure:",squash"`
// The IP address to use for the HTTP server started to serve the `http_directory`.
// If unset, Packer will automatically discover and assign an IP.
HTTPIP string `mapstructure:"http_ip"`
}
type bootCommandTemplateData struct {
HTTPIP string
HTTPPort int
Name string
}
func (c *BootConfig) Prepare(ctx *interpolate.Context) []error {
if c.BootWait == 0 {
c.BootWait = 10 * time.Second
}
return c.BootConfig.Prepare(ctx)
}
type StepBootCommand struct {
Config *BootConfig
VMName string
Ctx interpolate.Context
}
func (s *StepBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
debug := state.Get("debug").(bool)
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver)
if s.Config.BootCommand == nil {
return multistep.ActionContinue
}
// Wait the for the vm to boot.
if int64(s.Config.BootWait) > 0 {
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.Config.BootWait.String()))
select {
case <-time.After(s.Config.BootWait):
break
case <-ctx.Done():
return multistep.ActionHalt
}
}
var pauseFn multistep.DebugPauseFn
if debug {
pauseFn = state.Get("pauseFn").(multistep.DebugPauseFn)
}
port := state.Get("http_port").(int)
if port > 0 {
ip := state.Get("http_ip").(string)
s.Ctx.Data = &bootCommandTemplateData{
ip,
port,
s.VMName,
}
ui.Say(fmt.Sprintf("HTTP server is working at http://%v:%v/", ip, port))
}
var keyAlt, keyCtrl, keyShift bool
sendCodes := func(code key.Code, down bool) error {
switch code {
case key.CodeLeftAlt:
keyAlt = down
case key.CodeLeftControl:
keyCtrl = down
case key.CodeLeftShift:
keyShift = down
}
shift := down
if keyShift {
shift = keyShift
}
_, err := vm.TypeOnKeyboard(driver.KeyInput{
Scancode: code,
Ctrl: keyCtrl,
Alt: keyAlt,
Shift: shift,
})
if err != nil {
// retry once if error
ui.Error(fmt.Errorf("error typing a boot command (code, down) `%d, %t`: %w", code, down, err).Error())
ui.Say("trying key input again")
time.Sleep(s.Config.BootGroupInterval)
_, err = vm.TypeOnKeyboard(driver.KeyInput{
Scancode: code,
Ctrl: keyCtrl,
Alt: keyAlt,
Shift: shift,
})
if err != nil {
return fmt.Errorf("error typing a boot command (code, down) `%d, %t`: %w", code, down, err)
}
}
return nil
}
d := bootcommand.NewUSBDriver(sendCodes, s.Config.BootGroupInterval)
ui.Say("Typing boot command...")
flatBootCommand := s.Config.FlatBootCommand()
command, err := interpolate.Render(flatBootCommand, &s.Ctx)
if err != nil {
err := fmt.Errorf("Error preparing boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
seq, err := bootcommand.GenerateExpressionSequence(command)
if err != nil {
err := fmt.Errorf("Error generating boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err := seq.Do(ctx, d); err != nil {
err := fmt.Errorf("Error running boot command: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if pauseFn != nil {
pauseFn(multistep.DebugLocationAfterRun, fmt.Sprintf("boot_command: %s", command), state)
}
return multistep.ActionContinue
}
func (s *StepBootCommand) Cleanup(_ multistep.StateBag) {}

View File

@ -0,0 +1,65 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type ConfigParamsConfig
package common
import (
"context"
"fmt"
"github.com/vmware/govmomi/vim25/types"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type ConfigParamsConfig struct {
// configuration_parameters is a direct passthrough to the VSphere API's
// ConfigSpec: https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.ConfigSpec.html
ConfigParams map[string]string `mapstructure:"configuration_parameters"`
// Enables time synchronization with the host. Defaults to false.
ToolsSyncTime bool `mapstructure:"tools_sync_time"`
// If sets to true, vSphere will automatically check and upgrade VMware Tools upon a system power cycle.
// If not set, defaults to manual upgrade.
ToolsUpgradePolicy bool `mapstructure:"tools_upgrade_policy"`
}
type StepConfigParams struct {
Config *ConfigParamsConfig
}
func (s *StepConfigParams) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver)
configParams := make(map[string]string)
if s.Config.ConfigParams != nil {
configParams = s.Config.ConfigParams
}
var info *types.ToolsConfigInfo
if s.Config.ToolsSyncTime || s.Config.ToolsUpgradePolicy {
info = &types.ToolsConfigInfo{}
if s.Config.ToolsSyncTime {
info.SyncTimeWithHost = &s.Config.ToolsSyncTime
}
if s.Config.ToolsUpgradePolicy {
info.ToolsUpgradePolicy = "UpgradeAtPowerCycle"
}
}
ui.Say("Adding configuration parameters...")
if err := vm.AddConfigParams(configParams, info); err != nil {
state.Put("error", fmt.Errorf("error adding configuration parameters: %v", err))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepConfigParams) Cleanup(state multistep.StateBag) {}

View File

@ -0,0 +1,35 @@
// Code generated by "mapstructure-to-hcl2 -type ConfigParamsConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatConfigParamsConfig is an auto-generated flat version of ConfigParamsConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfigParamsConfig struct {
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters" hcl:"configuration_parameters"`
ToolsSyncTime *bool `mapstructure:"tools_sync_time" cty:"tools_sync_time" hcl:"tools_sync_time"`
ToolsUpgradePolicy *bool `mapstructure:"tools_upgrade_policy" cty:"tools_upgrade_policy" hcl:"tools_upgrade_policy"`
}
// FlatMapstructure returns a new FlatConfigParamsConfig.
// FlatConfigParamsConfig is an auto-generated flat version of ConfigParamsConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*ConfigParamsConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfigParamsConfig)
}
// HCL2Spec returns the hcl spec of a ConfigParamsConfig.
// This spec is used by HCL to read the fields of ConfigParamsConfig.
// The decoded values from this spec will then be applied to a FlatConfigParamsConfig.
func (*FlatConfigParamsConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"configuration_parameters": &hcldec.AttrSpec{Name: "configuration_parameters", Type: cty.Map(cty.String), Required: false},
"tools_sync_time": &hcldec.AttrSpec{Name: "tools_sync_time", Type: cty.Bool, Required: false},
"tools_upgrade_policy": &hcldec.AttrSpec{Name: "tools_upgrade_policy", Type: cty.Bool, Required: false},
}
return s
}

View File

@ -0,0 +1,64 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type ConnectConfig
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type ConnectConfig struct {
// vCenter server hostname.
VCenterServer string `mapstructure:"vcenter_server"`
// vSphere username.
Username string `mapstructure:"username"`
// vSphere password.
Password string `mapstructure:"password"`
// Do not validate vCenter server's TLS certificate. Defaults to `false`.
InsecureConnection bool `mapstructure:"insecure_connection"`
// VMware datacenter name. Required if there is more than one datacenter in vCenter.
Datacenter string `mapstructure:"datacenter"`
}
func (c *ConnectConfig) Prepare() []error {
var errs []error
if c.VCenterServer == "" {
errs = append(errs, fmt.Errorf("'vcenter_server' is required"))
}
if c.Username == "" {
errs = append(errs, fmt.Errorf("'username' is required"))
}
if c.Password == "" {
errs = append(errs, fmt.Errorf("'password' is required"))
}
return errs
}
type StepConnect struct {
Config *ConnectConfig
}
func (s *StepConnect) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
d, err := driver.NewDriver(&driver.ConnectConfig{
VCenterServer: s.Config.VCenterServer,
Username: s.Config.Username,
Password: s.Config.Password,
InsecureConnection: s.Config.InsecureConnection,
Datacenter: s.Config.Datacenter,
})
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("driver", d)
return multistep.ActionContinue
}
func (s *StepConnect) Cleanup(multistep.StateBag) {}

View File

@ -0,0 +1,39 @@
// Code generated by "mapstructure-to-hcl2 -type ConnectConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatConnectConfig is an auto-generated flat version of ConnectConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConnectConfig struct {
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server" hcl:"vcenter_server"`
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
InsecureConnection *bool `mapstructure:"insecure_connection" cty:"insecure_connection" hcl:"insecure_connection"`
Datacenter *string `mapstructure:"datacenter" cty:"datacenter" hcl:"datacenter"`
}
// FlatMapstructure returns a new FlatConnectConfig.
// FlatConnectConfig is an auto-generated flat version of ConnectConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*ConnectConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConnectConfig)
}
// HCL2Spec returns the hcl spec of a ConnectConfig.
// This spec is used by HCL to read the fields of ConnectConfig.
// The decoded values from this spec will then be applied to a FlatConnectConfig.
func (*FlatConnectConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"vcenter_server": &hcldec.AttrSpec{Name: "vcenter_server", Type: cty.String, Required: false},
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
"insecure_connection": &hcldec.AttrSpec{Name: "insecure_connection", Type: cty.Bool, Required: false},
"datacenter": &hcldec.AttrSpec{Name: "datacenter", Type: cty.String, Required: false},
}
return s
}

View File

@ -0,0 +1,68 @@
package common
import (
"context"
"fmt"
"net/url"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
// Defining this interface ensures that we use the common step download, or the
// mock created to test this wrapper
type DownloadStep interface {
Run(context.Context, multistep.StateBag) multistep.StepAction
Cleanup(multistep.StateBag)
UseSourceToFindCacheTarget(source string) (*url.URL, string, error)
}
// VSphere has a specialized need -- before we waste time downloading an iso,
// we need to check whether that iso already exists on the remote datastore.
// if it does, we skip the download. This wrapping-step still uses the common
// StepDownload but only if the image isn't already present on the datastore.
type StepDownload struct {
DownloadStep DownloadStep
// These keys are VSphere-specific and used to check the remote datastore.
Url []string
ResultKey string
Datastore string
Host string
}
func (s *StepDownload) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(driver.Driver)
ui := state.Get("ui").(packersdk.Ui)
// Check whether iso is present on remote datastore.
ds, err := driver.FindDatastore(s.Datastore, s.Host)
if err != nil {
state.Put("error", fmt.Errorf("datastore doesn't exist: %v", err))
return multistep.ActionHalt
}
// loop over URLs to see if any are already present. If they are, store that
// one instate and continue
for _, source := range s.Url {
_, targetPath, err := s.DownloadStep.UseSourceToFindCacheTarget(source)
if err != nil {
state.Put("error", fmt.Errorf("Error getting target path: %s", err))
return multistep.ActionHalt
}
_, remotePath, _, _ := GetRemoteDirectoryAndPath(targetPath, ds)
if exists := ds.FileExists(remotePath); exists {
ui.Say(fmt.Sprintf("File %s already uploaded; continuing", targetPath))
state.Put(s.ResultKey, targetPath)
return multistep.ActionContinue
}
}
// ISO is not present on datastore, so we need to download, then upload it.
// Pass through to the common download step.
return s.DownloadStep.Run(ctx, state)
}
func (s *StepDownload) Cleanup(state multistep.StateBag) {
}

View File

@ -0,0 +1,338 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type ExportConfig
package common
import (
"bytes"
"context"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
"io"
"os"
"path/filepath"
"strings"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
"github.com/pkg/errors"
"github.com/vmware/govmomi/nfc"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
// You may optionally export an ovf from VSphere to the instance running Packer.
//
// Example usage:
//
// In JSON:
// ```json
// ...
// "vm_name": "example-ubuntu",
// ...
// "export": {
// "force": true,
// "output_directory": "./output_vsphere"
// },
// ```
// In HCL2:
// ```hcl
// # ...
// vm_name = "example-ubuntu"
// # ...
// export {
// force = true
// output_directory = "./output_vsphere"
// }
// ```
// The above configuration would create the following files:
//
// ```text
// ./output_vsphere/example-ubuntu-disk-0.vmdk
// ./output_vsphere/example-ubuntu.mf
// ./output_vsphere/example-ubuntu.ovf
// ```
type ExportConfig struct {
// name of the ovf. defaults to the name of the VM
Name string `mapstructure:"name"`
// overwrite ovf if it exists
Force bool `mapstructure:"force"`
// include iso and img image files that are attached to the VM
Images bool `mapstructure:"images"`
// generate manifest using sha1, sha256, sha512. Defaults to 'sha256'. Use 'none' for no manifest.
Manifest string `mapstructure:"manifest"`
// Directory on the computer running Packer to export files to
OutputDir OutputConfig `mapstructure:",squash"`
// Advanced ovf export options. Options can include:
// * mac - MAC address is exported for all ethernet devices
// * uuid - UUID is exported for all virtual machines
// * extraconfig - all extra configuration options are exported for a virtual machine
// * nodevicesubtypes - resource subtypes for CD/DVD drives, floppy drives, and serial and parallel ports are not exported
//
// For example, adding the following export config option would output the mac addresses for all Ethernet devices in the ovf file:
//
// In JSON:
// ```json
// ...
// "export": {
// "options": ["mac"]
// },
// ```
// In HCL2:
// ```hcl
// ...
// export {
// options = ["mac"]
// }
// ```
Options []string `mapstructure:"options"`
}
var sha = map[string]func() hash.Hash{
"none": nil,
"sha1": sha1.New,
"sha256": sha256.New,
"sha512": sha512.New,
}
func (c *ExportConfig) Prepare(ctx *interpolate.Context, lc *LocationConfig, pc *common.PackerConfig) []error {
var errs *packersdk.MultiError
errs = packersdk.MultiErrorAppend(errs, c.OutputDir.Prepare(ctx, pc)...)
// manifest should default to sha256
if c.Manifest == "" {
c.Manifest = "sha256"
}
if _, ok := sha[c.Manifest]; !ok {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("unknown hash: %s. available options include available options being 'none', 'sha1', 'sha256', 'sha512'", c.Manifest))
}
if c.Name == "" {
c.Name = lc.VMName
}
target := getTarget(c.OutputDir.OutputDir, c.Name)
if !c.Force {
if _, err := os.Stat(target); err == nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("file already exists: %s", target))
}
}
if err := os.MkdirAll(c.OutputDir.OutputDir, c.OutputDir.DirPerm); err != nil {
errs = packersdk.MultiErrorAppend(errs, errors.Wrap(err, "unable to make directory for export"))
}
if errs != nil && len(errs.Errors) > 0 {
return errs.Errors
}
return nil
}
func getTarget(dir string, name string) string {
return filepath.Join(dir, name+".ovf")
}
type StepExport struct {
Name string
Force bool
Images bool
Manifest string
OutputDir string
Options []string
mf bytes.Buffer
}
func (s *StepExport) Cleanup(multistep.StateBag) {
}
func (s *StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver)
ui.Message("Starting export...")
lease, err := vm.Export()
if err != nil {
state.Put("error", errors.Wrap(err, "error exporting vm"))
return multistep.ActionHalt
}
info, err := lease.Wait(ctx, nil)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
u := lease.StartUpdater(ctx, info)
defer u.Done()
cdp := types.OvfCreateDescriptorParams{
Name: s.Name,
}
m := vm.NewOvfManager()
if len(s.Options) > 0 {
exportOptions, err := vm.GetOvfExportOptions(m)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
var unknown []string
for _, option := range s.Options {
found := false
for _, exportOpt := range exportOptions {
if exportOpt.Option == option {
found = true
break
}
}
if !found {
unknown = append(unknown, option)
}
cdp.ExportOption = append(cdp.ExportOption, option)
}
// only printing error message because the unknown options are just ignored by vcenter
if len(unknown) > 0 {
ui.Error(fmt.Sprintf("unknown export options %s", strings.Join(unknown, ",")))
}
}
for _, i := range info.Items {
if !s.include(&i) {
continue
}
if !strings.HasPrefix(i.Path, s.Name) {
i.Path = s.Name + "-" + i.Path
}
file := i.File()
ui.Message("Downloading: " + file.Path)
size, err := s.Download(ctx, lease, i)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
// Fix file size descriptor
file.Size = size
ui.Message("Exporting file: " + file.Path)
cdp.OvfFiles = append(cdp.OvfFiles, file)
}
if err = lease.Complete(ctx); err != nil {
state.Put("error", errors.Wrap(err, "unable to complete lease"))
return multistep.ActionHalt
}
desc, err := vm.CreateDescriptor(m, cdp)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to create descriptor"))
return multistep.ActionHalt
}
target := getTarget(s.OutputDir, s.Name)
file, err := os.Create(target)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to create file: "+target))
return multistep.ActionHalt
}
var w io.Writer = file
h, ok := s.newHash()
if ok {
w = io.MultiWriter(file, h)
}
ui.Message("Writing ovf...")
_, err = io.WriteString(w, desc.OvfDescriptor)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to write descriptor"))
return multistep.ActionHalt
}
if err = file.Close(); err != nil {
state.Put("error", errors.Wrap(err, "unable to close descriptor"))
return multistep.ActionHalt
}
if s.Manifest == "none" {
// manifest does not need to be created, return
return multistep.ActionContinue
}
ui.Message("Creating manifest...")
s.addHash(filepath.Base(target), h)
file, err = os.Create(filepath.Join(s.OutputDir, s.Name+".mf"))
if err != nil {
state.Put("error", errors.Wrap(err, "unable to create manifest"))
return multistep.ActionHalt
}
_, err = io.Copy(file, &s.mf)
if err != nil {
state.Put("error", errors.Wrap(err, "unable to write manifest"))
return multistep.ActionHalt
}
err = file.Close()
if err != nil {
state.Put("error", errors.Wrap(err, "unable to close file"))
return multistep.ActionHalt
}
ui.Message("Finished exporting...")
return multistep.ActionContinue
}
func (s *StepExport) include(item *nfc.FileItem) bool {
if s.Images {
return true
}
return filepath.Ext(item.Path) == ".vmdk"
}
func (s *StepExport) newHash() (hash.Hash, bool) {
// check if function is nil to handle the 'none' case
if h, ok := sha[s.Manifest]; ok && h != nil {
return h(), true
}
return nil, false
}
func (s *StepExport) addHash(p string, h hash.Hash) {
_, _ = fmt.Fprintf(&s.mf, "%s(%s)= %x\n", strings.ToUpper(s.Manifest), p, h.Sum(nil))
}
func (s *StepExport) Download(ctx context.Context, lease *nfc.Lease, item nfc.FileItem) (int64, error) {
path := filepath.Join(s.OutputDir, item.Path)
opts := soap.Download{}
if h, ok := s.newHash(); ok {
opts.Writer = h
defer s.addHash(item.Path, h)
}
err := lease.DownloadFile(ctx, path, item, opts)
if err != nil {
return 0, err
}
f, err := os.Stat(path)
if err != nil {
return 0, err
}
return f.Size(), err
}

View File

@ -0,0 +1,45 @@
// Code generated by "mapstructure-to-hcl2 -type ExportConfig"; DO NOT EDIT.
package common
import (
"io/fs"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatExportConfig is an auto-generated flat version of ExportConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatExportConfig struct {
Name *string `mapstructure:"name" cty:"name" hcl:"name"`
Force *bool `mapstructure:"force" cty:"force" hcl:"force"`
Images *bool `mapstructure:"images" cty:"images" hcl:"images"`
Manifest *string `mapstructure:"manifest" cty:"manifest" hcl:"manifest"`
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"`
DirPerm *fs.FileMode `mapstructure:"directory_permission" required:"false" cty:"directory_permission" hcl:"directory_permission"`
Options []string `mapstructure:"options" cty:"options" hcl:"options"`
}
// FlatMapstructure returns a new FlatExportConfig.
// FlatExportConfig is an auto-generated flat version of ExportConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*ExportConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatExportConfig)
}
// HCL2Spec returns the hcl spec of a ExportConfig.
// This spec is used by HCL to read the fields of ExportConfig.
// The decoded values from this spec will then be applied to a FlatExportConfig.
func (*FlatExportConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false},
"force": &hcldec.AttrSpec{Name: "force", Type: cty.Bool, Required: false},
"images": &hcldec.AttrSpec{Name: "images", Type: cty.Bool, Required: false},
"manifest": &hcldec.AttrSpec{Name: "manifest", Type: cty.String, Required: false},
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
"directory_permission": &hcldec.AttrSpec{Name: "directory_permission", Type: cty.Number, Required: false},
"options": &hcldec.AttrSpec{Name: "options", Type: cty.List(cty.String), Required: false},
}
return s
}

View File

@ -0,0 +1,98 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type HardwareConfig
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type HardwareConfig struct {
// Number of CPU cores.
CPUs int32 `mapstructure:"CPUs"`
// Number of CPU cores per socket.
CpuCores int32 `mapstructure:"cpu_cores"`
// Amount of reserved CPU resources in MHz.
CPUReservation int64 `mapstructure:"CPU_reservation"`
// Upper limit of available CPU resources in MHz.
CPULimit int64 `mapstructure:"CPU_limit"`
// Enable CPU hot plug setting for virtual machine. Defaults to `false`.
CpuHotAddEnabled bool `mapstructure:"CPU_hot_plug"`
// Amount of RAM in MB.
RAM int64 `mapstructure:"RAM"`
// Amount of reserved RAM in MB.
RAMReservation int64 `mapstructure:"RAM_reservation"`
// Reserve all available RAM. Defaults to `false`. Cannot be used together
// with `RAM_reservation`.
RAMReserveAll bool `mapstructure:"RAM_reserve_all"`
// Enable RAM hot plug setting for virtual machine. Defaults to `false`.
MemoryHotAddEnabled bool `mapstructure:"RAM_hot_plug"`
// Amount of video memory in KB.
VideoRAM int64 `mapstructure:"video_ram"`
// vGPU profile for accelerated graphics. See [NVIDIA GRID vGPU documentation](https://docs.nvidia.com/grid/latest/grid-vgpu-user-guide/index.html#configure-vmware-vsphere-vm-with-vgpu)
// for examples of profile names. Defaults to none.
VGPUProfile string `mapstructure:"vgpu_profile"`
// Enable nested hardware virtualization for VM. Defaults to `false`.
NestedHV bool `mapstructure:"NestedHV"`
// Set the Firmware for virtual machine. Supported values: `bios`, `efi` or `efi-secure`. Defaults to `bios`.
Firmware string `mapstructure:"firmware"`
// During the boot, force entry into the BIOS setup screen. Defaults to `false`.
ForceBIOSSetup bool `mapstructure:"force_bios_setup"`
}
func (c *HardwareConfig) Prepare() []error {
var errs []error
if c.RAMReservation > 0 && c.RAMReserveAll != false {
errs = append(errs, fmt.Errorf("'RAM_reservation' and 'RAM_reserve_all' cannot be used together"))
}
if c.Firmware != "" && c.Firmware != "bios" && c.Firmware != "efi" && c.Firmware != "efi-secure" {
errs = append(errs, fmt.Errorf("'firmware' must be '', 'bios', 'efi' or 'efi-secure'"))
}
return errs
}
type StepConfigureHardware struct {
Config *HardwareConfig
}
func (s *StepConfigureHardware) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(driver.VirtualMachine)
if *s.Config != (HardwareConfig{}) {
ui.Say("Customizing hardware...")
err := vm.Configure(&driver.HardwareConfig{
CPUs: s.Config.CPUs,
CpuCores: s.Config.CpuCores,
CPUReservation: s.Config.CPUReservation,
CPULimit: s.Config.CPULimit,
RAM: s.Config.RAM,
RAMReservation: s.Config.RAMReservation,
RAMReserveAll: s.Config.RAMReserveAll,
NestedHV: s.Config.NestedHV,
CpuHotAddEnabled: s.Config.CpuHotAddEnabled,
MemoryHotAddEnabled: s.Config.MemoryHotAddEnabled,
VideoRAM: s.Config.VideoRAM,
VGPUProfile: s.Config.VGPUProfile,
Firmware: s.Config.Firmware,
ForceBIOSSetup: s.Config.ForceBIOSSetup,
})
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *StepConfigureHardware) Cleanup(multistep.StateBag) {}

View File

@ -0,0 +1,57 @@
// Code generated by "mapstructure-to-hcl2 -type HardwareConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatHardwareConfig is an auto-generated flat version of HardwareConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatHardwareConfig struct {
CPUs *int32 `mapstructure:"CPUs" cty:"CPUs" hcl:"CPUs"`
CpuCores *int32 `mapstructure:"cpu_cores" cty:"cpu_cores" hcl:"cpu_cores"`
CPUReservation *int64 `mapstructure:"CPU_reservation" cty:"CPU_reservation" hcl:"CPU_reservation"`
CPULimit *int64 `mapstructure:"CPU_limit" cty:"CPU_limit" hcl:"CPU_limit"`
CpuHotAddEnabled *bool `mapstructure:"CPU_hot_plug" cty:"CPU_hot_plug" hcl:"CPU_hot_plug"`
RAM *int64 `mapstructure:"RAM" cty:"RAM" hcl:"RAM"`
RAMReservation *int64 `mapstructure:"RAM_reservation" cty:"RAM_reservation" hcl:"RAM_reservation"`
RAMReserveAll *bool `mapstructure:"RAM_reserve_all" cty:"RAM_reserve_all" hcl:"RAM_reserve_all"`
MemoryHotAddEnabled *bool `mapstructure:"RAM_hot_plug" cty:"RAM_hot_plug" hcl:"RAM_hot_plug"`
VideoRAM *int64 `mapstructure:"video_ram" cty:"video_ram" hcl:"video_ram"`
VGPUProfile *string `mapstructure:"vgpu_profile" cty:"vgpu_profile" hcl:"vgpu_profile"`
NestedHV *bool `mapstructure:"NestedHV" cty:"NestedHV" hcl:"NestedHV"`
Firmware *string `mapstructure:"firmware" cty:"firmware" hcl:"firmware"`
ForceBIOSSetup *bool `mapstructure:"force_bios_setup" cty:"force_bios_setup" hcl:"force_bios_setup"`
}
// FlatMapstructure returns a new FlatHardwareConfig.
// FlatHardwareConfig is an auto-generated flat version of HardwareConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*HardwareConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatHardwareConfig)
}
// HCL2Spec returns the hcl spec of a HardwareConfig.
// This spec is used by HCL to read the fields of HardwareConfig.
// The decoded values from this spec will then be applied to a FlatHardwareConfig.
func (*FlatHardwareConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"CPUs": &hcldec.AttrSpec{Name: "CPUs", Type: cty.Number, Required: false},
"cpu_cores": &hcldec.AttrSpec{Name: "cpu_cores", Type: cty.Number, Required: false},
"CPU_reservation": &hcldec.AttrSpec{Name: "CPU_reservation", Type: cty.Number, Required: false},
"CPU_limit": &hcldec.AttrSpec{Name: "CPU_limit", Type: cty.Number, Required: false},
"CPU_hot_plug": &hcldec.AttrSpec{Name: "CPU_hot_plug", Type: cty.Bool, Required: false},
"RAM": &hcldec.AttrSpec{Name: "RAM", Type: cty.Number, Required: false},
"RAM_reservation": &hcldec.AttrSpec{Name: "RAM_reservation", Type: cty.Number, Required: false},
"RAM_reserve_all": &hcldec.AttrSpec{Name: "RAM_reserve_all", Type: cty.Bool, Required: false},
"RAM_hot_plug": &hcldec.AttrSpec{Name: "RAM_hot_plug", Type: cty.Bool, Required: false},
"video_ram": &hcldec.AttrSpec{Name: "video_ram", Type: cty.Number, Required: false},
"vgpu_profile": &hcldec.AttrSpec{Name: "vgpu_profile", Type: cty.String, Required: false},
"NestedHV": &hcldec.AttrSpec{Name: "NestedHV", Type: cty.Bool, Required: false},
"firmware": &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false},
"force_bios_setup": &hcldec.AttrSpec{Name: "force_bios_setup", Type: cty.Bool, Required: false},
}
return s
}

View File

@ -0,0 +1,68 @@
package common
import (
"context"
"fmt"
"net"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
// Step to discover the http ip
// which guests use to reach the vm host
// To make sure the IP is set before boot command and http server steps
type StepHTTPIPDiscover struct {
HTTPIP string
Network *net.IPNet
}
func (s *StepHTTPIPDiscover) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ip, err := getHostIP(s.HTTPIP, s.Network)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("http_ip", ip)
return multistep.ActionContinue
}
func (s *StepHTTPIPDiscover) Cleanup(state multistep.StateBag) {}
func getHostIP(s string, network *net.IPNet) (string, error) {
if s != "" {
if net.ParseIP(s) != nil {
return s, nil
} else {
return "", fmt.Errorf("invalid IP address")
}
}
addrs, err := net.InterfaceAddrs()
if err != nil {
return "", err
}
// look for an IP that is contained in the ip_wait_address range
if network != nil {
for _, a := range addrs {
ipnet, ok := a.(*net.IPNet)
if ok && !ipnet.IP.IsLoopback() {
if network.Contains(ipnet.IP) {
return ipnet.IP.String(), nil
}
}
}
}
// fallback to an ipv4 address if an IP is not found in the range
for _, a := range addrs {
ipnet, ok := a.(*net.IPNet)
if ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String(), nil
}
}
}
return "", fmt.Errorf("IP not found")
}

View File

@ -0,0 +1,180 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type ContentLibraryDestinationConfig
package common
import (
"context"
"fmt"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
"github.com/vmware/govmomi/vapi/vcenter"
)
// With this configuration Packer creates a library item in a content library whose content is a VM template
// or an OVF template created from the just built VM.
// The template is stored in a existing or newly created library item.
type ContentLibraryDestinationConfig struct {
// Name of the library in which the new library item containing the template should be created/updated.
// The Content Library should be of type Local to allow deploying virtual machines.
Library string `mapstructure:"library"`
// Name of the library item that will be created or updated.
// For VM templates, the name of the item should be different from [vm_name](#vm_name) and
// the default is [vm_name](#vm_name) + timestamp when not set. VM templates will be always imported to a new library item.
// For OVF templates, the name defaults to [vm_name](#vm_name) when not set, and if an item with the same name already
// exists it will be then updated with the new OVF template, otherwise a new item will be created.
//
// ~> **Note**: It's not possible to update existing library items with a new VM template. If updating an existing library
// item is necessary, use an OVF template instead by setting the [ovf](#ovf) option as `true`.
//
Name string `mapstructure:"name"`
// Description of the library item that will be created.
// This option is not used when importing OVF templates.
// Defaults to "Packer imported [vm_name](#vm_name) VM template".
Description string `mapstructure:"description"`
// Cluster onto which the virtual machine template should be placed.
// If cluster and resource_pool are both specified, resource_pool must belong to cluster.
// If cluster and host are both specified, host must be a member of cluster.
// This option is not used when importing OVF templates.
// Defaults to [cluster](#cluster).
Cluster string `mapstructure:"cluster"`
// Virtual machine folder into which the virtual machine template should be placed.
// This option is not used when importing OVF templates.
// Defaults to the same folder as the source virtual machine.
Folder string `mapstructure:"folder"`
// Host onto which the virtual machine template should be placed.
// If host and resource_pool are both specified, resource_pool must belong to host.
// If host and cluster are both specified, host must be a member of cluster.
// This option is not used when importing OVF templates.
// Defaults to [host](#host).
Host string `mapstructure:"host"`
// Resource pool into which the virtual machine template should be placed.
// Defaults to [resource_pool](#resource_pool). if [resource_pool](#resource_pool) is also unset,
// the system will attempt to choose a suitable resource pool for the virtual machine template.
ResourcePool string `mapstructure:"resource_pool"`
// The datastore for the virtual machine template's configuration and log files.
// This option is not used when importing OVF templates.
// Defaults to the storage backing associated with the library specified by library.
Datastore string `mapstructure:"datastore"`
// If set to true, the VM will be destroyed after deploying the template to the Content Library.
// Defaults to `false`.
Destroy bool `mapstructure:"destroy"`
// When set to true, Packer will import and OVF template to the content library item. Defaults to `false`.
Ovf bool `mapstructure:"ovf"`
}
func (c *ContentLibraryDestinationConfig) Prepare(lc *LocationConfig) []error {
var errs *packersdk.MultiError
if c.Library == "" {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("a library name must be provided"))
}
if c.Ovf {
if c.Name == "" {
c.Name = lc.VMName
}
} else {
if c.Name == lc.VMName {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("the content library destination name must be different from the VM name"))
}
if c.Name == "" {
// Add timestamp to the name to differentiate from the original VM
// otherwise vSphere won't be able to create the template which will be imported
name, err := interpolate.Render(lc.VMName+"{{timestamp}}", nil)
if err != nil {
errs = packersdk.MultiErrorAppend(errs,
fmt.Errorf("unable to parse content library VM template name: %s", err))
}
c.Name = name
}
if c.Cluster == "" {
c.Cluster = lc.Cluster
}
if c.Host == "" {
c.Host = lc.Host
}
if c.ResourcePool == "" {
c.ResourcePool = lc.ResourcePool
}
if c.Description == "" {
c.Description = fmt.Sprintf("Packer imported %s VM template", lc.VMName)
}
}
if errs != nil && len(errs.Errors) > 0 {
return errs.Errors
}
return nil
}
type StepImportToContentLibrary struct {
ContentLibConfig *ContentLibraryDestinationConfig
}
func (s *StepImportToContentLibrary) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver)
var err error
if s.ContentLibConfig.Ovf {
ui.Say(fmt.Sprintf("Importing VM OVF template %s to Content Library...", s.ContentLibConfig.Name))
err = s.importOvfTemplate(vm)
} else {
ui.Say(fmt.Sprintf("Importing VM template %s to Content Library...", s.ContentLibConfig.Name))
err = s.importVmTemplate(vm)
}
if err != nil {
ui.Error(fmt.Sprintf("Failed to import template %s: %s", s.ContentLibConfig.Name, err.Error()))
state.Put("error", err)
return multistep.ActionHalt
}
if s.ContentLibConfig.Destroy {
state.Put("destroy_vm", s.ContentLibConfig.Destroy)
}
return multistep.ActionContinue
}
func (s *StepImportToContentLibrary) importOvfTemplate(vm *driver.VirtualMachineDriver) error {
ovf := vcenter.OVF{
Spec: vcenter.CreateSpec{
Name: s.ContentLibConfig.Name,
},
Target: vcenter.LibraryTarget{
LibraryID: s.ContentLibConfig.Library,
},
}
return vm.ImportOvfToContentLibrary(ovf)
}
func (s *StepImportToContentLibrary) importVmTemplate(vm *driver.VirtualMachineDriver) error {
template := vcenter.Template{
Name: s.ContentLibConfig.Name,
Description: s.ContentLibConfig.Description,
Library: s.ContentLibConfig.Library,
Placement: &vcenter.Placement{
Cluster: s.ContentLibConfig.Cluster,
Folder: s.ContentLibConfig.Folder,
Host: s.ContentLibConfig.Host,
ResourcePool: s.ContentLibConfig.ResourcePool,
},
}
if s.ContentLibConfig.Datastore != "" {
template.VMHomeStorage = &vcenter.DiskStorage{
Datastore: s.ContentLibConfig.Datastore,
}
}
return vm.ImportToContentLibrary(template)
}
func (s *StepImportToContentLibrary) Cleanup(multistep.StateBag) {
}

View File

@ -0,0 +1,49 @@
// Code generated by "mapstructure-to-hcl2 -type ContentLibraryDestinationConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatContentLibraryDestinationConfig is an auto-generated flat version of ContentLibraryDestinationConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatContentLibraryDestinationConfig struct {
Library *string `mapstructure:"library" cty:"library" hcl:"library"`
Name *string `mapstructure:"name" cty:"name" hcl:"name"`
Description *string `mapstructure:"description" cty:"description" hcl:"description"`
Cluster *string `mapstructure:"cluster" cty:"cluster" hcl:"cluster"`
Folder *string `mapstructure:"folder" cty:"folder" hcl:"folder"`
Host *string `mapstructure:"host" cty:"host" hcl:"host"`
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool" hcl:"resource_pool"`
Datastore *string `mapstructure:"datastore" cty:"datastore" hcl:"datastore"`
Destroy *bool `mapstructure:"destroy" cty:"destroy" hcl:"destroy"`
Ovf *bool `mapstructure:"ovf" cty:"ovf" hcl:"ovf"`
}
// FlatMapstructure returns a new FlatContentLibraryDestinationConfig.
// FlatContentLibraryDestinationConfig is an auto-generated flat version of ContentLibraryDestinationConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*ContentLibraryDestinationConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatContentLibraryDestinationConfig)
}
// HCL2Spec returns the hcl spec of a ContentLibraryDestinationConfig.
// This spec is used by HCL to read the fields of ContentLibraryDestinationConfig.
// The decoded values from this spec will then be applied to a FlatContentLibraryDestinationConfig.
func (*FlatContentLibraryDestinationConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"library": &hcldec.AttrSpec{Name: "library", Type: cty.String, Required: false},
"name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false},
"description": &hcldec.AttrSpec{Name: "description", Type: cty.String, Required: false},
"cluster": &hcldec.AttrSpec{Name: "cluster", Type: cty.String, Required: false},
"folder": &hcldec.AttrSpec{Name: "folder", Type: cty.String, Required: false},
"host": &hcldec.AttrSpec{Name: "host", Type: cty.String, Required: false},
"resource_pool": &hcldec.AttrSpec{Name: "resource_pool", Type: cty.String, Required: false},
"datastore": &hcldec.AttrSpec{Name: "datastore", Type: cty.String, Required: false},
"destroy": &hcldec.AttrSpec{Name: "destroy", Type: cty.Bool, Required: false},
"ovf": &hcldec.AttrSpec{Name: "ovf", Type: cty.Bool, Required: false},
}
return s
}

View File

@ -0,0 +1,117 @@
package common
import (
"context"
"fmt"
"log"
"path/filepath"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type StepRemoteUpload struct {
Datastore string
Host string
SetHostForDatastoreUploads bool
UploadedCustomCD bool
}
func (s *StepRemoteUpload) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
d := state.Get("driver").(driver.Driver)
if path, ok := state.GetOk("iso_path"); ok {
// user-supplied boot iso
fullRemotePath, err := s.uploadFile(path.(string), d, ui)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("iso_remote_path", fullRemotePath)
}
if cdPath, ok := state.GetOk("cd_path"); ok {
// Packer-created cd_files disk
fullRemotePath, err := s.uploadFile(cdPath.(string), d, ui)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
s.UploadedCustomCD = true
state.Put("cd_path", fullRemotePath)
}
return multistep.ActionContinue
}
func GetRemoteDirectoryAndPath(path string, ds driver.Datastore) (string, string, string, string) {
filename := filepath.Base(path)
remotePath := fmt.Sprintf("packer_cache/%s", filename)
remoteDirectory := fmt.Sprintf("[%s] packer_cache/", ds.Name())
fullRemotePath := fmt.Sprintf("%s/%s", remoteDirectory, filename)
return filename, remotePath, remoteDirectory, fullRemotePath
}
func (s *StepRemoteUpload) uploadFile(path string, d driver.Driver, ui packersdk.Ui) (string, error) {
ds, err := d.FindDatastore(s.Datastore, s.Host)
if err != nil {
return "", fmt.Errorf("datastore doesn't exist: %v", err)
}
filename, remotePath, remoteDirectory, fullRemotePath := GetRemoteDirectoryAndPath(path, ds)
if exists := ds.FileExists(remotePath); exists == true {
ui.Say(fmt.Sprintf("File %s already exists; skipping upload.", fullRemotePath))
return fullRemotePath, nil
}
ui.Say(fmt.Sprintf("Uploading %s to %s", filename, remotePath))
if exists := ds.DirExists(remotePath); exists == false {
log.Printf("Remote directory doesn't exist; creating...")
if err := ds.MakeDirectory(remoteDirectory); err != nil {
return "", err
}
}
if err := ds.UploadFile(path, remotePath, s.Host, s.SetHostForDatastoreUploads); err != nil {
return "", err
}
return fullRemotePath, nil
}
func (s *StepRemoteUpload) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
if !s.UploadedCustomCD {
return
}
UploadedCDPath, ok := state.GetOk("cd_path")
if !ok {
return
}
ui := state.Get("ui").(packersdk.Ui)
d := state.Get("driver").(*driver.VCenterDriver)
ui.Say("Deleting cd_files image from remote datastore ...")
ds, err := d.FindDatastore(s.Datastore, s.Host)
if err != nil {
log.Printf("Error finding datastore to delete custom CD; please delete manually: %s", err)
return
}
err = ds.Delete(UploadedCDPath.(string))
if err != nil {
log.Printf("Error deleting custom CD from remote datastore; please delete manually: %s", err)
return
}
}

View File

@ -0,0 +1,46 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type RemoveCDRomConfig
package common
import (
"context"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type RemoveCDRomConfig struct {
// Remove CD-ROM devices from template. Defaults to `false`.
RemoveCdrom bool `mapstructure:"remove_cdrom"`
}
type StepRemoveCDRom struct {
Config *RemoveCDRomConfig
}
func (s *StepRemoveCDRom) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(driver.VirtualMachine)
ui.Say("Eject CD-ROM drives...")
err := vm.EjectCdroms()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
if s.Config.RemoveCdrom == true {
ui.Say("Deleting CD-ROM drives...")
err := vm.RemoveCdroms()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *StepRemoveCDRom) Cleanup(state multistep.StateBag) {}

View File

@ -0,0 +1,31 @@
// Code generated by "mapstructure-to-hcl2 -type RemoveCDRomConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatRemoveCDRomConfig is an auto-generated flat version of RemoveCDRomConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatRemoveCDRomConfig struct {
RemoveCdrom *bool `mapstructure:"remove_cdrom" cty:"remove_cdrom" hcl:"remove_cdrom"`
}
// FlatMapstructure returns a new FlatRemoveCDRomConfig.
// FlatRemoveCDRomConfig is an auto-generated flat version of RemoveCDRomConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*RemoveCDRomConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatRemoveCDRomConfig)
}
// HCL2Spec returns the hcl spec of a RemoveCDRomConfig.
// This spec is used by HCL to read the fields of RemoveCDRomConfig.
// The decoded values from this spec will then be applied to a FlatRemoveCDRomConfig.
func (*FlatRemoveCDRomConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"remove_cdrom": &hcldec.AttrSpec{Name: "remove_cdrom", Type: cty.Bool, Required: false},
}
return s
}

View File

@ -0,0 +1,49 @@
package common
import (
"context"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type StepRemoveFloppy struct {
Datastore string
Host string
}
func (s *StepRemoveFloppy) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(driver.VirtualMachine)
d := state.Get("driver").(driver.Driver)
ui.Say("Deleting Floppy drives...")
floppies, err := vm.FloppyDevices()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
if err = vm.RemoveDevice(true, floppies...); err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
if UploadedFloppyPath, ok := state.GetOk("uploaded_floppy_path"); ok {
ui.Say("Deleting Floppy image...")
ds, err := d.FindDatastore(s.Datastore, s.Host)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
if err := ds.Delete(UploadedFloppyPath.(string)); err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Remove("uploaded_floppy_path")
}
return multistep.ActionContinue
}
func (s *StepRemoveFloppy) Cleanup(state multistep.StateBag) {}

View File

@ -0,0 +1,80 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type RunConfig
package common
import (
"context"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type RunConfig struct {
// Priority of boot devices. Defaults to `disk,cdrom`
BootOrder string `mapstructure:"boot_order"` // example: "floppy,cdrom,ethernet,disk"
}
type StepRun struct {
Config *RunConfig
SetOrder bool
}
func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver)
if s.Config.BootOrder != "" {
ui.Say("Set boot order...")
order := strings.Split(s.Config.BootOrder, ",")
if err := vm.SetBootOrder(order); err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
} else {
if s.SetOrder {
ui.Say("Set boot order temporary...")
if err := vm.SetBootOrder([]string{"disk", "cdrom"}); err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
}
}
ui.Say("Power on VM...")
err := vm.PowerOn()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepRun) Cleanup(state multistep.StateBag) {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver)
if s.Config.BootOrder == "" && s.SetOrder {
ui.Say("Clear boot order...")
if err := vm.SetBootOrder([]string{"-"}); err != nil {
state.Put("error", err)
return
}
}
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
ui.Say("Power off VM...")
err := vm.PowerOff()
if err != nil {
ui.Error(err.Error())
}
}

View File

@ -0,0 +1,31 @@
// Code generated by "mapstructure-to-hcl2 -type RunConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatRunConfig is an auto-generated flat version of RunConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatRunConfig struct {
BootOrder *string `mapstructure:"boot_order" cty:"boot_order" hcl:"boot_order"`
}
// FlatMapstructure returns a new FlatRunConfig.
// FlatRunConfig is an auto-generated flat version of RunConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*RunConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatRunConfig)
}
// HCL2Spec returns the hcl spec of a RunConfig.
// This spec is used by HCL to read the fields of RunConfig.
// The decoded values from this spec will then be applied to a FlatRunConfig.
func (*FlatRunConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.String, Required: false},
}
return s
}

View File

@ -0,0 +1,109 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type ShutdownConfig
package common
import (
"bytes"
"context"
"fmt"
"log"
"time"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type ShutdownConfig struct {
// Specify a VM guest shutdown command. This command will be executed using
// the `communicator`. Otherwise the VMware guest tools are used to gracefully
// shutdown the VM guest.
Command string `mapstructure:"shutdown_command"`
// Amount of time to wait for graceful VM shutdown.
// Defaults to 5m or five minutes.
// This will likely need to be modified if the `communicator` is 'none'.
Timeout time.Duration `mapstructure:"shutdown_timeout"`
// Packer normally halts the virtual machine after all provisioners have
// run when no `shutdown_command` is defined. If this is set to `true`, Packer
// *will not* halt the virtual machine but will assume that you will send the stop
// signal yourself through a preseed.cfg, a script or the final provisioner.
// Packer will wait for a default of five minutes until the virtual machine is shutdown.
// The timeout can be changed using `shutdown_timeout` option.
DisableShutdown bool `mapstructure:"disable_shutdown"`
}
func (c *ShutdownConfig) Prepare(comm communicator.Config) (warnings []string, errs []error) {
if c.Timeout == 0 {
c.Timeout = 5 * time.Minute
}
if comm.Type == "none" && c.Command != "" {
warnings = append(warnings, "The parameter `shutdown_command` is ignored as it requires a `communicator`.")
}
return
}
type StepShutdown struct {
Config *ShutdownConfig
}
func (s *StepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver)
if off, _ := vm.IsPoweredOff(); off {
// Probably power off initiated by last provisioner, though disable_shutdown is not set
ui.Say("VM is already powered off")
return multistep.ActionContinue
}
comm, _ := state.Get("communicator").(packersdk.Communicator)
if comm == nil {
msg := fmt.Sprintf("Please shutdown virtual machine within %s.", s.Config.Timeout)
ui.Message(msg)
} else if s.Config.DisableShutdown {
ui.Say("Automatic shutdown disabled. Please shutdown virtual machine.")
} else if s.Config.Command != "" {
// Communicator is not needed unless shutdown_command is populated
ui.Say("Executing shutdown command...")
log.Printf("Shutdown command: %s", s.Config.Command)
var stdout, stderr bytes.Buffer
cmd := &packersdk.RemoteCmd{
Command: s.Config.Command,
Stdout: &stdout,
Stderr: &stderr,
}
err := comm.Start(ctx, cmd)
if err != nil {
state.Put("error", fmt.Errorf("Failed to send shutdown command: %s", err))
return multistep.ActionHalt
}
} else {
ui.Say("Shutting down VM...")
err := vm.StartShutdown()
if err != nil {
state.Put("error", fmt.Errorf("Cannot shut down VM: %v", err))
return multistep.ActionHalt
}
}
log.Printf("Waiting max %s for shutdown to complete", s.Config.Timeout)
err := vm.WaitForShutdown(ctx, s.Config.Timeout)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepShutdown) Cleanup(state multistep.StateBag) {}

View File

@ -0,0 +1,35 @@
// Code generated by "mapstructure-to-hcl2 -type ShutdownConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatShutdownConfig is an auto-generated flat version of ShutdownConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatShutdownConfig struct {
Command *string `mapstructure:"shutdown_command" cty:"shutdown_command" hcl:"shutdown_command"`
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout" hcl:"shutdown_timeout"`
DisableShutdown *bool `mapstructure:"disable_shutdown" cty:"disable_shutdown" hcl:"disable_shutdown"`
}
// FlatMapstructure returns a new FlatShutdownConfig.
// FlatShutdownConfig is an auto-generated flat version of ShutdownConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*ShutdownConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatShutdownConfig)
}
// HCL2Spec returns the hcl spec of a ShutdownConfig.
// This spec is used by HCL to read the fields of ShutdownConfig.
// The decoded values from this spec will then be applied to a FlatShutdownConfig.
func (*FlatShutdownConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"disable_shutdown": &hcldec.AttrSpec{Name: "disable_shutdown", Type: cty.Bool, Required: false},
}
return s
}

View File

@ -0,0 +1,32 @@
package common
import (
"context"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type StepCreateSnapshot struct {
CreateSnapshot bool
}
func (s *StepCreateSnapshot) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver)
if s.CreateSnapshot {
ui.Say("Creating snapshot...")
err := vm.CreateSnapshot("Created by Packer")
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *StepCreateSnapshot) Cleanup(state multistep.StateBag) {}

View File

@ -0,0 +1,115 @@
package common
import (
"context"
"fmt"
"io/ioutil"
"os"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/communicator/ssh"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/uuid"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
// StepSshKeyPair executes the business logic for setting the SSH key pair in
// the specified communicator.Config.
type StepSshKeyPair struct {
Debug bool
DebugKeyPath string
Comm *communicator.Config
}
func (s *StepSshKeyPair) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
if s.Comm.Type != "ssh" || s.Comm.SSHPassword != "" {
return multistep.ActionContinue
}
ui := state.Get("ui").(packersdk.Ui)
comment := fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
if s.Comm.SSHPrivateKeyFile != "" {
ui.Say("Using existing SSH private key for the communicator...")
privateKeyBytes, err := s.Comm.ReadSSHPrivateKeyFile()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
kp, err := ssh.KeyPairFromPrivateKey(ssh.FromPrivateKeyConfig{
RawPrivateKeyPemBlock: privateKeyBytes,
Comment: comment,
})
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
s.Comm.SSHPrivateKey = privateKeyBytes
s.Comm.SSHKeyPairName = kp.Comment
s.Comm.SSHTemporaryKeyPairName = kp.Comment
s.Comm.SSHPublicKey = kp.PublicKeyAuthorizedKeysLine
return multistep.ActionContinue
}
if s.Comm.SSHAgentAuth {
ui.Say("Using local SSH Agent to authenticate connections for the communicator...")
return multistep.ActionContinue
}
ui.Say("Creating ephemeral key pair for SSH communicator...")
if s.Comm.SSHTemporaryKeyPairName != "" {
comment = s.Comm.SSHTemporaryKeyPairName
}
kp, err := ssh.NewKeyPair(ssh.CreateKeyPairConfig{
Comment: comment,
Type: ssh.Rsa,
})
if err != nil {
state.Put("error", fmt.Errorf("Error creating temporary keypair: %s", err))
return multistep.ActionHalt
}
s.Comm.SSHKeyPairName = kp.Comment
s.Comm.SSHTemporaryKeyPairName = kp.Comment
s.Comm.SSHPrivateKey = kp.PrivateKeyPemBlock
s.Comm.SSHPublicKey = kp.PublicKeyAuthorizedKeysLine
s.Comm.SSHClearAuthorizedKeys = true
vm := state.Get("vm").(*driver.VirtualMachineDriver)
err = vm.AddPublicKeys(ctx, string(s.Comm.SSHPublicKey))
if err != nil {
state.Put("error", fmt.Errorf("error saving temporary keypair in the vm: %s", err))
return multistep.ActionHalt
}
ui.Say("Created ephemeral SSH key pair for communicator")
// If we're in debug mode, output the private key to the working
// directory.
if s.Debug {
ui.Message(fmt.Sprintf("Saving communicator private key for debug purposes: %s", s.DebugKeyPath))
// Write the key out
if err := ioutil.WriteFile(s.DebugKeyPath, kp.PrivateKeyPemBlock, 0600); err != nil {
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *StepSshKeyPair) Cleanup(state multistep.StateBag) {
if s.Debug {
if err := os.Remove(s.DebugKeyPath); err != nil {
ui := state.Get("ui").(packersdk.Ui)
ui.Error(fmt.Sprintf(
"Error removing debug key '%s': %s", s.DebugKeyPath, err))
}
}
}

View File

@ -0,0 +1,31 @@
package common
import (
"context"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type StepConvertToTemplate struct {
ConvertToTemplate bool
}
func (s *StepConvertToTemplate) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver)
if s.ConvertToTemplate {
ui.Say("Convert VM into template...")
err := vm.ConvertToTemplate()
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *StepConvertToTemplate) Cleanup(state multistep.StateBag) {}

View File

@ -0,0 +1,182 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type WaitIpConfig
package common
import (
"context"
"fmt"
"log"
"net"
"time"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type WaitIpConfig struct {
// Amount of time to wait for VM's IP, similar to 'ssh_timeout'.
// Defaults to 30m (30 minutes). See the Golang
// [ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation
// for full details.
WaitTimeout time.Duration `mapstructure:"ip_wait_timeout"`
// Amount of time to wait for VM's IP to settle down, sometimes VM may
// report incorrect IP initially, then its recommended to set that
// parameter to apx. 2 minutes. Examples 45s and 10m. Defaults to
// 5s(5 seconds). See the Golang
// [ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation
// for full details.
SettleTimeout time.Duration `mapstructure:"ip_settle_timeout"`
// Set this to a CIDR address to cause the service to wait for an address that is contained in
// this network range. Defaults to "0.0.0.0/0" for any ipv4 address. Examples include:
//
// * empty string ("") - remove all filters
// * `0:0:0:0:0:0:0:0/0` - allow only ipv6 addresses
// * `192.168.1.0/24` - only allow ipv4 addresses from 192.168.1.1 to 192.168.1.254
WaitAddress *string `mapstructure:"ip_wait_address"`
ipnet *net.IPNet
// WaitTimeout is a total timeout, so even if VM changes IP frequently and it doesn't settle down we will end waiting.
}
type StepWaitForIp struct {
Config *WaitIpConfig
}
func (c *WaitIpConfig) Prepare() []error {
var errs []error
if c.SettleTimeout == 0 {
c.SettleTimeout = 5 * time.Second
}
if c.WaitTimeout == 0 {
c.WaitTimeout = 30 * time.Minute
}
if c.WaitAddress == nil {
addr := "0.0.0.0/0"
c.WaitAddress = &addr
}
if *c.WaitAddress != "" {
var err error
_, c.ipnet, err = net.ParseCIDR(*c.WaitAddress)
if err != nil {
errs = append(errs, fmt.Errorf("unable to parse \"ip_wait_address\": %w", err))
}
}
return errs
}
func (c *WaitIpConfig) GetIPNet() *net.IPNet {
return c.ipnet
}
func (s *StepWaitForIp) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
vm := state.Get("vm").(*driver.VirtualMachineDriver)
var ip string
var err error
sub, cancel := context.WithCancel(ctx)
waitDone := make(chan bool, 1)
defer func() {
cancel()
}()
go func() {
ui.Say("Waiting for IP...")
ip, err = doGetIp(vm, sub, s.Config)
waitDone <- true
}()
log.Printf("[INFO] Waiting for IP, up to total timeout: %s, settle timeout: %s", s.Config.WaitTimeout, s.Config.SettleTimeout)
timeout := time.After(s.Config.WaitTimeout)
for {
select {
case <-timeout:
cancel()
<-waitDone
if ip != "" {
state.Put("ip", ip)
log.Printf("[WARN] API timeout waiting for IP but one IP was found. Using IP: %s", ip)
return multistep.ActionContinue
}
err := fmt.Errorf("Timeout waiting for IP.")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
case <-ctx.Done():
cancel()
log.Println("[WARN] Interrupt detected, quitting waiting for IP.")
return multistep.ActionHalt
case <-waitDone:
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("ip", ip)
ui.Say(fmt.Sprintf("IP address: %v", ip))
return multistep.ActionContinue
case <-time.After(1 * time.Second):
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return multistep.ActionHalt
}
}
}
}
func doGetIp(vm *driver.VirtualMachineDriver, ctx context.Context, c *WaitIpConfig) (string, error) {
var prevIp = ""
var stopTime time.Time
var interval time.Duration
if c.SettleTimeout.Seconds() >= 120 {
interval = 30 * time.Second
} else if c.SettleTimeout.Seconds() >= 60 {
interval = 15 * time.Second
} else if c.SettleTimeout.Seconds() >= 10 {
interval = 5 * time.Second
} else {
interval = 1 * time.Second
}
loop:
ip, err := vm.WaitForIP(ctx, c.ipnet)
if err != nil {
return "", err
}
// Check for ctx cancellation to avoid printing any IP logs at the timeout
select {
case <-ctx.Done():
return ip, fmt.Errorf("IP wait cancelled.")
default:
}
if prevIp == "" || prevIp != ip {
if prevIp == "" {
log.Printf("VM IP aquired: %s", ip)
} else {
log.Printf("VM IP changed from %s to %s", prevIp, ip)
}
prevIp = ip
stopTime = time.Now().Add(c.SettleTimeout)
goto loop
} else {
log.Printf("VM IP is still the same: %s", prevIp)
if time.Now().After(stopTime) {
log.Printf("VM IP seems stable enough: %s", ip)
return ip, nil
}
select {
case <-ctx.Done():
return "", fmt.Errorf("IP wait cancelled")
case <-time.After(interval):
goto loop
}
}
}
func (s *StepWaitForIp) Cleanup(state multistep.StateBag) {}

View File

@ -0,0 +1,35 @@
// Code generated by "mapstructure-to-hcl2 -type WaitIpConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatWaitIpConfig is an auto-generated flat version of WaitIpConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatWaitIpConfig struct {
WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout" hcl:"ip_wait_timeout"`
SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout" hcl:"ip_settle_timeout"`
WaitAddress *string `mapstructure:"ip_wait_address" cty:"ip_wait_address" hcl:"ip_wait_address"`
}
// FlatMapstructure returns a new FlatWaitIpConfig.
// FlatWaitIpConfig is an auto-generated flat version of WaitIpConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*WaitIpConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatWaitIpConfig)
}
// HCL2Spec returns the hcl spec of a WaitIpConfig.
// This spec is used by HCL to read the fields of WaitIpConfig.
// The decoded values from this spec will then be applied to a FlatWaitIpConfig.
func (*FlatWaitIpConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"ip_wait_timeout": &hcldec.AttrSpec{Name: "ip_wait_timeout", Type: cty.String, Required: false},
"ip_settle_timeout": &hcldec.AttrSpec{Name: "ip_settle_timeout", Type: cty.String, Required: false},
"ip_wait_address": &hcldec.AttrSpec{Name: "ip_wait_address", Type: cty.String, Required: false},
}
return s
}

View File

@ -0,0 +1,118 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type StorageConfig,DiskConfig
package common
import (
"fmt"
)
// Defines the disk storage for a VM.
//
// Example that will create a 15GB and a 20GB disk on the VM. The second disk will be thin provisioned:
//
// In JSON:
// ```json
// "storage": [
// {
// "disk_size": 15000
// },
// {
// "disk_size": 20000,
// "disk_thin_provisioned": true
// }
// ],
// ```
// In HCL2:
// ```hcl
// storage {
// disk_size = 15000
// }
// storage {
// disk_size = 20000
// disk_thin_provisioned = true
// }
// ```
//
// Example that creates 2 pvscsi controllers and adds 2 disks to each one:
//
// In JSON:
// ```json
// "disk_controller_type": ["pvscsi", "pvscsi"],
// "storage": [
// {
// "disk_size": 15000,
// "disk_controller_index": 0
// },
// {
// "disk_size": 15000,
// "disk_controller_index": 0
// },
// {
// "disk_size": 15000,
// "disk_controller_index": 1
// },
// {
// "disk_size": 15000,
// "disk_controller_index": 1
// }
// ],
// ```
//
// In HCL2:
// ```hcl
// disk_controller_type = ["pvscsi", "pvscsi"]
// storage {
// disk_size = 15000,
// disk_controller_index = 0
// }
// storage {
// disk_size = 15000
// disk_controller_index = 0
// }
// storage {
// disk_size = 15000
// disk_controller_index = 1
// }
// storage {
// disk_size = 15000
// disk_controller_index = 1
// }
// ```
type DiskConfig struct {
// The size of the disk in MB.
DiskSize int64 `mapstructure:"disk_size" required:"true"`
// Enable VMDK thin provisioning for VM. Defaults to `false`.
DiskThinProvisioned bool `mapstructure:"disk_thin_provisioned"`
// Enable VMDK eager scrubbing for VM. Defaults to `false`.
DiskEagerlyScrub bool `mapstructure:"disk_eagerly_scrub"`
// The assigned disk controller. Defaults to the first one (0)
DiskControllerIndex int `mapstructure:"disk_controller_index"`
}
type StorageConfig struct {
// Set VM disk controller type. Example `lsilogic`, `pvscsi`, `nvme`, or `scsi`. Use a list to define additional controllers.
// Defaults to `lsilogic`. See
// [SCSI, SATA, and NVMe Storage Controller Conditions, Limitations, and Compatibility](https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.vsphere.vm_admin.doc/GUID-5872D173-A076-42FE-8D0B-9DB0EB0E7362.html#GUID-5872D173-A076-42FE-8D0B-9DB0EB0E7362)
// for additional details.
DiskControllerType []string `mapstructure:"disk_controller_type"`
// Configures a collection of one or more disks to be provisioned along with the VM. See the [Storage Configuration](#storage-configuration).
Storage []DiskConfig `mapstructure:"storage"`
}
func (c *StorageConfig) Prepare() []error {
var errs []error
if len(c.Storage) > 0 {
for i, storage := range c.Storage {
if storage.DiskSize == 0 {
errs = append(errs, fmt.Errorf("storage[%d].'disk_size' is required", i))
}
if storage.DiskControllerIndex >= len(c.DiskControllerType) {
errs = append(errs, fmt.Errorf("storage[%d].'disk_controller_index' references an unknown disk controller", i))
}
}
}
return errs
}

View File

@ -0,0 +1,62 @@
// Code generated by "mapstructure-to-hcl2 -type StorageConfig,DiskConfig"; DO NOT EDIT.
package common
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatDiskConfig is an auto-generated flat version of DiskConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatDiskConfig struct {
DiskSize *int64 `mapstructure:"disk_size" required:"true" cty:"disk_size" hcl:"disk_size"`
DiskThinProvisioned *bool `mapstructure:"disk_thin_provisioned" cty:"disk_thin_provisioned" hcl:"disk_thin_provisioned"`
DiskEagerlyScrub *bool `mapstructure:"disk_eagerly_scrub" cty:"disk_eagerly_scrub" hcl:"disk_eagerly_scrub"`
DiskControllerIndex *int `mapstructure:"disk_controller_index" cty:"disk_controller_index" hcl:"disk_controller_index"`
}
// FlatMapstructure returns a new FlatDiskConfig.
// FlatDiskConfig is an auto-generated flat version of DiskConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*DiskConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatDiskConfig)
}
// HCL2Spec returns the hcl spec of a DiskConfig.
// This spec is used by HCL to read the fields of DiskConfig.
// The decoded values from this spec will then be applied to a FlatDiskConfig.
func (*FlatDiskConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
"disk_thin_provisioned": &hcldec.AttrSpec{Name: "disk_thin_provisioned", Type: cty.Bool, Required: false},
"disk_eagerly_scrub": &hcldec.AttrSpec{Name: "disk_eagerly_scrub", Type: cty.Bool, Required: false},
"disk_controller_index": &hcldec.AttrSpec{Name: "disk_controller_index", Type: cty.Number, Required: false},
}
return s
}
// FlatStorageConfig is an auto-generated flat version of StorageConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatStorageConfig struct {
DiskControllerType []string `mapstructure:"disk_controller_type" cty:"disk_controller_type" hcl:"disk_controller_type"`
Storage []FlatDiskConfig `mapstructure:"storage" cty:"storage" hcl:"storage"`
}
// FlatMapstructure returns a new FlatStorageConfig.
// FlatStorageConfig is an auto-generated flat version of StorageConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*StorageConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatStorageConfig)
}
// HCL2Spec returns the hcl spec of a StorageConfig.
// This spec is used by HCL to read the fields of StorageConfig.
// The decoded values from this spec will then be applied to a FlatStorageConfig.
func (*FlatStorageConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"disk_controller_type": &hcldec.AttrSpec{Name: "disk_controller_type", Type: cty.List(cty.String), Required: false},
"storage": &hcldec.BlockListSpec{TypeName: "storage", Nested: hcldec.ObjectSpec((*FlatDiskConfig)(nil).HCL2Spec())},
}
return s
}

View File

@ -0,0 +1,19 @@
package driver
import "github.com/vmware/govmomi/object"
type Cluster struct {
driver *VCenterDriver
cluster *object.ClusterComputeResource
}
func (d *VCenterDriver) FindCluster(name string) (*Cluster, error) {
c, err := d.finder.ClusterComputeResource(d.ctx, name)
if err != nil {
return nil, err
}
return &Cluster{
cluster: c,
driver: d,
}, nil
}

View File

@ -0,0 +1,235 @@
package driver
import (
"fmt"
"path"
"regexp"
"strings"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type Datastore interface {
Info(params ...string) (*mo.Datastore, error)
FileExists(path string) bool
DirExists(path string) bool
Name() string
ResolvePath(path string) string
UploadFile(src, dst, host string, setHost bool) error
Delete(path string) error
MakeDirectory(path string) error
Reference() types.ManagedObjectReference
}
type DatastoreDriver struct {
ds *object.Datastore
driver *VCenterDriver
}
func (d *VCenterDriver) NewDatastore(ref *types.ManagedObjectReference) Datastore {
return &DatastoreDriver{
ds: object.NewDatastore(d.client.Client, *ref),
driver: d,
}
}
// If name is an empty string, then resolve host's one
func (d *VCenterDriver) FindDatastore(name string, host string) (Datastore, error) {
if name == "" {
h, err := d.FindHost(host)
if err != nil {
return nil, fmt.Errorf("Error finding host for to get datastore: %s", err)
}
i, err := h.Info("datastore")
if err != nil {
return nil, fmt.Errorf("Error getting datastore info from host: %s", err)
}
if len(i.Datastore) > 1 {
return nil, fmt.Errorf("Host has multiple datastores. Specify it explicitly")
}
ds := d.NewDatastore(&i.Datastore[0])
inf, err := ds.Info("name")
if err != nil {
return nil, fmt.Errorf("Error getting datastore name: %s", err)
}
name = inf.Name
}
ds, err := d.finder.Datastore(d.ctx, name)
if err != nil {
return nil, fmt.Errorf("Error finding datastore with name %s: %s", name, err)
}
return &DatastoreDriver{
ds: ds,
driver: d,
}, nil
}
func (d *VCenterDriver) GetDatastoreName(id string) (string, error) {
obj := types.ManagedObjectReference{
Type: "Datastore",
Value: id,
}
pc := property.DefaultCollector(d.vimClient)
var me mo.ManagedEntity
err := pc.RetrieveOne(d.ctx, obj, []string{"name"}, &me)
if err != nil {
return id, err
}
return me.Name, nil
}
func (ds *DatastoreDriver) Info(params ...string) (*mo.Datastore, error) {
var p []string
if len(params) == 0 {
p = []string{"*"}
} else {
p = params
}
var info mo.Datastore
err := ds.ds.Properties(ds.driver.ctx, ds.ds.Reference(), p, &info)
if err != nil {
return nil, err
}
return &info, nil
}
func (ds *DatastoreDriver) DirExists(filepath string) bool {
_, err := ds.ds.Stat(ds.driver.ctx, filepath)
if _, ok := err.(object.DatastoreNoSuchDirectoryError); ok {
return false
}
return true
}
func (ds *DatastoreDriver) FileExists(path string) bool {
_, err := ds.ds.Stat(ds.driver.ctx, path)
return err == nil
}
func (ds *DatastoreDriver) Name() string {
return ds.ds.Name()
}
func (ds *DatastoreDriver) Reference() types.ManagedObjectReference {
return ds.ds.Reference()
}
func (ds *DatastoreDriver) ResolvePath(path string) string {
return ds.ds.Path(path)
}
// The file ID isn't available via the API, so we use DatastoreBrowser to search
func (d *VCenterDriver) GetDatastoreFilePath(datastoreID, dir, filename string) (string, error) {
ref := types.ManagedObjectReference{Type: "Datastore", Value: datastoreID}
ds := object.NewDatastore(d.vimClient, ref)
b, err := ds.Browser(d.ctx)
if err != nil {
return filename, err
}
ext := path.Ext(filename)
pat := strings.Replace(filename, ext, "*"+ext, 1)
spec := types.HostDatastoreBrowserSearchSpec{
MatchPattern: []string{pat},
}
task, err := b.SearchDatastore(d.ctx, dir, &spec)
if err != nil {
return filename, err
}
info, err := task.WaitForResult(d.ctx, nil)
if err != nil {
return filename, err
}
res, ok := info.Result.(types.HostDatastoreBrowserSearchResults)
if !ok {
return filename, fmt.Errorf("search(%s) result type=%T", pat, info.Result)
}
if len(res.File) != 1 {
return filename, fmt.Errorf("search(%s) result files=%d", pat, len(res.File))
}
return res.File[0].GetFileInfo().Path, nil
}
func (ds *DatastoreDriver) UploadFile(src, dst, host string, setHost bool) error {
p := soap.DefaultUpload
ctx := ds.driver.ctx
if setHost && host != "" {
h, err := ds.driver.FindHost(host)
if err != nil {
return err
}
ctx = ds.ds.HostContext(ctx, h.host)
}
return ds.ds.UploadFile(ctx, src, dst, &p)
}
func (ds *DatastoreDriver) Delete(path string) error {
dc, err := ds.driver.finder.Datacenter(ds.driver.ctx, ds.ds.DatacenterPath)
if err != nil {
return err
}
fm := ds.ds.NewFileManager(dc, false)
return fm.Delete(ds.driver.ctx, path)
}
func (ds *DatastoreDriver) MakeDirectory(path string) error {
dc, err := ds.driver.finder.Datacenter(ds.driver.ctx, ds.ds.DatacenterPath)
if err != nil {
return err
}
fm := ds.ds.NewFileManager(dc, false)
return fm.FileManager.MakeDirectory(ds.driver.ctx, path, dc, true)
}
// Cuts out the datastore prefix
// Example: "[datastore1] file.ext" --> "file.ext"
func RemoveDatastorePrefix(path string) string {
res := object.DatastorePath{}
if hadPrefix := res.FromString(path); hadPrefix {
return res.Path
} else {
return path
}
}
type DatastoreIsoPath struct {
path string
}
func (d *DatastoreIsoPath) Validate() bool {
// Matches:
// [datastore] /dir/subdir/file
// [datastore] dir/subdir/file
// [] /dir/subdir/file
// [data-store] /dir/subdir/file
// dir/subdir/file or dir/subdir/file
matched, _ := regexp.MatchString(`^\s*(\[[^\[\]\/]*\])?\s*[^\[\]]+\s*$`, d.path)
return matched
}
func (d *DatastoreIsoPath) GetFilePath() string {
filePath := d.path
parts := strings.Split(d.path, "]")
if len(parts) > 1 {
// removes datastore name from path
filePath = parts[1]
filePath = strings.TrimSpace(filePath)
}
return filePath
}

View File

@ -0,0 +1,81 @@
package driver
import (
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type DatastoreMock struct {
FileExistsCalled bool
FileExistsReturn bool
DirExistsCalled bool
DirExistsReturn bool
NameReturn string
MakeDirectoryCalled bool
ResolvePathCalled bool
ResolvePathReturn string
DeleteCalled bool
DeletePath string
DeleteErr error
UploadFileCalled bool
UploadFileSrc string
UploadFileDst string
UploadFileHost string
UploadFileSetHost bool
UploadFileErr error
}
func (ds *DatastoreMock) Info(params ...string) (*mo.Datastore, error) {
return nil, nil
}
func (ds *DatastoreMock) FileExists(path string) bool {
ds.FileExistsCalled = true
return ds.FileExistsReturn
}
func (ds *DatastoreMock) DirExists(path string) bool {
ds.DirExistsCalled = true
return ds.DirExistsReturn
}
func (ds *DatastoreMock) Name() string {
if ds.NameReturn == "" {
return "datastore-mock"
}
return ds.NameReturn
}
func (ds *DatastoreMock) Reference() types.ManagedObjectReference {
return types.ManagedObjectReference{}
}
func (ds *DatastoreMock) ResolvePath(path string) string {
ds.ResolvePathCalled = true
return ds.ResolvePathReturn
}
func (ds *DatastoreMock) UploadFile(src, dst, host string, setHost bool) error {
ds.UploadFileCalled = true
ds.UploadFileSrc = src
ds.UploadFileDst = dst
ds.UploadFileHost = host
ds.UploadFileSetHost = setHost
return ds.UploadFileErr
}
func (ds *DatastoreMock) Delete(path string) error {
ds.DeleteCalled = true
ds.DeletePath = path
return ds.DeleteErr
}
func (ds *DatastoreMock) MakeDirectory(path string) error {
ds.MakeDirectoryCalled = true
return nil
}

View File

@ -0,0 +1,85 @@
package driver
import (
"errors"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/types"
)
type Disk struct {
DiskSize int64
DiskEagerlyScrub bool
DiskThinProvisioned bool
ControllerIndex int
}
type StorageConfig struct {
DiskControllerType []string // example: "scsi", "pvscsi", "nvme", "lsilogic"
Storage []Disk
}
func (c *StorageConfig) AddStorageDevices(existingDevices object.VirtualDeviceList) ([]types.BaseVirtualDeviceConfigSpec, error) {
newDevices := object.VirtualDeviceList{}
// Create new controller based on existing devices list and add it to the new devices list
// to confirm creation
var controllers []types.BaseVirtualController
for _, controllerType := range c.DiskControllerType {
var device types.BaseVirtualDevice
var err error
if controllerType == "nvme" {
device, err = existingDevices.CreateNVMEController()
} else {
device, err = existingDevices.CreateSCSIController(controllerType)
}
if err != nil {
return nil, err
}
existingDevices = append(existingDevices, device)
newDevices = append(newDevices, device)
controller, err := existingDevices.FindDiskController(existingDevices.Name(device))
if err != nil {
return nil, err
}
controllers = append(controllers, controller)
}
for _, dc := range c.Storage {
disk := &types.VirtualDisk{
VirtualDevice: types.VirtualDevice{
Key: existingDevices.NewKey(),
Backing: &types.VirtualDiskFlatVer2BackingInfo{
DiskMode: string(types.VirtualDiskModePersistent),
ThinProvisioned: types.NewBool(dc.DiskThinProvisioned),
EagerlyScrub: types.NewBool(dc.DiskEagerlyScrub),
},
},
CapacityInKB: dc.DiskSize * 1024,
}
existingDevices.AssignController(disk, controllers[dc.ControllerIndex])
existingDevices = append(existingDevices, disk)
newDevices = append(newDevices, disk)
}
return newDevices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
}
func findDisk(devices object.VirtualDeviceList) (*types.VirtualDisk, error) {
var disks []*types.VirtualDisk
for _, device := range devices {
switch d := device.(type) {
case *types.VirtualDisk:
disks = append(disks, d)
}
}
switch len(disks) {
case 0:
return nil, errors.New("VM has no disks")
case 1:
return disks[0], nil
}
return nil, errors.New("VM has multiple disks")
}

View File

@ -0,0 +1,129 @@
package driver
import (
"context"
"fmt"
"net/url"
"time"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/session"
"github.com/vmware/govmomi/vapi/library"
"github.com/vmware/govmomi/vapi/rest"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/soap"
"github.com/vmware/govmomi/vim25/types"
)
type Driver interface {
NewVM(ref *types.ManagedObjectReference) VirtualMachine
FindVM(name string) (VirtualMachine, error)
FindCluster(name string) (*Cluster, error)
PreCleanVM(ui packersdk.Ui, vmPath string, force bool) error
CreateVM(config *CreateConfig) (VirtualMachine, error)
NewDatastore(ref *types.ManagedObjectReference) Datastore
FindDatastore(name string, host string) (Datastore, error)
GetDatastoreName(id string) (string, error)
GetDatastoreFilePath(datastoreID, dir, filename string) (string, error)
NewFolder(ref *types.ManagedObjectReference) *Folder
FindFolder(name string) (*Folder, error)
NewHost(ref *types.ManagedObjectReference) *Host
FindHost(name string) (*Host, error)
NewNetwork(ref *types.ManagedObjectReference) *Network
FindNetwork(name string) (*Network, error)
FindNetworks(name string) ([]*Network, error)
NewResourcePool(ref *types.ManagedObjectReference) *ResourcePool
FindResourcePool(cluster string, host string, name string) (*ResourcePool, error)
FindContentLibraryByName(name string) (*Library, error)
FindContentLibraryItem(libraryId string, name string) (*library.Item, error)
FindContentLibraryFileDatastorePath(isoPath string) (string, error)
}
type VCenterDriver struct {
// context that controls the authenticated sessions used to run the VM commands
ctx context.Context
client *govmomi.Client
vimClient *vim25.Client
restClient *RestClient
finder *find.Finder
datacenter *object.Datacenter
}
type ConnectConfig struct {
VCenterServer string
Username string
Password string
InsecureConnection bool
Datacenter string
}
func NewDriver(config *ConnectConfig) (Driver, error) {
ctx := context.TODO()
vcenterUrl, err := url.Parse(fmt.Sprintf("https://%v/sdk", config.VCenterServer))
if err != nil {
return nil, err
}
credentials := url.UserPassword(config.Username, config.Password)
vcenterUrl.User = credentials
soapClient := soap.NewClient(vcenterUrl, config.InsecureConnection)
vimClient, err := vim25.NewClient(ctx, soapClient)
if err != nil {
return nil, err
}
vimClient.RoundTripper = session.KeepAlive(vimClient.RoundTripper, 10*time.Minute)
client := &govmomi.Client{
Client: vimClient,
SessionManager: session.NewManager(vimClient),
}
err = client.SessionManager.Login(ctx, credentials)
if err != nil {
return nil, err
}
finder := find.NewFinder(client.Client, false)
datacenter, err := finder.DatacenterOrDefault(ctx, config.Datacenter)
if err != nil {
return nil, err
}
finder.SetDatacenter(datacenter)
d := &VCenterDriver{
ctx: ctx,
client: client,
vimClient: vimClient,
restClient: &RestClient{
client: rest.NewClient(vimClient),
credentials: credentials,
},
datacenter: datacenter,
finder: finder,
}
return d, nil
}
// The rest.Client requires vCenter.
// RestClient is to modularize the rest.Client session and use it only when is necessary.
// This will allow users without vCenter to use the other features that doesn't use the rest.Client.
// To use the client login/logout must be done to create an authenticated session.
type RestClient struct {
client *rest.Client
credentials *url.Userinfo
}
func (r *RestClient) Login(ctx context.Context) error {
return r.client.Login(ctx, r.credentials)
}
func (r *RestClient) Logout(ctx context.Context) error {
return r.client.Logout(ctx)
}

View File

@ -0,0 +1,119 @@
package driver
import (
"fmt"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/vmware/govmomi/vapi/library"
"github.com/vmware/govmomi/vim25/types"
)
type DriverMock struct {
FindDatastoreCalled bool
DatastoreMock *DatastoreMock
FindDatastoreName string
FindDatastoreHost string
FindDatastoreErr error
PreCleanShouldFail bool
PreCleanVMCalled bool
PreCleanForce bool
PreCleanVMPath string
CreateVMShouldFail bool
CreateVMCalled bool
CreateConfig *CreateConfig
VM VirtualMachine
FindVMCalled bool
FindVMName string
}
func NewDriverMock() *DriverMock {
return new(DriverMock)
}
func (d *DriverMock) FindDatastore(name string, host string) (Datastore, error) {
d.FindDatastoreCalled = true
if d.DatastoreMock == nil {
d.DatastoreMock = new(DatastoreMock)
}
d.FindDatastoreName = name
d.FindDatastoreHost = host
return d.DatastoreMock, d.FindDatastoreErr
}
func (d *DriverMock) NewVM(ref *types.ManagedObjectReference) VirtualMachine {
return nil
}
func (d *DriverMock) FindVM(name string) (VirtualMachine, error) {
d.FindVMCalled = true
if d.VM == nil {
d.VM = new(VirtualMachineMock)
}
d.FindVMName = name
return d.VM, d.FindDatastoreErr
}
func (d *DriverMock) FindCluster(name string) (*Cluster, error) {
return nil, nil
}
func (d *DriverMock) PreCleanVM(ui packersdk.Ui, vmPath string, force bool) error {
d.PreCleanVMCalled = true
if d.PreCleanShouldFail {
return fmt.Errorf("pre clean failed")
}
d.PreCleanForce = true
d.PreCleanVMPath = vmPath
return nil
}
func (d *DriverMock) CreateVM(config *CreateConfig) (VirtualMachine, error) {
d.CreateVMCalled = true
if d.CreateVMShouldFail {
return nil, fmt.Errorf("create vm failed")
}
d.CreateConfig = config
d.VM = new(VirtualMachineDriver)
return d.VM, nil
}
func (d *DriverMock) NewDatastore(ref *types.ManagedObjectReference) Datastore { return nil }
func (d *DriverMock) GetDatastoreName(id string) (string, error) { return "", nil }
func (d *DriverMock) GetDatastoreFilePath(datastoreID, dir, filename string) (string, error) {
return "", nil
}
func (d *DriverMock) NewFolder(ref *types.ManagedObjectReference) *Folder { return nil }
func (d *DriverMock) FindFolder(name string) (*Folder, error) { return nil, nil }
func (d *DriverMock) NewHost(ref *types.ManagedObjectReference) *Host { return nil }
func (d *DriverMock) FindHost(name string) (*Host, error) { return nil, nil }
func (d *DriverMock) NewNetwork(ref *types.ManagedObjectReference) *Network { return nil }
func (d *DriverMock) FindNetwork(name string) (*Network, error) { return nil, nil }
func (d *DriverMock) FindNetworks(name string) ([]*Network, error) { return nil, nil }
func (d *DriverMock) NewResourcePool(ref *types.ManagedObjectReference) *ResourcePool { return nil }
func (d *DriverMock) FindResourcePool(cluster string, host string, name string) (*ResourcePool, error) {
return nil, nil
}
func (d *DriverMock) FindContentLibraryByName(name string) (*Library, error) { return nil, nil }
func (d *DriverMock) FindContentLibraryItem(libraryId string, name string) (*library.Item, error) {
return nil, nil
}
func (d *DriverMock) FindContentLibraryFileDatastorePath(isoPath string) (string, error) {
return "", nil
}

View File

@ -0,0 +1,93 @@
package driver
import (
"fmt"
"path"
"strings"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type Folder struct {
driver *VCenterDriver
folder *object.Folder
}
func (d *VCenterDriver) NewFolder(ref *types.ManagedObjectReference) *Folder {
return &Folder{
folder: object.NewFolder(d.client.Client, *ref),
driver: d,
}
}
func (d *VCenterDriver) FindFolder(name string) (*Folder, error) {
if name != "" {
// create folders if they don't exist
parent := ""
parentFolder, err := d.finder.Folder(d.ctx, path.Join(d.datacenter.InventoryPath, "vm"))
if err != nil {
return nil, err
}
folders := strings.Split(name, "/")
for _, folder := range folders {
parent = path.Join(parent, folder)
f, err := d.finder.Folder(d.ctx, path.Join(d.datacenter.InventoryPath, "vm", parent))
if _, ok := err.(*find.NotFoundError); ok {
f, err = parentFolder.CreateFolder(d.ctx, folder)
}
if err != nil {
return nil, err
}
parentFolder = f
}
}
f, err := d.finder.Folder(d.ctx, path.Join(d.datacenter.InventoryPath, "vm", name))
if err != nil {
return nil, err
}
return &Folder{
folder: f,
driver: d,
}, nil
}
func (f *Folder) Info(params ...string) (*mo.Folder, error) {
var p []string
if len(params) == 0 {
p = []string{"*"}
} else {
p = params
}
var info mo.Folder
err := f.folder.Properties(f.driver.ctx, f.folder.Reference(), p, &info)
if err != nil {
return nil, err
}
return &info, nil
}
func (f *Folder) Path() (string, error) {
info, err := f.Info("name", "parent")
if err != nil {
return "", err
}
if info.Parent.Type == "Datacenter" {
return "", nil
} else {
parent := f.driver.NewFolder(info.Parent)
path, err := parent.Path()
if err != nil {
return "", err
}
if path == "" {
return info.Name, nil
} else {
return fmt.Sprintf("%v/%v", path, info.Name), nil
}
}
}

View File

@ -0,0 +1,45 @@
package driver
import (
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type Host struct {
driver *VCenterDriver
host *object.HostSystem
}
func (d *VCenterDriver) NewHost(ref *types.ManagedObjectReference) *Host {
return &Host{
host: object.NewHostSystem(d.client.Client, *ref),
driver: d,
}
}
func (d *VCenterDriver) FindHost(name string) (*Host, error) {
h, err := d.finder.HostSystem(d.ctx, name)
if err != nil {
return nil, err
}
return &Host{
host: h,
driver: d,
}, nil
}
func (h *Host) Info(params ...string) (*mo.HostSystem, error) {
var p []string
if len(params) == 0 {
p = []string{"*"}
} else {
p = params
}
var info mo.HostSystem
err := h.host.Properties(h.driver.ctx, h.host.Reference(), p, &info)
if err != nil {
return nil, err
}
return &info, nil
}

View File

@ -0,0 +1,113 @@
package driver
import (
"fmt"
"log"
"path"
"strings"
"github.com/vmware/govmomi/vapi/library"
)
type Library struct {
driver *VCenterDriver
library *library.Library
}
func (d *VCenterDriver) FindContentLibraryByName(name string) (*Library, error) {
lm := library.NewManager(d.restClient.client)
l, err := lm.GetLibraryByName(d.ctx, name)
if err != nil {
return nil, err
}
return &Library{
library: l,
driver: d,
}, nil
}
func (d *VCenterDriver) FindContentLibraryItem(libraryId string, name string) (*library.Item, error) {
lm := library.NewManager(d.restClient.client)
items, err := lm.GetLibraryItems(d.ctx, libraryId)
if err != nil {
return nil, err
}
for _, item := range items {
if item.Name == name {
return &item, nil
}
}
return nil, fmt.Errorf("Item %s not found", name)
}
func (d *VCenterDriver) FindContentLibraryFileDatastorePath(isoPath string) (string, error) {
log.Printf("Check if ISO path is a Content Library path")
err := d.restClient.Login(d.ctx)
if err != nil {
log.Printf("vCenter client not available. ISO path not identified as a Content Library path")
return isoPath, err
}
libraryFilePath := &LibraryFilePath{path: isoPath}
err = libraryFilePath.Validate()
if err != nil {
log.Printf("ISO path not identified as a Content Library path")
return isoPath, err
}
libraryName := libraryFilePath.GetLibraryName()
itemName := libraryFilePath.GetLibraryItemName()
isoFile := libraryFilePath.GetFileName()
lib, err := d.FindContentLibraryByName(libraryName)
if err != nil {
log.Printf("ISO path not identified as a Content Library path")
return isoPath, err
}
log.Printf("ISO path identified as a Content Library path")
log.Printf("Finding the equivalent datastore path for the Content Library ISO file path")
libItem, err := d.FindContentLibraryItem(lib.library.ID, itemName)
if err != nil {
log.Printf("[WARN] Couldn't find item %s: %s", itemName, err.Error())
return isoPath, err
}
datastoreName, err := d.GetDatastoreName(lib.library.Storage[0].DatastoreID)
if err != nil {
log.Printf("[WARN] Couldn't find datastore name for library %s", libraryName)
return isoPath, err
}
libItemDir := fmt.Sprintf("[%s] contentlib-%s/%s", datastoreName, lib.library.ID, libItem.ID)
isoFilePath, err := d.GetDatastoreFilePath(lib.library.Storage[0].DatastoreID, libItemDir, isoFile)
if err != nil {
log.Printf("[WARN] Couldn't find datastore ID path for %s", isoFile)
return isoPath, err
}
_ = d.restClient.Logout(d.ctx)
return path.Join(libItemDir, isoFilePath), nil
}
type LibraryFilePath struct {
path string
}
func (l *LibraryFilePath) Validate() error {
l.path = strings.TrimLeft(l.path, "/")
parts := strings.Split(l.path, "/")
if len(parts) != 3 {
return fmt.Errorf("Not a valid Content Library File path. The path must contain the nanmes for the library, item and file.")
}
return nil
}
func (l *LibraryFilePath) GetLibraryName() string {
return strings.Split(l.path, "/")[0]
}
func (l *LibraryFilePath) GetLibraryItemName() string {
return strings.Split(l.path, "/")[1]
}
func (l *LibraryFilePath) GetFileName() string {
return strings.Split(l.path, "/")[2]
}

View File

@ -0,0 +1,77 @@
package driver
import (
"fmt"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type Network struct {
driver *VCenterDriver
network object.NetworkReference
}
func (d *VCenterDriver) NewNetwork(ref *types.ManagedObjectReference) *Network {
return &Network{
network: object.NewNetwork(d.client.Client, *ref),
driver: d,
}
}
func (d *VCenterDriver) FindNetwork(name string) (*Network, error) {
n, err := d.finder.Network(d.ctx, name)
if err != nil {
return nil, err
}
return &Network{
network: n,
driver: d,
}, nil
}
func (d *VCenterDriver) FindNetworks(name string) ([]*Network, error) {
ns, err := d.finder.NetworkList(d.ctx, name)
if err != nil {
return nil, err
}
var networks []*Network
for _, n := range ns {
networks = append(networks, &Network{
network: n,
driver: d,
})
}
return networks, nil
}
func (n *Network) Info(params ...string) (*mo.Network, error) {
var p []string
if len(params) == 0 {
p = []string{"*"}
} else {
p = params
}
var info mo.Network
network, ok := n.network.(*object.Network)
if !ok {
return nil, fmt.Errorf("unexpected %t network object type", n.network)
}
err := network.Properties(n.driver.ctx, network.Reference(), p, &info)
if err != nil {
return nil, err
}
return &info, nil
}
type MultipleNetworkFoundError struct {
path string
append string
}
func (e *MultipleNetworkFoundError) Error() string {
return fmt.Sprintf("path '%s' resolves to multiple networks. %s", e.path, e.append)
}

View File

@ -0,0 +1,91 @@
package driver
import (
"fmt"
"log"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type ResourcePool struct {
pool *object.ResourcePool
driver *VCenterDriver
}
func (d *VCenterDriver) NewResourcePool(ref *types.ManagedObjectReference) *ResourcePool {
return &ResourcePool{
pool: object.NewResourcePool(d.client.Client, *ref),
driver: d,
}
}
func (d *VCenterDriver) FindResourcePool(cluster string, host string, name string) (*ResourcePool, error) {
var res string
if cluster != "" {
res = cluster
} else {
res = host
}
resourcePath := fmt.Sprintf("%v/Resources/%v", res, name)
p, err := d.finder.ResourcePool(d.ctx, resourcePath)
if err != nil {
log.Printf("[WARN] %s not found. Looking for default resource pool.", resourcePath)
dp, dperr := d.finder.DefaultResourcePool(d.ctx)
if _, ok := dperr.(*find.NotFoundError); ok {
// VirtualApp extends ResourcePool, so it should support VirtualApp types.
vapp, verr := d.finder.VirtualApp(d.ctx, name)
if verr != nil {
return nil, err
}
dp = vapp.ResourcePool
} else if dperr != nil {
return nil, err
}
p = dp
}
return &ResourcePool{
pool: p,
driver: d,
}, nil
}
func (p *ResourcePool) Info(params ...string) (*mo.ResourcePool, error) {
var params2 []string
if len(params) == 0 {
params2 = []string{"*"}
} else {
params2 = params
}
var info mo.ResourcePool
err := p.pool.Properties(p.driver.ctx, p.pool.Reference(), params2, &info)
if err != nil {
return nil, err
}
return &info, nil
}
func (p *ResourcePool) Path() (string, error) {
poolInfo, err := p.Info("name", "parent")
if err != nil {
return "", err
}
if poolInfo.Parent.Type == "ComputeResource" {
return "", nil
} else {
parent := p.driver.NewResourcePool(poolInfo.Parent)
parentPath, err := parent.Path()
if err != nil {
return "", err
}
if parentPath == "" {
return poolInfo.Name, nil
} else {
return fmt.Sprintf("%v/%v", parentPath, poolInfo.Name), nil
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,89 @@
package driver
import (
"errors"
"github.com/vmware/govmomi/vim25/types"
)
var (
ErrNoSataController = errors.New("no available SATA controller")
)
func (vm *VirtualMachineDriver) AddSATAController() error {
sata := &types.VirtualAHCIController{}
return vm.addDevice(sata)
}
func (vm *VirtualMachineDriver) FindSATAController() (*types.VirtualAHCIController, error) {
l, err := vm.Devices()
if err != nil {
return nil, err
}
c := l.PickController((*types.VirtualAHCIController)(nil))
if c == nil {
return nil, ErrNoSataController
}
return c.(*types.VirtualAHCIController), nil
}
func (vm *VirtualMachineDriver) CreateCdrom(c *types.VirtualController) (*types.VirtualCdrom, error) {
l, err := vm.Devices()
if err != nil {
return nil, err
}
device := &types.VirtualCdrom{}
l.AssignController(device, c)
device.Backing = &types.VirtualCdromAtapiBackingInfo{
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{},
}
device.Connectable = &types.VirtualDeviceConnectInfo{
AllowGuestControl: true,
Connected: true,
StartConnected: true,
}
return device, nil
}
func (vm *VirtualMachineDriver) RemoveCdroms() error {
devices, err := vm.Devices()
if err != nil {
return err
}
cdroms := devices.SelectByType((*types.VirtualCdrom)(nil))
if err = vm.RemoveDevice(true, cdroms...); err != nil {
return err
}
sata := devices.SelectByType((*types.VirtualAHCIController)(nil))
if err = vm.RemoveDevice(true, sata...); err != nil {
return err
}
return nil
}
func (vm *VirtualMachineDriver) EjectCdroms() error {
devices, err := vm.Devices()
if err != nil {
return err
}
cdroms := devices.SelectByType((*types.VirtualCdrom)(nil))
for _, cd := range cdroms {
c := cd.(*types.VirtualCdrom)
c.Backing = &types.VirtualCdromRemotePassthroughBackingInfo{}
c.Connectable = &types.VirtualDeviceConnectInfo{}
err := vm.vm.EditDevice(vm.driver.ctx, c)
if err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,39 @@
package driver
import (
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/types"
"golang.org/x/mobile/event/key"
)
type KeyInput struct {
Scancode key.Code
Alt bool
Ctrl bool
Shift bool
}
func (vm *VirtualMachineDriver) TypeOnKeyboard(input KeyInput) (int32, error) {
var spec types.UsbScanCodeSpec
spec.KeyEvents = append(spec.KeyEvents, types.UsbScanCodeSpecKeyEvent{
UsbHidCode: int32(input.Scancode)<<16 | 7,
Modifiers: &types.UsbScanCodeSpecModifierType{
LeftControl: &input.Ctrl,
LeftAlt: &input.Alt,
LeftShift: &input.Shift,
},
})
req := &types.PutUsbScanCodes{
This: vm.vm.Reference(),
Spec: spec,
}
resp, err := methods.PutUsbScanCodes(vm.driver.ctx, vm.driver.client.RoundTripper, req)
if err != nil {
return 0, err
}
return resp.Returnval, nil
}

View File

@ -0,0 +1,235 @@
package driver
import (
"context"
"net"
"time"
"github.com/vmware/govmomi/nfc"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/ovf"
"github.com/vmware/govmomi/vapi/vcenter"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type VirtualMachineMock struct {
DestroyError error
DestroyCalled bool
ConfigureError error
ConfigureCalled bool
ConfigureHardwareConfig *HardwareConfig
FindSATAControllerCalled bool
FindSATAControllerErr error
AddSATAControllerCalled bool
AddSATAControllerErr error
AddCdromCalled bool
AddCdromCalledTimes int
AddCdromErr error
AddCdromTypes []string
AddCdromPaths []string
GetDirCalled bool
GetDirResponse string
GetDirErr error
AddFloppyCalled bool
AddFloppyImagePath string
AddFloppyErr error
FloppyDevicesErr error
FloppyDevicesReturn object.VirtualDeviceList
FloppyDevicesCalled bool
RemoveDeviceErr error
RemoveDeviceCalled bool
RemoveDeviceKeepFiles bool
RemoveDeviceDevices []types.BaseVirtualDevice
EjectCdromsCalled bool
EjectCdromsErr error
RemoveCdromsCalled bool
RemoveCdromsErr error
CloneCalled bool
CloneConfig *CloneConfig
CloneError error
}
func (vm *VirtualMachineMock) Info(params ...string) (*mo.VirtualMachine, error) {
return nil, nil
}
func (vm *VirtualMachineMock) Devices() (object.VirtualDeviceList, error) {
return object.VirtualDeviceList{}, nil
}
func (vm *VirtualMachineMock) FloppyDevices() (object.VirtualDeviceList, error) {
vm.FloppyDevicesCalled = true
return vm.FloppyDevicesReturn, vm.FloppyDevicesErr
}
func (vm *VirtualMachineMock) Clone(ctx context.Context, config *CloneConfig) (VirtualMachine, error) {
vm.CloneCalled = true
vm.CloneConfig = config
return vm, vm.CloneError
}
func (vm *VirtualMachineMock) updateVAppConfig(ctx context.Context, newProps map[string]string) (*types.VmConfigSpec, error) {
return nil, nil
}
func (vm *VirtualMachineMock) AddPublicKeys(ctx context.Context, publicKeys string) error {
return nil
}
func (vm *VirtualMachineMock) Properties(ctx context.Context) (*mo.VirtualMachine, error) {
return nil, nil
}
func (vm *VirtualMachineMock) Destroy() error {
vm.DestroyCalled = true
if vm.DestroyError != nil {
return vm.DestroyError
}
return nil
}
func (vm *VirtualMachineMock) Configure(config *HardwareConfig) error {
vm.ConfigureCalled = true
vm.ConfigureHardwareConfig = config
if vm.ConfigureError != nil {
return vm.ConfigureError
}
return nil
}
func (vm *VirtualMachineMock) Customize(spec types.CustomizationSpec) error {
return nil
}
func (vm *VirtualMachineMock) ResizeDisk(diskSize int64) ([]types.BaseVirtualDeviceConfigSpec, error) {
return nil, nil
}
func (vm *VirtualMachineMock) PowerOn() error {
return nil
}
func (vm *VirtualMachineMock) WaitForIP(ctx context.Context, ipNet *net.IPNet) (string, error) {
return "", nil
}
func (vm *VirtualMachineMock) PowerOff() error {
return nil
}
func (vm *VirtualMachineMock) IsPoweredOff() (bool, error) {
return false, nil
}
func (vm *VirtualMachineMock) StartShutdown() error {
return nil
}
func (vm *VirtualMachineMock) WaitForShutdown(ctx context.Context, timeout time.Duration) error {
return nil
}
func (vm *VirtualMachineMock) CreateSnapshot(name string) error {
return nil
}
func (vm *VirtualMachineMock) ConvertToTemplate() error {
return nil
}
func (vm *VirtualMachineMock) ImportOvfToContentLibrary(ovf vcenter.OVF) error {
return nil
}
func (vm *VirtualMachineMock) ImportToContentLibrary(template vcenter.Template) error {
return nil
}
func (vm *VirtualMachineMock) GetDir() (string, error) {
vm.GetDirCalled = true
return vm.GetDirResponse, vm.GetDirErr
}
func (vm *VirtualMachineMock) AddCdrom(cdromType string, isoPath string) error {
vm.AddCdromCalledTimes++
vm.AddCdromCalled = true
vm.AddCdromTypes = append(vm.AddCdromTypes, cdromType)
vm.AddCdromPaths = append(vm.AddCdromPaths, isoPath)
return vm.AddCdromErr
}
func (vm *VirtualMachineMock) AddFloppy(imgPath string) error {
vm.AddFloppyCalled = true
vm.AddFloppyImagePath = imgPath
return vm.AddFloppyErr
}
func (vm *VirtualMachineMock) SetBootOrder(order []string) error {
return nil
}
func (vm *VirtualMachineMock) RemoveDevice(keepFiles bool, device ...types.BaseVirtualDevice) error {
vm.RemoveDeviceCalled = true
vm.RemoveDeviceKeepFiles = keepFiles
vm.RemoveDeviceDevices = device
return vm.RemoveDeviceErr
}
func (vm *VirtualMachineMock) addDevice(device types.BaseVirtualDevice) error {
return nil
}
func (vm *VirtualMachineMock) AddConfigParams(params map[string]string, info *types.ToolsConfigInfo) error {
return nil
}
func (vm *VirtualMachineMock) Export() (*nfc.Lease, error) {
return nil, nil
}
func (vm *VirtualMachineMock) CreateDescriptor(m *ovf.Manager, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) {
return nil, nil
}
func (vm *VirtualMachineMock) NewOvfManager() *ovf.Manager {
return nil
}
func (vm *VirtualMachineMock) GetOvfExportOptions(m *ovf.Manager) ([]types.OvfOptionInfo, error) {
return nil, nil
}
func (vm *VirtualMachineMock) AddSATAController() error {
vm.AddSATAControllerCalled = true
return vm.AddSATAControllerErr
}
func (vm *VirtualMachineMock) FindSATAController() (*types.VirtualAHCIController, error) {
vm.FindSATAControllerCalled = true
return nil, vm.FindSATAControllerErr
}
func (vm *VirtualMachineMock) CreateCdrom(c *types.VirtualController) (*types.VirtualCdrom, error) {
return nil, nil
}
func (vm *VirtualMachineMock) RemoveCdroms() error {
vm.RemoveCdromsCalled = true
return vm.RemoveCdromsErr
}
func (vm *VirtualMachineMock) EjectCdroms() error {
vm.EjectCdromsCalled = true
return vm.EjectCdromsErr
}

View File

@ -0,0 +1,179 @@
package iso
import (
"context"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
type Builder struct {
config Config
runner multistep.Runner
}
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
warnings, errs := b.config.Prepare(raws...)
if errs != nil {
return nil, warnings, errs
}
return nil, warnings, nil
}
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
state := new(multistep.BasicStateBag)
state.Put("debug", b.config.PackerDebug)
state.Put("hook", hook)
state.Put("ui", ui)
var steps []multistep.Step
steps = append(steps,
&common.StepConnect{
Config: &b.config.ConnectConfig,
},
&common.StepDownload{
DownloadStep: &commonsteps.StepDownload{
Checksum: b.config.ISOChecksum,
Description: "ISO",
Extension: b.config.TargetExtension,
ResultKey: "iso_path",
TargetPath: b.config.TargetPath,
Url: b.config.ISOUrls,
},
Url: b.config.ISOUrls,
ResultKey: "iso_path",
Datastore: b.config.Datastore,
Host: b.config.Host,
},
&commonsteps.StepCreateCD{
Files: b.config.CDConfig.CDFiles,
Label: b.config.CDConfig.CDLabel,
},
&common.StepRemoteUpload{
Datastore: b.config.Datastore,
Host: b.config.Host,
SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads,
},
&StepCreateVM{
Config: &b.config.CreateConfig,
Location: &b.config.LocationConfig,
Force: b.config.PackerConfig.PackerForce,
},
&common.StepConfigureHardware{
Config: &b.config.HardwareConfig,
},
&common.StepAddCDRom{
Config: &b.config.CDRomConfig,
},
&common.StepConfigParams{
Config: &b.config.ConfigParamsConfig,
},
&commonsteps.StepCreateFloppy{
Files: b.config.FloppyFiles,
Directories: b.config.FloppyDirectories,
Label: b.config.FloppyLabel,
},
&common.StepAddFloppy{
Config: &b.config.FloppyConfig,
Datastore: b.config.Datastore,
Host: b.config.Host,
SetHostForDatastoreUploads: b.config.SetHostForDatastoreUploads,
},
&common.StepHTTPIPDiscover{
HTTPIP: b.config.BootConfig.HTTPIP,
Network: b.config.WaitIpConfig.GetIPNet(),
},
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
&common.StepRun{
Config: &b.config.RunConfig,
SetOrder: true,
},
&common.StepBootCommand{
Config: &b.config.BootConfig,
Ctx: b.config.ctx,
VMName: b.config.VMName,
},
)
if b.config.Comm.Type != "none" {
steps = append(steps,
&common.StepWaitForIp{
Config: &b.config.WaitIpConfig,
},
&communicator.StepConnect{
Config: &b.config.Comm,
Host: common.CommHost(b.config.Comm.Host()),
SSHConfig: b.config.Comm.SSHConfigFunc(),
},
&commonsteps.StepProvision{},
)
}
steps = append(steps,
&common.StepShutdown{
Config: &b.config.ShutdownConfig,
},
&common.StepRemoveFloppy{
Datastore: b.config.Datastore,
Host: b.config.Host,
},
&common.StepRemoveCDRom{
Config: &b.config.RemoveCDRomConfig,
},
&common.StepCreateSnapshot{
CreateSnapshot: b.config.CreateSnapshot,
},
&common.StepConvertToTemplate{
ConvertToTemplate: b.config.ConvertToTemplate,
},
)
if b.config.ContentLibraryDestinationConfig != nil {
steps = append(steps, &common.StepImportToContentLibrary{
ContentLibConfig: b.config.ContentLibraryDestinationConfig,
})
}
if b.config.Export != nil {
steps = append(steps, &common.StepExport{
Name: b.config.Export.Name,
Force: b.config.Export.Force,
Images: b.config.Export.Images,
Manifest: b.config.Export.Manifest,
OutputDir: b.config.Export.OutputDir.OutputDir,
Options: b.config.Export.Options,
})
}
b.runner = commonsteps.NewRunnerWithPauseFn(steps, b.config.PackerConfig, ui, state)
b.runner.Run(ctx, state)
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
if _, ok := state.GetOk("vm"); !ok {
return nil, nil
}
artifact := &common.Artifact{
Name: b.config.VMName,
VM: state.Get("vm").(*driver.VirtualMachineDriver),
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
}
if b.config.Export != nil {
artifact.Outconfig = &b.config.Export.OutputDir
}
return artifact, nil
}

View File

@ -0,0 +1,107 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type Config
package iso
import (
packerCommon "github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/communicator"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
)
type Config struct {
packerCommon.PackerConfig `mapstructure:",squash"`
commonsteps.HTTPConfig `mapstructure:",squash"`
commonsteps.CDConfig `mapstructure:",squash"`
common.ConnectConfig `mapstructure:",squash"`
CreateConfig `mapstructure:",squash"`
common.LocationConfig `mapstructure:",squash"`
common.HardwareConfig `mapstructure:",squash"`
common.ConfigParamsConfig `mapstructure:",squash"`
commonsteps.ISOConfig `mapstructure:",squash"`
common.CDRomConfig `mapstructure:",squash"`
common.RemoveCDRomConfig `mapstructure:",squash"`
common.FloppyConfig `mapstructure:",squash"`
common.RunConfig `mapstructure:",squash"`
common.BootConfig `mapstructure:",squash"`
common.WaitIpConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
common.ShutdownConfig `mapstructure:",squash"`
// Create a snapshot when set to `true`, so the VM can be used as a base
// for linked clones. Defaults to `false`.
CreateSnapshot bool `mapstructure:"create_snapshot"`
// Convert VM to a template. Defaults to `false`.
ConvertToTemplate bool `mapstructure:"convert_to_template"`
// Configuration for exporting VM to an ovf file.
// The VM will not be exported if no [Export Configuration](#export-configuration) is specified.
Export *common.ExportConfig `mapstructure:"export"`
// Configuration for importing the VM template to a Content Library.
// The VM template will not be imported if no [Content Library Import Configuration](#content-library-import-configuration) is specified.
// The import doesn't work if [convert_to_template](#convert_to_template) is set to true.
ContentLibraryDestinationConfig *common.ContentLibraryDestinationConfig `mapstructure:"content_library_destination"`
ctx interpolate.Context
}
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
err := config.Decode(c, &config.DecodeOpts{
PluginType: common.BuilderId,
Interpolate: true,
InterpolateContext: &c.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"boot_command",
},
},
}, raws...)
if err != nil {
return nil, err
}
warnings := make([]string, 0)
errs := new(packersdk.MultiError)
if c.ISOUrls != nil || c.RawSingleISOUrl != "" {
isoWarnings, isoErrs := c.ISOConfig.Prepare(&c.ctx)
warnings = append(warnings, isoWarnings...)
errs = packersdk.MultiErrorAppend(errs, isoErrs...)
}
errs = packersdk.MultiErrorAppend(errs, c.ConnectConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.CreateConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.LocationConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.HardwareConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
errs = packersdk.MultiErrorAppend(errs, c.CDRomConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.CDConfig.Prepare(&c.ctx)...)
errs = packersdk.MultiErrorAppend(errs, c.BootConfig.Prepare(&c.ctx)...)
errs = packersdk.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
errs = packersdk.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
shutdownWarnings, shutdownErrs := c.ShutdownConfig.Prepare(c.Comm)
warnings = append(warnings, shutdownWarnings...)
errs = packersdk.MultiErrorAppend(errs, shutdownErrs...)
if c.Export != nil {
errs = packersdk.MultiErrorAppend(errs, c.Export.Prepare(&c.ctx, &c.LocationConfig, &c.PackerConfig)...)
}
if c.ContentLibraryDestinationConfig != nil {
errs = packersdk.MultiErrorAppend(errs, c.ContentLibraryDestinationConfig.Prepare(&c.LocationConfig)...)
}
if len(errs.Errors) > 0 {
return warnings, errs
}
return warnings, nil
}

View File

@ -0,0 +1,286 @@
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
package iso
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"`
CDLabel *string `mapstructure:"cd_label" cty:"cd_label" hcl:"cd_label"`
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server" hcl:"vcenter_server"`
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
InsecureConnection *bool `mapstructure:"insecure_connection" cty:"insecure_connection" hcl:"insecure_connection"`
Datacenter *string `mapstructure:"datacenter" cty:"datacenter" hcl:"datacenter"`
Version *uint `mapstructure:"vm_version" cty:"vm_version" hcl:"vm_version"`
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type" hcl:"guest_os_type"`
DiskControllerType []string `mapstructure:"disk_controller_type" cty:"disk_controller_type" hcl:"disk_controller_type"`
Storage []common.FlatDiskConfig `mapstructure:"storage" cty:"storage" hcl:"storage"`
NICs []FlatNIC `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
USBController []string `mapstructure:"usb_controller" cty:"usb_controller" hcl:"usb_controller"`
Notes *string `mapstructure:"notes" cty:"notes" hcl:"notes"`
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
Folder *string `mapstructure:"folder" cty:"folder" hcl:"folder"`
Cluster *string `mapstructure:"cluster" cty:"cluster" hcl:"cluster"`
Host *string `mapstructure:"host" cty:"host" hcl:"host"`
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool" hcl:"resource_pool"`
Datastore *string `mapstructure:"datastore" cty:"datastore" hcl:"datastore"`
SetHostForDatastoreUploads *bool `mapstructure:"set_host_for_datastore_uploads" cty:"set_host_for_datastore_uploads" hcl:"set_host_for_datastore_uploads"`
CPUs *int32 `mapstructure:"CPUs" cty:"CPUs" hcl:"CPUs"`
CpuCores *int32 `mapstructure:"cpu_cores" cty:"cpu_cores" hcl:"cpu_cores"`
CPUReservation *int64 `mapstructure:"CPU_reservation" cty:"CPU_reservation" hcl:"CPU_reservation"`
CPULimit *int64 `mapstructure:"CPU_limit" cty:"CPU_limit" hcl:"CPU_limit"`
CpuHotAddEnabled *bool `mapstructure:"CPU_hot_plug" cty:"CPU_hot_plug" hcl:"CPU_hot_plug"`
RAM *int64 `mapstructure:"RAM" cty:"RAM" hcl:"RAM"`
RAMReservation *int64 `mapstructure:"RAM_reservation" cty:"RAM_reservation" hcl:"RAM_reservation"`
RAMReserveAll *bool `mapstructure:"RAM_reserve_all" cty:"RAM_reserve_all" hcl:"RAM_reserve_all"`
MemoryHotAddEnabled *bool `mapstructure:"RAM_hot_plug" cty:"RAM_hot_plug" hcl:"RAM_hot_plug"`
VideoRAM *int64 `mapstructure:"video_ram" cty:"video_ram" hcl:"video_ram"`
VGPUProfile *string `mapstructure:"vgpu_profile" cty:"vgpu_profile" hcl:"vgpu_profile"`
NestedHV *bool `mapstructure:"NestedHV" cty:"NestedHV" hcl:"NestedHV"`
Firmware *string `mapstructure:"firmware" cty:"firmware" hcl:"firmware"`
ForceBIOSSetup *bool `mapstructure:"force_bios_setup" cty:"force_bios_setup" hcl:"force_bios_setup"`
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters" hcl:"configuration_parameters"`
ToolsSyncTime *bool `mapstructure:"tools_sync_time" cty:"tools_sync_time" hcl:"tools_sync_time"`
ToolsUpgradePolicy *bool `mapstructure:"tools_upgrade_policy" cty:"tools_upgrade_policy" hcl:"tools_upgrade_policy"`
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
CdromType *string `mapstructure:"cdrom_type" cty:"cdrom_type" hcl:"cdrom_type"`
ISOPaths []string `mapstructure:"iso_paths" cty:"iso_paths" hcl:"iso_paths"`
RemoveCdrom *bool `mapstructure:"remove_cdrom" cty:"remove_cdrom" hcl:"remove_cdrom"`
FloppyIMGPath *string `mapstructure:"floppy_img_path" cty:"floppy_img_path" hcl:"floppy_img_path"`
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
BootOrder *string `mapstructure:"boot_order" cty:"boot_order" hcl:"boot_order"`
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
HTTPIP *string `mapstructure:"http_ip" cty:"http_ip" hcl:"http_ip"`
WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout" hcl:"ip_wait_timeout"`
SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout" hcl:"ip_settle_timeout"`
WaitAddress *string `mapstructure:"ip_wait_address" cty:"ip_wait_address" hcl:"ip_wait_address"`
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
Command *string `mapstructure:"shutdown_command" cty:"shutdown_command" hcl:"shutdown_command"`
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout" hcl:"shutdown_timeout"`
DisableShutdown *bool `mapstructure:"disable_shutdown" cty:"disable_shutdown" hcl:"disable_shutdown"`
CreateSnapshot *bool `mapstructure:"create_snapshot" cty:"create_snapshot" hcl:"create_snapshot"`
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template" hcl:"convert_to_template"`
Export *common.FlatExportConfig `mapstructure:"export" cty:"export" hcl:"export"`
ContentLibraryDestinationConfig *common.FlatContentLibraryDestinationConfig `mapstructure:"content_library_destination" cty:"content_library_destination" hcl:"content_library_destination"`
}
// FlatMapstructure returns a new FlatConfig.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
"http_interface": &hcldec.AttrSpec{Name: "http_interface", Type: cty.String, Required: false},
"cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false},
"cd_label": &hcldec.AttrSpec{Name: "cd_label", Type: cty.String, Required: false},
"vcenter_server": &hcldec.AttrSpec{Name: "vcenter_server", Type: cty.String, Required: false},
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
"insecure_connection": &hcldec.AttrSpec{Name: "insecure_connection", Type: cty.Bool, Required: false},
"datacenter": &hcldec.AttrSpec{Name: "datacenter", Type: cty.String, Required: false},
"vm_version": &hcldec.AttrSpec{Name: "vm_version", Type: cty.Number, Required: false},
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
"disk_controller_type": &hcldec.AttrSpec{Name: "disk_controller_type", Type: cty.List(cty.String), Required: false},
"storage": &hcldec.BlockListSpec{TypeName: "storage", Nested: hcldec.ObjectSpec((*common.FlatDiskConfig)(nil).HCL2Spec())},
"network_adapters": &hcldec.BlockListSpec{TypeName: "network_adapters", Nested: hcldec.ObjectSpec((*FlatNIC)(nil).HCL2Spec())},
"usb_controller": &hcldec.AttrSpec{Name: "usb_controller", Type: cty.List(cty.String), Required: false},
"notes": &hcldec.AttrSpec{Name: "notes", Type: cty.String, Required: false},
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
"folder": &hcldec.AttrSpec{Name: "folder", Type: cty.String, Required: false},
"cluster": &hcldec.AttrSpec{Name: "cluster", Type: cty.String, Required: false},
"host": &hcldec.AttrSpec{Name: "host", Type: cty.String, Required: false},
"resource_pool": &hcldec.AttrSpec{Name: "resource_pool", Type: cty.String, Required: false},
"datastore": &hcldec.AttrSpec{Name: "datastore", Type: cty.String, Required: false},
"set_host_for_datastore_uploads": &hcldec.AttrSpec{Name: "set_host_for_datastore_uploads", Type: cty.Bool, Required: false},
"CPUs": &hcldec.AttrSpec{Name: "CPUs", Type: cty.Number, Required: false},
"cpu_cores": &hcldec.AttrSpec{Name: "cpu_cores", Type: cty.Number, Required: false},
"CPU_reservation": &hcldec.AttrSpec{Name: "CPU_reservation", Type: cty.Number, Required: false},
"CPU_limit": &hcldec.AttrSpec{Name: "CPU_limit", Type: cty.Number, Required: false},
"CPU_hot_plug": &hcldec.AttrSpec{Name: "CPU_hot_plug", Type: cty.Bool, Required: false},
"RAM": &hcldec.AttrSpec{Name: "RAM", Type: cty.Number, Required: false},
"RAM_reservation": &hcldec.AttrSpec{Name: "RAM_reservation", Type: cty.Number, Required: false},
"RAM_reserve_all": &hcldec.AttrSpec{Name: "RAM_reserve_all", Type: cty.Bool, Required: false},
"RAM_hot_plug": &hcldec.AttrSpec{Name: "RAM_hot_plug", Type: cty.Bool, Required: false},
"video_ram": &hcldec.AttrSpec{Name: "video_ram", Type: cty.Number, Required: false},
"vgpu_profile": &hcldec.AttrSpec{Name: "vgpu_profile", Type: cty.String, Required: false},
"NestedHV": &hcldec.AttrSpec{Name: "NestedHV", Type: cty.Bool, Required: false},
"firmware": &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false},
"force_bios_setup": &hcldec.AttrSpec{Name: "force_bios_setup", Type: cty.Bool, Required: false},
"configuration_parameters": &hcldec.AttrSpec{Name: "configuration_parameters", Type: cty.Map(cty.String), Required: false},
"tools_sync_time": &hcldec.AttrSpec{Name: "tools_sync_time", Type: cty.Bool, Required: false},
"tools_upgrade_policy": &hcldec.AttrSpec{Name: "tools_upgrade_policy", Type: cty.Bool, Required: false},
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
"iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false},
"iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", Type: cty.String, Required: false},
"cdrom_type": &hcldec.AttrSpec{Name: "cdrom_type", Type: cty.String, Required: false},
"iso_paths": &hcldec.AttrSpec{Name: "iso_paths", Type: cty.List(cty.String), Required: false},
"remove_cdrom": &hcldec.AttrSpec{Name: "remove_cdrom", Type: cty.Bool, Required: false},
"floppy_img_path": &hcldec.AttrSpec{Name: "floppy_img_path", Type: cty.String, Required: false},
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, Required: false},
"boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.String, Required: false},
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
"http_ip": &hcldec.AttrSpec{Name: "http_ip", Type: cty.String, Required: false},
"ip_wait_timeout": &hcldec.AttrSpec{Name: "ip_wait_timeout", Type: cty.String, Required: false},
"ip_settle_timeout": &hcldec.AttrSpec{Name: "ip_settle_timeout", Type: cty.String, Required: false},
"ip_wait_address": &hcldec.AttrSpec{Name: "ip_wait_address", Type: cty.String, Required: false},
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
"winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false},
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
"disable_shutdown": &hcldec.AttrSpec{Name: "disable_shutdown", Type: cty.Bool, Required: false},
"create_snapshot": &hcldec.AttrSpec{Name: "create_snapshot", Type: cty.Bool, Required: false},
"convert_to_template": &hcldec.AttrSpec{Name: "convert_to_template", Type: cty.Bool, Required: false},
"export": &hcldec.BlockSpec{TypeName: "export", Nested: hcldec.ObjectSpec((*common.FlatExportConfig)(nil).HCL2Spec())},
"content_library_destination": &hcldec.BlockSpec{TypeName: "content_library_destination", Nested: hcldec.ObjectSpec((*common.FlatContentLibraryDestinationConfig)(nil).HCL2Spec())},
}
return s
}

View File

@ -0,0 +1,188 @@
//go:generate struct-markdown
//go:generate mapstructure-to-hcl2 -type NIC,CreateConfig
package iso
import (
"context"
"fmt"
"path"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
)
// Defines a Network Adapter
//
// Example that creates two network adapters:
//
// In JSON:
// ```json
// "network_adapters": [
// {
// "network": "VM Network",
// "network_card": "vmxnet3"
// },
// {
// "network": "OtherNetwork",
// "network_card": "vmxnet3"
// }
// ],
// ```
// In HCL2:
// ```hcl
// network_adapters {
// network = "VM Network"
// network_card = "vmxnet3"
// }
// network_adapters {
// network = "OtherNetwork"
// network_card = "vmxnet3"
// }
// ```
type NIC struct {
// Set the network in which the VM will be connected to. If no network is
// specified, `host` must be specified to allow Packer to look for the
// available network. If the network is inside a network folder in vCenter,
// you need to provide the full path to the network.
Network string `mapstructure:"network"`
// Set VM network card type. Example `vmxnet3`.
NetworkCard string `mapstructure:"network_card" required:"true"`
// Set network card MAC address
MacAddress string `mapstructure:"mac_address"`
// Enable DirectPath I/O passthrough
Passthrough *bool `mapstructure:"passthrough"`
}
type CreateConfig struct {
// Set VM hardware version. Defaults to the most current VM hardware
// version supported by vCenter. See
// [VMWare article 1003746](https://kb.vmware.com/s/article/1003746) for
// the full list of supported VM hardware versions.
Version uint `mapstructure:"vm_version"`
// Set VM OS type. Defaults to `otherGuest`. See [
// here](https://code.vmware.com/apis/358/vsphere/doc/vim.vm.GuestOsDescriptor.GuestOsIdentifier.html)
// for a full list of possible values.
GuestOSType string `mapstructure:"guest_os_type"`
StorageConfig common.StorageConfig `mapstructure:",squash"`
// Network adapters
NICs []NIC `mapstructure:"network_adapters"`
// Create USB controllers for the virtual machine. "usb" for a usb 2.0 controller. "xhci" for a usb 3.0 controller. There can only be at most one of each.
USBController []string `mapstructure:"usb_controller"`
// VM notes.
Notes string `mapstructure:"notes"`
}
func (c *CreateConfig) Prepare() []error {
var errs []error
if len(c.StorageConfig.DiskControllerType) == 0 {
c.StorageConfig.DiskControllerType = append(c.StorageConfig.DiskControllerType, "")
}
// there should be at least one
if len(c.StorageConfig.Storage) == 0 {
errs = append(errs, fmt.Errorf("no storage devices have been defined"))
}
errs = append(errs, c.StorageConfig.Prepare()...)
if c.GuestOSType == "" {
c.GuestOSType = "otherGuest"
}
usbCount := 0
xhciCount := 0
for i, s := range c.USBController {
switch s {
// 1 and true for backwards compatibility
case "usb", "1", "true":
usbCount++
case "xhci":
xhciCount++
// 0 and false for backwards compatibility
case "false", "0":
continue
default:
errs = append(errs, fmt.Errorf("usb_controller[%d] references an unknown usb controller", i))
}
}
if usbCount > 1 || xhciCount > 1 {
errs = append(errs, fmt.Errorf("there can only be one usb controller and one xhci controller"))
}
return errs
}
type StepCreateVM struct {
Config *CreateConfig
Location *common.LocationConfig
Force bool
}
func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
d := state.Get("driver").(driver.Driver)
vmPath := path.Join(s.Location.Folder, s.Location.VMName)
err := d.PreCleanVM(ui, vmPath, s.Force)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
ui.Say("Creating VM...")
// add network/network card an the first nic for backwards compatibility in the type is defined
var networkCards []driver.NIC
for _, nic := range s.Config.NICs {
networkCards = append(networkCards, driver.NIC{
Network: nic.Network,
NetworkCard: nic.NetworkCard,
MacAddress: nic.MacAddress,
Passthrough: nic.Passthrough,
})
}
// add disk as the first drive for backwards compatibility if the type is defined
var disks []driver.Disk
for _, disk := range s.Config.StorageConfig.Storage {
disks = append(disks, driver.Disk{
DiskSize: disk.DiskSize,
DiskEagerlyScrub: disk.DiskEagerlyScrub,
DiskThinProvisioned: disk.DiskThinProvisioned,
ControllerIndex: disk.DiskControllerIndex,
})
}
vm, err := d.CreateVM(&driver.CreateConfig{
StorageConfig: driver.StorageConfig{
DiskControllerType: s.Config.StorageConfig.DiskControllerType,
Storage: disks,
},
Annotation: s.Config.Notes,
Name: s.Location.VMName,
Folder: s.Location.Folder,
Cluster: s.Location.Cluster,
Host: s.Location.Host,
ResourcePool: s.Location.ResourcePool,
Datastore: s.Location.Datastore,
GuestOS: s.Config.GuestOSType,
NICs: networkCards,
USBController: s.Config.USBController,
Version: s.Config.Version,
})
if err != nil {
state.Put("error", fmt.Errorf("error creating vm: %v", err))
return multistep.ActionHalt
}
state.Put("vm", vm)
return multistep.ActionContinue
}
func (s *StepCreateVM) Cleanup(state multistep.StateBag) {
common.CleanupVM(state)
}

View File

@ -0,0 +1,73 @@
// Code generated by "mapstructure-to-hcl2 -type NIC,CreateConfig"; DO NOT EDIT.
package iso
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
"github.com/zclconf/go-cty/cty"
)
// FlatCreateConfig is an auto-generated flat version of CreateConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatCreateConfig struct {
Version *uint `mapstructure:"vm_version" cty:"vm_version" hcl:"vm_version"`
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type" hcl:"guest_os_type"`
DiskControllerType []string `mapstructure:"disk_controller_type" cty:"disk_controller_type" hcl:"disk_controller_type"`
Storage []common.FlatDiskConfig `mapstructure:"storage" cty:"storage" hcl:"storage"`
NICs []FlatNIC `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
USBController []string `mapstructure:"usb_controller" cty:"usb_controller" hcl:"usb_controller"`
Notes *string `mapstructure:"notes" cty:"notes" hcl:"notes"`
}
// FlatMapstructure returns a new FlatCreateConfig.
// FlatCreateConfig is an auto-generated flat version of CreateConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*CreateConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatCreateConfig)
}
// HCL2Spec returns the hcl spec of a CreateConfig.
// This spec is used by HCL to read the fields of CreateConfig.
// The decoded values from this spec will then be applied to a FlatCreateConfig.
func (*FlatCreateConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"vm_version": &hcldec.AttrSpec{Name: "vm_version", Type: cty.Number, Required: false},
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
"disk_controller_type": &hcldec.AttrSpec{Name: "disk_controller_type", Type: cty.List(cty.String), Required: false},
"storage": &hcldec.BlockListSpec{TypeName: "storage", Nested: hcldec.ObjectSpec((*common.FlatDiskConfig)(nil).HCL2Spec())},
"network_adapters": &hcldec.BlockListSpec{TypeName: "network_adapters", Nested: hcldec.ObjectSpec((*FlatNIC)(nil).HCL2Spec())},
"usb_controller": &hcldec.AttrSpec{Name: "usb_controller", Type: cty.List(cty.String), Required: false},
"notes": &hcldec.AttrSpec{Name: "notes", Type: cty.String, Required: false},
}
return s
}
// FlatNIC is an auto-generated flat version of NIC.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatNIC struct {
Network *string `mapstructure:"network" cty:"network" hcl:"network"`
NetworkCard *string `mapstructure:"network_card" required:"true" cty:"network_card" hcl:"network_card"`
MacAddress *string `mapstructure:"mac_address" cty:"mac_address" hcl:"mac_address"`
Passthrough *bool `mapstructure:"passthrough" cty:"passthrough" hcl:"passthrough"`
}
// FlatMapstructure returns a new FlatNIC.
// FlatNIC is an auto-generated flat version of NIC.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*NIC) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatNIC)
}
// HCL2Spec returns the hcl spec of a NIC.
// This spec is used by HCL to read the fields of NIC.
// The decoded values from this spec will then be applied to a FlatNIC.
func (*FlatNIC) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"network": &hcldec.AttrSpec{Name: "network", Type: cty.String, Required: false},
"network_card": &hcldec.AttrSpec{Name: "network_card", Type: cty.String, Required: false},
"mac_address": &hcldec.AttrSpec{Name: "mac_address", Type: cty.String, Required: false},
"passthrough": &hcldec.AttrSpec{Name: "passthrough", Type: cty.Bool, Required: false},
}
return s
}

View File

@ -0,0 +1,160 @@
//go:generate mapstructure-to-hcl2 -type Config
package vsphere_template
import (
"context"
"errors"
"fmt"
"net/url"
"strings"
"time"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/common"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
vsphere "github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
vspherepost "github.com/hashicorp/packer-plugin-vsphere/post-processor/vsphere"
"github.com/vmware/govmomi"
)
const (
// BuilderId for the local artifacts
BuilderIdESX = "mitchellh.vmware-esx"
ArtifactConfFormat = "artifact.conf.format"
ArtifactConfKeepRegistered = "artifact.conf.keep_registered"
ArtifactConfSkipExport = "artifact.conf.skip_export"
)
var builtins = map[string]string{
vspherepost.BuilderId: "vmware",
BuilderIdESX: "vmware",
vsphere.BuilderId: "vsphere",
"packer.post-processor.artifice": "artifice",
}
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Host string `mapstructure:"host"`
Insecure bool `mapstructure:"insecure"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
Datacenter string `mapstructure:"datacenter"`
Folder string `mapstructure:"folder"`
SnapshotEnable bool `mapstructure:"snapshot_enable"`
SnapshotName string `mapstructure:"snapshot_name"`
SnapshotDescription string `mapstructure:"snapshot_description"`
ReregisterVM config.Trilean `mapstructure:"reregister_vm"`
ctx interpolate.Context
}
type PostProcessor struct {
config Config
url *url.URL
}
func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
func (p *PostProcessor) Configure(raws ...interface{}) error {
err := config.Decode(&p.config, &config.DecodeOpts{
PluginType: vsphere.BuilderId,
Interpolate: true,
InterpolateContext: &p.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{},
},
}, raws...)
if err != nil {
return err
}
errs := new(packersdk.MultiError)
vc := map[string]*string{
"host": &p.config.Host,
"username": &p.config.Username,
"password": &p.config.Password,
}
for key, ptr := range vc {
if *ptr == "" {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("%s must be set", key))
}
}
if p.config.Folder != "" && !strings.HasPrefix(p.config.Folder, "/") {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Folder must be bound to the root"))
}
sdk, err := url.Parse(fmt.Sprintf("https://%v/sdk", p.config.Host))
if err != nil {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("Error invalid vSphere sdk endpoint: %s", err))
return errs
}
sdk.User = url.UserPassword(p.config.Username, p.config.Password)
p.url = sdk
if len(errs.Errors) > 0 {
return errs
}
return nil
}
func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifact packersdk.Artifact) (packersdk.Artifact, bool, bool, error) {
if _, ok := builtins[artifact.BuilderId()]; !ok {
return nil, false, false, fmt.Errorf("The Packer vSphere Template post-processor "+
"can only take an artifact from the VMware-iso builder, built on "+
"ESXi (i.e. remote) or an artifact from the vSphere post-processor. "+
"Artifact type %s does not fit this requirement", artifact.BuilderId())
}
f := artifact.State(ArtifactConfFormat)
k := artifact.State(ArtifactConfKeepRegistered)
s := artifact.State(ArtifactConfSkipExport)
if f != "" && k != "true" && s == "false" {
return nil, false, false, errors.New("To use this post-processor with exporting behavior you need set keep_registered as true")
}
// In some occasions the VM state is powered on and if we immediately try to mark as template
// (after the ESXi creates it) it will fail. If vSphere is given a few seconds this behavior doesn't reappear.
ui.Message("Waiting 10s for VMware vSphere to start")
time.Sleep(10 * time.Second)
c, err := govmomi.NewClient(context.Background(), p.url, p.config.Insecure)
if err != nil {
return nil, false, false, fmt.Errorf("Error connecting to vSphere: %s", err)
}
defer c.Logout(context.Background())
state := new(multistep.BasicStateBag)
state.Put("ui", ui)
state.Put("client", c)
steps := []multistep.Step{
&stepChooseDatacenter{
Datacenter: p.config.Datacenter,
},
&stepCreateFolder{
Folder: p.config.Folder,
},
NewStepCreateSnapshot(artifact, p),
NewStepMarkAsTemplate(artifact, p),
}
runner := commonsteps.NewRunnerWithPauseFn(steps, p.config.PackerConfig, ui, state)
runner.Run(ctx, state)
if rawErr, ok := state.GetOk("error"); ok {
return nil, false, false, rawErr.(error)
}
return artifact, true, true, nil
}

View File

@ -0,0 +1,65 @@
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
package vsphere_template
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
Host *string `mapstructure:"host" cty:"host" hcl:"host"`
Insecure *bool `mapstructure:"insecure" cty:"insecure" hcl:"insecure"`
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
Datacenter *string `mapstructure:"datacenter" cty:"datacenter" hcl:"datacenter"`
Folder *string `mapstructure:"folder" cty:"folder" hcl:"folder"`
SnapshotEnable *bool `mapstructure:"snapshot_enable" cty:"snapshot_enable" hcl:"snapshot_enable"`
SnapshotName *string `mapstructure:"snapshot_name" cty:"snapshot_name" hcl:"snapshot_name"`
SnapshotDescription *string `mapstructure:"snapshot_description" cty:"snapshot_description" hcl:"snapshot_description"`
ReregisterVM *bool `mapstructure:"reregister_vm" cty:"reregister_vm" hcl:"reregister_vm"`
}
// FlatMapstructure returns a new FlatConfig.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"host": &hcldec.AttrSpec{Name: "host", Type: cty.String, Required: false},
"insecure": &hcldec.AttrSpec{Name: "insecure", Type: cty.Bool, Required: false},
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
"datacenter": &hcldec.AttrSpec{Name: "datacenter", Type: cty.String, Required: false},
"folder": &hcldec.AttrSpec{Name: "folder", Type: cty.String, Required: false},
"snapshot_enable": &hcldec.AttrSpec{Name: "snapshot_enable", Type: cty.Bool, Required: false},
"snapshot_name": &hcldec.AttrSpec{Name: "snapshot_name", Type: cty.String, Required: false},
"snapshot_description": &hcldec.AttrSpec{Name: "snapshot_description", Type: cty.String, Required: false},
"reregister_vm": &hcldec.AttrSpec{Name: "reregister_vm", Type: cty.Bool, Required: false},
}
return s
}

View File

@ -0,0 +1,35 @@
package vsphere_template
import (
"context"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/find"
)
type stepChooseDatacenter struct {
Datacenter string
}
func (s *stepChooseDatacenter) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
cli := state.Get("client").(*govmomi.Client)
finder := find.NewFinder(cli.Client, false)
ui.Message("Choosing datacenter...")
dc, err := finder.DatacenterOrDefault(context.Background(), s.Datacenter)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state.Put("dcPath", dc.InventoryPath)
return multistep.ActionContinue
}
func (s *stepChooseDatacenter) Cleanup(multistep.StateBag) {}

View File

@ -0,0 +1,86 @@
package vsphere_template
import (
"context"
"fmt"
"path"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
)
type stepCreateFolder struct {
Folder string
}
func (s *stepCreateFolder) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
cli := state.Get("client").(*govmomi.Client)
dcPath := state.Get("dcPath").(string)
ui.Message("Creating or checking destination folders...")
base := path.Join(dcPath, "vm")
fullPath := path.Join(base, s.Folder)
si := object.NewSearchIndex(cli.Client)
var folders []string
var err error
var ref object.Reference
// We iterate over the path starting with full path
// If we don't find it, we save the folder name and continue with the previous path
// The iteration ends when we find an existing path otherwise it throws error
for {
ref, err = si.FindByInventoryPath(context.Background(), fullPath)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if ref == nil {
dir, folder := path.Split(fullPath)
fullPath = path.Clean(dir)
if fullPath == dcPath {
err = fmt.Errorf("vSphere base path %s not found", base)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
folders = append(folders, folder)
} else {
break
}
}
if root, ok := ref.(*object.Folder); ok {
for i := len(folders) - 1; i >= 0; i-- {
ui.Message(fmt.Sprintf("Creating folder: %v", folders[i]))
root, err = root.CreateFolder(context.Background(), folders[i])
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
fullPath = path.Join(fullPath, folders[i])
}
root.SetInventoryPath(fullPath)
state.Put("folder", root)
} else {
err = fmt.Errorf("folder not found: '%v'", ref)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepCreateFolder) Cleanup(multistep.StateBag) {}

View File

@ -0,0 +1,76 @@
package vsphere_template
import (
"context"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-vsphere/post-processor/vsphere"
"github.com/vmware/govmomi"
)
type stepCreateSnapshot struct {
VMName string
RemoteFolder string
SnapshotName string
SnapshotDescription string
SnapshotEnable bool
}
func NewStepCreateSnapshot(artifact packersdk.Artifact, p *PostProcessor) *stepCreateSnapshot {
remoteFolder := "Discovered virtual machine"
vmname := artifact.Id()
if artifact.BuilderId() == vsphere.BuilderId {
id := strings.Split(artifact.Id(), "::")
remoteFolder = id[1]
vmname = id[2]
}
return &stepCreateSnapshot{
VMName: vmname,
RemoteFolder: remoteFolder,
SnapshotEnable: p.config.SnapshotEnable,
SnapshotName: p.config.SnapshotName,
SnapshotDescription: p.config.SnapshotDescription,
}
}
func (s *stepCreateSnapshot) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
cli := state.Get("client").(*govmomi.Client)
dcPath := state.Get("dcPath").(string)
if !s.SnapshotEnable {
return multistep.ActionContinue
}
ui.Message("Creating a Snapshot...")
vm, err := findRuntimeVM(cli, dcPath, s.VMName, s.RemoteFolder)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
task, err := vm.CreateSnapshot(context.Background(), s.SnapshotName, s.SnapshotDescription, false, false)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err = task.Wait(context.Background()); err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepCreateSnapshot) Cleanup(multistep.StateBag) {}

View File

@ -0,0 +1,186 @@
package vsphere_template
import (
"context"
"fmt"
"path"
"regexp"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-vsphere/post-processor/vsphere"
"github.com/vmware/govmomi"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/vim25/types"
)
type stepMarkAsTemplate struct {
VMName string
RemoteFolder string
ReregisterVM config.Trilean
}
func NewStepMarkAsTemplate(artifact packersdk.Artifact, p *PostProcessor) *stepMarkAsTemplate {
remoteFolder := "Discovered virtual machine"
vmname := artifact.Id()
if artifact.BuilderId() == vsphere.BuilderId {
id := strings.Split(artifact.Id(), "::")
remoteFolder = id[1]
vmname = id[2]
}
return &stepMarkAsTemplate{
VMName: vmname,
RemoteFolder: remoteFolder,
ReregisterVM: p.config.ReregisterVM,
}
}
func (s *stepMarkAsTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui)
cli := state.Get("client").(*govmomi.Client)
folder := state.Get("folder").(*object.Folder)
dcPath := state.Get("dcPath").(string)
vm, err := findRuntimeVM(cli, dcPath, s.VMName, s.RemoteFolder)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Use a simple "MarkAsTemplate" method unless `reregister_vm` is true
if s.ReregisterVM.False() {
ui.Message("Marking as a template...")
if err := vm.MarkAsTemplate(context.Background()); err != nil {
state.Put("error", err)
ui.Error("vm.MarkAsTemplate:" + err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
ui.Message("Re-register VM as a template...")
dsPath, err := datastorePath(vm)
if err != nil {
state.Put("error", err)
ui.Error("datastorePath:" + err.Error())
return multistep.ActionHalt
}
host, err := vm.HostSystem(context.Background())
if err != nil {
state.Put("error", err)
ui.Error("vm.HostSystem:" + err.Error())
return multistep.ActionHalt
}
if err := vm.Unregister(context.Background()); err != nil {
state.Put("error", err)
ui.Error("vm.Unregister:" + err.Error())
return multistep.ActionHalt
}
if err := unregisterPreviousVM(cli, folder, s.VMName); err != nil {
state.Put("error", err)
ui.Error("unregisterPreviousVM:" + err.Error())
return multistep.ActionHalt
}
task, err := folder.RegisterVM(context.Background(), dsPath.String(), s.VMName, true, nil, host)
if err != nil {
state.Put("error", err)
ui.Error("RegisterVM:" + err.Error())
return multistep.ActionHalt
}
if err = task.Wait(context.Background()); err != nil {
state.Put("error", err)
ui.Error("task.Wait:" + err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func datastorePath(vm *object.VirtualMachine) (*object.DatastorePath, error) {
devices, err := vm.Device(context.Background())
if err != nil {
return nil, err
}
disk := ""
for _, device := range devices {
if d, ok := device.(*types.VirtualDisk); ok {
if b, ok := d.Backing.(types.BaseVirtualDeviceFileBackingInfo); ok {
disk = b.GetVirtualDeviceFileBackingInfo().FileName
}
break
}
}
if disk == "" {
return nil, fmt.Errorf("disk not found in '%v'", vm.Name())
}
re := regexp.MustCompile("\\[(.*?)\\]")
datastore := re.FindStringSubmatch(disk)[1]
vmxPath := path.Join("/", path.Dir(strings.Split(disk, " ")[1]), vm.Name()+".vmx")
return &object.DatastorePath{
Datastore: datastore,
Path: vmxPath,
}, nil
}
func findRuntimeVM(cli *govmomi.Client, dcPath, name, remoteFolder string) (*object.VirtualMachine, error) {
si := object.NewSearchIndex(cli.Client)
fullPath := path.Join(dcPath, "vm", remoteFolder, name)
ref, err := si.FindByInventoryPath(context.Background(), fullPath)
if err != nil {
return nil, err
}
if ref == nil {
return nil, fmt.Errorf("VM at path %s not found", fullPath)
}
vm := ref.(*object.VirtualMachine)
if vm.InventoryPath == "" {
vm.SetInventoryPath(fullPath)
}
return vm, nil
}
// If in the target folder a virtual machine or template already exists
// it will be removed to maintain consistency
func unregisterPreviousVM(cli *govmomi.Client, folder *object.Folder, name string) error {
si := object.NewSearchIndex(cli.Client)
fullPath := path.Join(folder.InventoryPath, name)
ref, err := si.FindByInventoryPath(context.Background(), fullPath)
if err != nil {
return err
}
if ref != nil {
if vm, ok := ref.(*object.VirtualMachine); ok {
return vm.Unregister(context.Background())
} else {
return fmt.Errorf("an object name '%v' already exists", name)
}
}
return nil
}
func (s *stepMarkAsTemplate) Cleanup(multistep.StateBag) {}

View File

@ -0,0 +1,47 @@
package vsphere
import (
"fmt"
)
const BuilderId = "packer.post-processor.vsphere"
type Artifact struct {
files []string
datastore string
vmfolder string
vmname string
}
func NewArtifact(datastore, vmfolder, vmname string, files []string) *Artifact {
return &Artifact{
files: files,
datastore: datastore,
vmfolder: vmfolder,
vmname: vmname,
}
}
func (*Artifact) BuilderId() string {
return BuilderId
}
func (a *Artifact) Files() []string {
return a.files
}
func (a *Artifact) Id() string {
return fmt.Sprintf("%s::%s::%s", a.datastore, a.vmfolder, a.vmname)
}
func (a *Artifact) String() string {
return fmt.Sprintf("VM: %s Folder: %s Datastore: %s", a.vmname, a.vmfolder, a.datastore)
}
func (*Artifact) State(name string) interface{} {
return nil
}
func (a *Artifact) Destroy() error {
return nil
}

View File

@ -0,0 +1,280 @@
//go:generate mapstructure-to-hcl2 -type Config
package vsphere
import (
"bytes"
"context"
"fmt"
"log"
"net/url"
"os/exec"
"regexp"
"runtime"
"strings"
"time"
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/hashicorp/packer-plugin-sdk/common"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
shelllocal "github.com/hashicorp/packer-plugin-sdk/shell-local"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
)
var ovftool string = "ovftool"
var (
// Regular expression to validate RFC1035 hostnames from full fqdn or simple hostname.
// For example "packer-esxi1". Requires proper DNS setup and/or correct DNS search domain setting.
hostnameRegex = regexp.MustCompile(`^[[:alnum:]][[:alnum:]\-]{0,61}[[:alnum:]]|[[:alpha:]]$`)
// Simple regular expression to validate IPv4 values.
// For example "192.168.1.1".
ipv4Regex = regexp.MustCompile(`^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$`)
)
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Cluster string `mapstructure:"cluster"`
Datacenter string `mapstructure:"datacenter"`
Datastore string `mapstructure:"datastore"`
DiskMode string `mapstructure:"disk_mode"`
Host string `mapstructure:"host"`
ESXiHost string `mapstructure:"esxi_host"`
Insecure bool `mapstructure:"insecure"`
Options []string `mapstructure:"options"`
Overwrite bool `mapstructure:"overwrite"`
Password string `mapstructure:"password"`
ResourcePool string `mapstructure:"resource_pool"`
Username string `mapstructure:"username"`
VMFolder string `mapstructure:"vm_folder"`
VMName string `mapstructure:"vm_name"`
VMNetwork string `mapstructure:"vm_network"`
ctx interpolate.Context
}
type PostProcessor struct {
config Config
}
func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
func (p *PostProcessor) Configure(raws ...interface{}) error {
err := config.Decode(&p.config, &config.DecodeOpts{
PluginType: BuilderId,
Interpolate: true,
InterpolateContext: &p.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{},
},
}, raws...)
if err != nil {
return err
}
// Defaults
if p.config.DiskMode == "" {
p.config.DiskMode = "thick"
}
// Accumulate any errors
errs := new(packersdk.MultiError)
if runtime.GOOS == "windows" {
ovftool = "ovftool.exe"
}
if _, err := exec.LookPath(ovftool); err != nil {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("ovftool not found: %s", err))
}
// First define all our templatable parameters that are _required_
templates := map[string]*string{
"cluster": &p.config.Cluster,
"datacenter": &p.config.Datacenter,
"diskmode": &p.config.DiskMode,
"host": &p.config.Host,
"password": &p.config.Password,
"username": &p.config.Username,
"vm_name": &p.config.VMName,
}
for key, ptr := range templates {
if *ptr == "" {
errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("%s must be set", key))
}
}
if len(errs.Errors) > 0 {
return errs
}
return nil
}
func (p *PostProcessor) generateURI() (*url.URL, error) {
// use net/url lib to encode and escape url elements
ovftool_uri := fmt.Sprintf("vi://%s/%s/host/%s",
p.config.Host,
p.config.Datacenter,
p.config.Cluster)
if p.config.ResourcePool != "" {
ovftool_uri += "/Resources/" + p.config.ResourcePool
}
u, err := url.Parse(ovftool_uri)
if err != nil {
return nil, fmt.Errorf("Couldn't generate uri for ovftool: %s", err)
}
u.User = url.UserPassword(p.config.Username, p.config.Password)
if p.config.ESXiHost != "" {
q := u.Query()
if ipv4Regex.MatchString(p.config.ESXiHost) {
q.Add("ip", p.config.ESXiHost)
} else if hostnameRegex.MatchString(p.config.ESXiHost) {
q.Add("dns", p.config.ESXiHost)
}
u.RawQuery = q.Encode()
}
return u, nil
}
func getEncodedPassword(u *url.URL) (string, bool) {
// filter password from all logging
password, passwordSet := u.User.Password()
if passwordSet && password != "" {
encodedPassword := strings.Split(u.User.String(), ":")[1]
return encodedPassword, true
}
return password, false
}
func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifact packersdk.Artifact) (packersdk.Artifact, bool, bool, error) {
source := ""
for _, path := range artifact.Files() {
if strings.HasSuffix(path, ".vmx") || strings.HasSuffix(path, ".ovf") || strings.HasSuffix(path, ".ova") {
source = path
break
}
}
if source == "" {
return nil, false, false, fmt.Errorf("VMX, OVF or OVA file not found")
}
ovftool_uri, err := p.generateURI()
if err != nil {
return nil, false, false, err
}
encodedPassword, isSet := getEncodedPassword(ovftool_uri)
if isSet {
packersdk.LogSecretFilter.Set(encodedPassword)
}
args, err := p.BuildArgs(source, ovftool_uri.String())
if err != nil {
ui.Message(fmt.Sprintf("Failed: %s\n", err))
}
ui.Message(fmt.Sprintf("Uploading %s to vSphere", source))
log.Printf("Starting ovftool with parameters: %s", strings.Join(args, " "))
ui.Message("Validating Username and Password with dry-run")
err = p.ValidateOvfTool(args, ovftool)
if err != nil {
return nil, false, false, err
}
// Validation has passed, so run for real.
ui.Message("Calling OVFtool to upload vm")
commandAndArgs := []string{ovftool}
commandAndArgs = append(commandAndArgs, args...)
comm := &shelllocal.Communicator{
ExecuteCommand: commandAndArgs,
}
flattenedCmd := strings.Join(commandAndArgs, " ")
cmd := &packersdk.RemoteCmd{Command: flattenedCmd}
log.Printf("[INFO] (vsphere): starting ovftool command: %s", flattenedCmd)
err = cmd.RunWithUi(ctx, comm, ui)
if err != nil || cmd.ExitStatus() != 0 {
return nil, false, false, fmt.Errorf(
"Error uploading virtual machine: Please see output above for more information.")
}
artifact = NewArtifact(p.config.Datastore, p.config.VMFolder, p.config.VMName, artifact.Files())
return artifact, false, false, nil
}
func (p *PostProcessor) ValidateOvfTool(args []string, ofvtool string) error {
args = append([]string{"--verifyOnly"}, args...)
var out bytes.Buffer
cmdCtx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
cmd := exec.CommandContext(cmdCtx, ovftool, args...)
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
stdin, err := cmd.StdinPipe()
if err != nil {
return err
}
defer stdin.Close()
if err := cmd.Run(); err != nil {
outString := out.String()
if strings.Contains(outString, "Enter login information for") {
err = fmt.Errorf("Error performing OVFtool dry run; the username " +
"or password you provided to ovftool is likely invalid.")
return err
}
return nil
}
return nil
}
func (p *PostProcessor) BuildArgs(source, ovftool_uri string) ([]string, error) {
args := []string{
"--acceptAllEulas",
fmt.Sprintf(`--name=%s`, p.config.VMName),
fmt.Sprintf(`--datastore=%s`, p.config.Datastore),
}
if p.config.Insecure {
args = append(args, fmt.Sprintf(`--noSSLVerify=%t`, p.config.Insecure))
}
if p.config.DiskMode != "" {
args = append(args, fmt.Sprintf(`--diskMode=%s`, p.config.DiskMode))
}
if p.config.VMFolder != "" {
args = append(args, fmt.Sprintf(`--vmFolder=%s`, p.config.VMFolder))
}
if p.config.VMNetwork != "" {
args = append(args, fmt.Sprintf(`--network=%s`, p.config.VMNetwork))
}
if p.config.Overwrite == true {
args = append(args, "--overwrite")
}
if len(p.config.Options) > 0 {
args = append(args, p.config.Options...)
}
args = append(args, source)
args = append(args, ovftool_uri)
return args, nil
}

View File

@ -0,0 +1,75 @@
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
package vsphere
import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/zclconf/go-cty/cty"
)
// FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
Cluster *string `mapstructure:"cluster" cty:"cluster" hcl:"cluster"`
Datacenter *string `mapstructure:"datacenter" cty:"datacenter" hcl:"datacenter"`
Datastore *string `mapstructure:"datastore" cty:"datastore" hcl:"datastore"`
DiskMode *string `mapstructure:"disk_mode" cty:"disk_mode" hcl:"disk_mode"`
Host *string `mapstructure:"host" cty:"host" hcl:"host"`
ESXiHost *string `mapstructure:"esxi_host" cty:"esxi_host" hcl:"esxi_host"`
Insecure *bool `mapstructure:"insecure" cty:"insecure" hcl:"insecure"`
Options []string `mapstructure:"options" cty:"options" hcl:"options"`
Overwrite *bool `mapstructure:"overwrite" cty:"overwrite" hcl:"overwrite"`
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool" hcl:"resource_pool"`
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
VMFolder *string `mapstructure:"vm_folder" cty:"vm_folder" hcl:"vm_folder"`
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
VMNetwork *string `mapstructure:"vm_network" cty:"vm_network" hcl:"vm_network"`
}
// FlatMapstructure returns a new FlatConfig.
// FlatConfig is an auto-generated flat version of Config.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatConfig)
}
// HCL2Spec returns the hcl spec of a Config.
// This spec is used by HCL to read the fields of Config.
// The decoded values from this spec will then be applied to a FlatConfig.
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"cluster": &hcldec.AttrSpec{Name: "cluster", Type: cty.String, Required: false},
"datacenter": &hcldec.AttrSpec{Name: "datacenter", Type: cty.String, Required: false},
"datastore": &hcldec.AttrSpec{Name: "datastore", Type: cty.String, Required: false},
"disk_mode": &hcldec.AttrSpec{Name: "disk_mode", Type: cty.String, Required: false},
"host": &hcldec.AttrSpec{Name: "host", Type: cty.String, Required: false},
"esxi_host": &hcldec.AttrSpec{Name: "esxi_host", Type: cty.String, Required: false},
"insecure": &hcldec.AttrSpec{Name: "insecure", Type: cty.Bool, Required: false},
"options": &hcldec.AttrSpec{Name: "options", Type: cty.List(cty.String), Required: false},
"overwrite": &hcldec.AttrSpec{Name: "overwrite", Type: cty.Bool, Required: false},
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
"resource_pool": &hcldec.AttrSpec{Name: "resource_pool", Type: cty.String, Required: false},
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
"vm_folder": &hcldec.AttrSpec{Name: "vm_folder", Type: cty.String, Required: false},
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
"vm_network": &hcldec.AttrSpec{Name: "vm_network", Type: cty.String, Required: false},
}
return s
}

View File

@ -11,6 +11,7 @@ builds:
- amd64
- 386
- arm64
- mips64le
env:
- CGO_ENABLED=0
main: ./govc/main.go
@ -27,6 +28,7 @@ builds:
- amd64
- 386
- arm64
- mips64le
env:
- CGO_ENABLED=0
main: ./vcsim/main.go

View File

@ -33,3 +33,6 @@ Uwe Bessle <Uwe.Bessle@iteratec.de> Uwe Bessle <uwe.bessle@web.de>
Vadim Egorov <vegorov@vmware.com> <egorovv@gmail.com>
Zach Tucker <ztucker@vmware.com> <jzt@users.noreply.github.com>
Zee Yang <zeey@vmware.com> <zee.yang@gmail.com>
Ian Eyberg <ian@deferpanic.com> <ian@opuler.com>
Parveen Chahal <parkuma@microsoft.com> <mail.chahal@gmail.com>
Yun Zhou <yunz@vmware.com> <41678287+gh05tn0va@users.noreply.github.com>

View File

@ -12,7 +12,7 @@ services: false
# Set the version of Go.
language: go
go: 1.14
go: 1.15
# Always set the project's Go import path to ensure that forked
# builds get cloned to the correct location.
@ -44,23 +44,14 @@ jobs:
install: true
script: make install
- <<: *build-stage
env: GOOS=linux GOARCH=386
- <<: *build-stage
env: GOOS=darwin GOARCH=amd64
- <<: *build-stage
env: GOOS=darwin GOARCH=386
- <<: *build-stage
env: GOOS=freebsd GOARCH=amd64
- <<: *build-stage
env: GOOS=freebsd GOARCH=386
- <<: *build-stage
env: GOOS=windows GOARCH=amd64
- <<: *build-stage
env: GOOS=windows GOARCH=386
# The test stage executes the test target.
- stage: test

View File

@ -1,5 +1,19 @@
# changelog
### 0.24.0 (2020-12-20)
* Update generated API bindings to vSphere 7.0U1 release
* Add vSAN 7.0 API bindings
* Add AuthorizationManager.HasUserPrivilegeOnEntities wrapper
* Add sms generated types and methods
* Add ExtendDisk and InflateDisk wrappers to vlsm.ObjectManager
* Add vSAN Performance Data Collection API
### 0.23.0 (2020-06-11)
* Finder: support DistributedVirtualSwitch traversal

View File

@ -8,9 +8,9 @@ abrarshivani <abrarshivani@users.noreply.github.com>
Adam Shannon <adamkshannon@gmail.com>
Al Biheiri <abiheiri@apple.com>
Alessandro Cortiana <alessandro.cortiana@gmail.com>
Alex <puzo2002@gmail.com>
Alex Bozhenko <alexbozhenko@fb.com>
Alex Ellis (VMware) <alexellis2@gmail.com>
Alex <puzo2002@gmail.com>
Alvaro Miranda <kikitux@gmail.com>
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br>
Amit Bathla <abathla@.vmware.com>
@ -31,27 +31,30 @@ Austin Parker <aparker@apprenda.com>
Balu Dontu <bdontu@vmware.com>
bastienbc <bastien.barbe.creuly@gmail.com>
Ben Corrie <bcorrie@vmware.com>
Ben Vickers <bvickers@pivotal.io>
Benjamin Davini <davinib@vmware.com>
Benjamin Peterson <benjamin@python.org>
Bhavya Choudhary <bhavyac@vmware.com>
Bob Killen <killen.bob@gmail.com>
Brad Fitzpatrick <bradfitz@golang.org>
brian57860 <brian57860@users.noreply.github.com>
Bruce Downs <bruceadowns@gmail.com>
Cédric Blomart <cblomart@gmail.com>
Cheng Cheng <chengch@vmware.com>
Chethan Venkatesh <chethanv@vmware.com>
Chris Marchesi <chrism@vancluevertech.com>
Christian Höltje <docwhat@gerf.org>
Clint Greenwood <cgreenwood@vmware.com>
CuiHaozhi <cuihaozhi@chinacloud.com.cn>
Daniel Mueller <deso@posteo.net>
Cédric Blomart <cblomart@gmail.com>
Dan Ilan <danilan@google.com>
Daniel Mueller <deso@posteo.net>
Danny Lockard <danny.lockard@banno.com>
Dave Gress <gressd@vmware.com>
Dave Smith-Uchida <dsmithuchida@vmware.com>
Dave Tucker <dave@dtucker.co.uk>
Davide Agnello <dagnello@hp.com>
David Gress <gressd@vmware.com>
David Stark <dave@davidstark.name>
Davide Agnello <dagnello@hp.com>
Davinder Kumar <davinderk@vmware.com>
demarey <christophe.demarey@inria.fr>
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@ -82,6 +85,7 @@ Hasan Mahmood <mahmoodh@vmware.com>
Henrik Hodne <henrik@travis-ci.com>
hkumar <hkumar@vmware.com>
hui luo <luoh@vmware.com>
Ian Eyberg <ian@deferpanic.com>
Isaac Rodman <isaac@eyz.us>
Ivan Mikushin <imikushin@vmware.com>
Ivan Porto Carrero <icarrero@vmware.com>
@ -91,16 +95,18 @@ Jeremy Canady <jcanady@jackhenry.com>
jeremy-clerc <jeremy@clerc.io>
Jiatong Wang <wjiatong@vmware.com>
jingyizPensando <jingyiz@pensando.io>
João Pereira <joaodrp@gmail.com>
Jonas Ausevicius <jonas.ausevicius@virtustream.com>
Jorge Sevilla <jorge.sevilla@rstor.io>
João Pereira <joaodrp@gmail.com>
kayrus <kay.diam@gmail.com>
Kevin George <georgek@vmware.com>
Leslie Wang <qiwa@pensando.io>
leslie-qiwa <leslie.qiwa@gmail.com>
Lintong Jiang <lintongj@vmware.com>
Liping Xue <lipingx@vmware.com>
Louie Jiang <jiangl@vmware.com>
Luther Monson <luther.monson@gmail.com>
Madanagopal Arunachalam <marunachalam@vmware.com>
maplain <fangyuanl@vmware.com>
Marc Carmier <mcarmier@gmail.com>
Marcus Tan <marcus.tan@rubrik.com>
@ -109,15 +115,17 @@ Marin Atanasov Nikolov <mnikolov@vmware.com>
Mario Trangoni <mjtrangoni@gmail.com>
Mark Peek <markpeek@vmware.com>
Matt Clay <matt@mystile.com>
Matthew Cosgrove <matthew.cosgrove@dell.com>
Matt Moore <mattmoor@vmware.com>
Matt Moriarity <matt@mattmoriarity.com>
Matthew Cosgrove <matthew.cosgrove@dell.com>
Mevan Samaratunga <mevansam@gmail.com>
Michael Gasch <mgasch@vmware.com>
Michal Jankowski <mjankowski@vmware.com>
mingwei <mingwei@smartx.com>
Nicolas Lamirault <nicolas.lamirault@gmail.com>
Omar Kohl <omarkohl@gmail.com>
Parham Alvani <parham.alvani@gmail.com>
Parveen Chahal <parkuma@microsoft.com>
Pierre Gronlier <pierre.gronlier@corp.ovh.com>
Pieter Noordhuis <pnoordhuis@vmware.com>
prydin <prydin@vmware.com>
@ -125,11 +133,12 @@ rHermes <teodor_spaeren@riseup.net>
Rowan Jacobs <rojacobs@pivotal.io>
rsikdar <rsikdar@berkeley.edu>
runner.mei <runner.mei@gmail.com>
Sandeep Pissay Srinivasa Rao <ssrinivas@vmware.com>
S.Çağlar Onur <conur@vmware.com>
Sandeep Pissay Srinivasa Rao <ssrinivas@vmware.com>
Sergey Ignatov <sergey.ignatov@jetbrains.com>
serokles <timbo.alexander@gmail.com>
Shalini Bhaskara <sbhaskara@vmware.com>
Shaozhen Ding <dsz0111@gmail.com>
Shawn Neal <sneal@sneal.net>
shylasrinivas <sshyla@vmware.com>
sky-joker <sky.jokerxx@gmail.com>
@ -157,12 +166,17 @@ Waldek Maleska <w.maleska@gmail.com>
William Lam <info.virtuallyghetto@gmail.com>
Witold Krecicki <wpk@culm.net>
xing-yang <xingyang105@gmail.com>
yangxi <yangxi@vmware.com>
Yang Yang <yangy@vmware.com>
yangxi <yangxi@vmware.com>
Yann Hodique <yhodique@google.com>
Yash Nitin Desai <desaiy@vmware.com>
Yassine TIJANI <ytijani@vmware.com>
yiyingy <yiyingy@vmware.com>
ykakarap <yuva2811@gmail.com>
Yogesh Sobale <6104071+ysobale@users.noreply.github.com>
Yun Zhou <yunz@vmware.com>
Yuya Kusakabe <yuya.kusakabe@gmail.com>
Zacharias Taubert <zacharias.taubert@gmail.com>
Zach Tucker <ztucker@vmware.com>
Zacharias Taubert <zacharias.taubert@gmail.com>
Zee Yang <zeey@vmware.com>
zyuxin <zyuxin@vmware.com>

View File

@ -89,6 +89,8 @@ Refer to the [CHANGELOG](CHANGELOG.md) for version to version changes.
* [OPS](https://github.com/nanovms/ops)
* [VMware Event Broker Appliance](https://github.com/vmware-samples/vcenter-event-broker-appliance/tree/development/vmware-event-router)
## Related projects
* [rbvmomi](https://github.com/vmware/rbvmomi)

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2014-2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2014-2020 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -22,6 +22,7 @@ import (
"path"
"strings"
"github.com/vmware/govmomi/internal"
"github.com/vmware/govmomi/list"
"github.com/vmware/govmomi/object"
"github.com/vmware/govmomi/property"
@ -67,6 +68,18 @@ func (f *Finder) SetDatacenter(dc *object.Datacenter) *Finder {
return f
}
// InventoryPath composes the given object's inventory path.
// There is no vSphere property or method that provides an inventory path directly.
// This method uses the ManagedEntity.Parent field to determine the ancestry tree of the object and
// the ManagedEntity.Name field for each ancestor to compose the path.
func InventoryPath(ctx context.Context, client *vim25.Client, obj types.ManagedObjectReference) (string, error) {
entities, err := mo.Ancestors(ctx, client, client.ServiceContent.PropertyCollector, obj)
if err != nil {
return "", err
}
return internal.InventoryPath(entities), nil
}
// findRoot makes it possible to use "find" mode with a different root path.
// Example: ResourcePoolList("/dc1/host/cluster1/...")
func (f *Finder) findRoot(ctx context.Context, root *list.Element, parts []string) bool {
@ -103,8 +116,8 @@ func (f *Finder) find(ctx context.Context, arg string, s *spec) ([]list.Element,
isPath := strings.Contains(arg, "/")
root := list.Element{
Path: "/",
Object: object.NewRootFolder(f.client),
Path: "/",
}
parts := list.ToParts(arg)
@ -119,19 +132,10 @@ func (f *Finder) find(ctx context.Context, arg string, s *spec) ([]list.Element,
return nil, err
}
mes, err := mo.Ancestors(ctx, f.client, f.client.ServiceContent.PropertyCollector, pivot.Reference())
root.Path, err = InventoryPath(ctx, f.client, pivot.Reference())
if err != nil {
return nil, err
}
for _, me := range mes {
// Skip root entity in building inventory path.
if me.Parent == nil {
continue
}
root.Path = path.Join(root.Path, me.Name)
}
root.Object = pivot
parts = parts[1:]
}
@ -281,8 +285,7 @@ func (f *Finder) managedObjectList(ctx context.Context, path string, tl bool, in
return f.find(ctx, path, s)
}
// Element returns an Element for the given ManagedObjectReference
// This method is only useful for looking up the InventoryPath of a ManagedObjectReference.
// Element is deprecated, use InventoryPath() instead.
func (f *Finder) Element(ctx context.Context, ref types.ManagedObjectReference) (*list.Element, error) {
rl := func(_ context.Context) (object.Reference, error) {
return ref, nil
@ -311,7 +314,7 @@ func (f *Finder) Element(ctx context.Context, ref types.ManagedObjectReference)
// ObjectReference converts the given ManagedObjectReference to a type from the object package via object.NewReference
// with the object.Common.InventoryPath field set.
func (f *Finder) ObjectReference(ctx context.Context, ref types.ManagedObjectReference) (object.Reference, error) {
e, err := f.Element(ctx, ref)
path, err := InventoryPath(ctx, f.client, ref)
if err != nil {
return nil, err
}
@ -322,7 +325,7 @@ func (f *Finder) ObjectReference(ctx context.Context, ref types.ManagedObjectRef
SetInventoryPath(string)
}
r.(common).SetInventoryPath(e.Path)
r.(common).SetInventoryPath(path)
if f.dc != nil {
if ds, ok := r.(*object.Datastore); ok {

38
vendor/github.com/vmware/govmomi/internal/helpers.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
/*
Copyright (c) 2020 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package internal
import (
"path"
"github.com/vmware/govmomi/vim25/mo"
)
// InventoryPath composed of entities by Name
func InventoryPath(entities []mo.ManagedEntity) string {
val := "/"
for _, entity := range entities {
// Skip root folder in building inventory path.
if entity.Parent == nil {
continue
}
val = path.Join(val, entity.Name)
}
return val
}

123
vendor/github.com/vmware/govmomi/internal/methods.go generated vendored Normal file
View File

@ -0,0 +1,123 @@
/*
Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package internal
import (
"context"
"github.com/vmware/govmomi/vim25/soap"
)
type RetrieveDynamicTypeManagerBody struct {
Req *RetrieveDynamicTypeManagerRequest `xml:"urn:vim25 RetrieveDynamicTypeManager"`
Res *RetrieveDynamicTypeManagerResponse `xml:"urn:vim25 RetrieveDynamicTypeManagerResponse"`
Fault_ *soap.Fault
}
func (b *RetrieveDynamicTypeManagerBody) Fault() *soap.Fault { return b.Fault_ }
func RetrieveDynamicTypeManager(ctx context.Context, r soap.RoundTripper, req *RetrieveDynamicTypeManagerRequest) (*RetrieveDynamicTypeManagerResponse, error) {
var reqBody, resBody RetrieveDynamicTypeManagerBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type RetrieveManagedMethodExecuterBody struct {
Req *RetrieveManagedMethodExecuterRequest `xml:"urn:vim25 RetrieveManagedMethodExecuter"`
Res *RetrieveManagedMethodExecuterResponse `xml:"urn:vim25 RetrieveManagedMethodExecuterResponse"`
Fault_ *soap.Fault
}
func (b *RetrieveManagedMethodExecuterBody) Fault() *soap.Fault { return b.Fault_ }
func RetrieveManagedMethodExecuter(ctx context.Context, r soap.RoundTripper, req *RetrieveManagedMethodExecuterRequest) (*RetrieveManagedMethodExecuterResponse, error) {
var reqBody, resBody RetrieveManagedMethodExecuterBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type DynamicTypeMgrQueryMoInstancesBody struct {
Req *DynamicTypeMgrQueryMoInstancesRequest `xml:"urn:vim25 DynamicTypeMgrQueryMoInstances"`
Res *DynamicTypeMgrQueryMoInstancesResponse `xml:"urn:vim25 DynamicTypeMgrQueryMoInstancesResponse"`
Fault_ *soap.Fault
}
func (b *DynamicTypeMgrQueryMoInstancesBody) Fault() *soap.Fault { return b.Fault_ }
func DynamicTypeMgrQueryMoInstances(ctx context.Context, r soap.RoundTripper, req *DynamicTypeMgrQueryMoInstancesRequest) (*DynamicTypeMgrQueryMoInstancesResponse, error) {
var reqBody, resBody DynamicTypeMgrQueryMoInstancesBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type DynamicTypeMgrQueryTypeInfoBody struct {
Req *DynamicTypeMgrQueryTypeInfoRequest `xml:"urn:vim25 DynamicTypeMgrQueryTypeInfo"`
Res *DynamicTypeMgrQueryTypeInfoResponse `xml:"urn:vim25 DynamicTypeMgrQueryTypeInfoResponse"`
Fault_ *soap.Fault
}
func (b *DynamicTypeMgrQueryTypeInfoBody) Fault() *soap.Fault { return b.Fault_ }
func DynamicTypeMgrQueryTypeInfo(ctx context.Context, r soap.RoundTripper, req *DynamicTypeMgrQueryTypeInfoRequest) (*DynamicTypeMgrQueryTypeInfoResponse, error) {
var reqBody, resBody DynamicTypeMgrQueryTypeInfoBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}
type ExecuteSoapBody struct {
Req *ExecuteSoapRequest `xml:"urn:vim25 ExecuteSoap"`
Res *ExecuteSoapResponse `xml:"urn:vim25 ExecuteSoapResponse"`
Fault_ *soap.Fault
}
func (b *ExecuteSoapBody) Fault() *soap.Fault { return b.Fault_ }
func ExecuteSoap(ctx context.Context, r soap.RoundTripper, req *ExecuteSoapRequest) (*ExecuteSoapResponse, error) {
var reqBody, resBody ExecuteSoapBody
reqBody.Req = req
if err := r.RoundTrip(ctx, &reqBody, &resBody); err != nil {
return nil, err
}
return resBody.Res, nil
}

270
vendor/github.com/vmware/govmomi/internal/types.go generated vendored Normal file
View File

@ -0,0 +1,270 @@
/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package internal
import (
"reflect"
"github.com/vmware/govmomi/vim25/types"
)
type DynamicTypeMgrQueryMoInstancesRequest struct {
This types.ManagedObjectReference `xml:"_this"`
FilterSpec BaseDynamicTypeMgrFilterSpec `xml:"filterSpec,omitempty,typeattr"`
}
type DynamicTypeMgrQueryMoInstancesResponse struct {
Returnval []DynamicTypeMgrMoInstance `xml:"urn:vim25 returnval"`
}
type DynamicTypeEnumTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
WsdlName string `xml:"wsdlName"`
Version string `xml:"version"`
Value []string `xml:"value,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
func init() {
types.Add("DynamicTypeEnumTypeInfo", reflect.TypeOf((*DynamicTypeEnumTypeInfo)(nil)).Elem())
}
type DynamicTypeMgrAllTypeInfoRequest struct {
types.DynamicData
ManagedTypeInfo []DynamicTypeMgrManagedTypeInfo `xml:"managedTypeInfo,omitempty"`
EnumTypeInfo []DynamicTypeEnumTypeInfo `xml:"enumTypeInfo,omitempty"`
DataTypeInfo []DynamicTypeMgrDataTypeInfo `xml:"dataTypeInfo,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrAllTypeInfo", reflect.TypeOf((*DynamicTypeMgrAllTypeInfoRequest)(nil)).Elem())
}
type DynamicTypeMgrAnnotation struct {
types.DynamicData
Name string `xml:"name"`
Parameter []string `xml:"parameter,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrAnnotation", reflect.TypeOf((*DynamicTypeMgrAnnotation)(nil)).Elem())
}
type DynamicTypeMgrDataTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
WsdlName string `xml:"wsdlName"`
Version string `xml:"version"`
Base []string `xml:"base,omitempty"`
Property []DynamicTypeMgrPropertyTypeInfo `xml:"property,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrDataTypeInfo", reflect.TypeOf((*DynamicTypeMgrDataTypeInfo)(nil)).Elem())
}
func (b *DynamicTypeMgrFilterSpec) GetDynamicTypeMgrFilterSpec() *DynamicTypeMgrFilterSpec { return b }
type BaseDynamicTypeMgrFilterSpec interface {
GetDynamicTypeMgrFilterSpec() *DynamicTypeMgrFilterSpec
}
type DynamicTypeMgrFilterSpec struct {
types.DynamicData
}
func init() {
types.Add("DynamicTypeMgrFilterSpec", reflect.TypeOf((*DynamicTypeMgrFilterSpec)(nil)).Elem())
}
type DynamicTypeMgrManagedTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
WsdlName string `xml:"wsdlName"`
Version string `xml:"version"`
Base []string `xml:"base,omitempty"`
Property []DynamicTypeMgrPropertyTypeInfo `xml:"property,omitempty"`
Method []DynamicTypeMgrMethodTypeInfo `xml:"method,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrManagedTypeInfo", reflect.TypeOf((*DynamicTypeMgrManagedTypeInfo)(nil)).Elem())
}
type DynamicTypeMgrMethodTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
WsdlName string `xml:"wsdlName"`
Version string `xml:"version"`
ParamTypeInfo []DynamicTypeMgrParamTypeInfo `xml:"paramTypeInfo,omitempty"`
ReturnTypeInfo *DynamicTypeMgrParamTypeInfo `xml:"returnTypeInfo,omitempty"`
Fault []string `xml:"fault,omitempty"`
PrivId string `xml:"privId,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrMethodTypeInfo", reflect.TypeOf((*DynamicTypeMgrMethodTypeInfo)(nil)).Elem())
}
type DynamicTypeMgrMoFilterSpec struct {
DynamicTypeMgrFilterSpec
Id string `xml:"id,omitempty"`
TypeSubstr string `xml:"typeSubstr,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrMoFilterSpec", reflect.TypeOf((*DynamicTypeMgrMoFilterSpec)(nil)).Elem())
}
type DynamicTypeMgrMoInstance struct {
types.DynamicData
Id string `xml:"id"`
MoType string `xml:"moType"`
}
func init() {
types.Add("DynamicTypeMgrMoInstance", reflect.TypeOf((*DynamicTypeMgrMoInstance)(nil)).Elem())
}
type DynamicTypeMgrParamTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
Version string `xml:"version"`
Type string `xml:"type"`
PrivId string `xml:"privId,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrParamTypeInfo", reflect.TypeOf((*DynamicTypeMgrParamTypeInfo)(nil)).Elem())
}
type DynamicTypeMgrPropertyTypeInfo struct {
types.DynamicData
Name string `xml:"name"`
Version string `xml:"version"`
Type string `xml:"type"`
PrivId string `xml:"privId,omitempty"`
MsgIdFormat string `xml:"msgIdFormat,omitempty"`
Annotation []DynamicTypeMgrAnnotation `xml:"annotation,omitempty"`
}
type DynamicTypeMgrQueryTypeInfoRequest struct {
This types.ManagedObjectReference `xml:"_this"`
FilterSpec BaseDynamicTypeMgrFilterSpec `xml:"filterSpec,omitempty,typeattr"`
}
type DynamicTypeMgrQueryTypeInfoResponse struct {
Returnval DynamicTypeMgrAllTypeInfoRequest `xml:"urn:vim25 returnval"`
}
func init() {
types.Add("DynamicTypeMgrPropertyTypeInfo", reflect.TypeOf((*DynamicTypeMgrPropertyTypeInfo)(nil)).Elem())
}
type DynamicTypeMgrTypeFilterSpec struct {
DynamicTypeMgrFilterSpec
TypeSubstr string `xml:"typeSubstr,omitempty"`
}
func init() {
types.Add("DynamicTypeMgrTypeFilterSpec", reflect.TypeOf((*DynamicTypeMgrTypeFilterSpec)(nil)).Elem())
}
type ReflectManagedMethodExecuterSoapArgument struct {
types.DynamicData
Name string `xml:"name"`
Val string `xml:"val"`
}
func init() {
types.Add("ReflectManagedMethodExecuterSoapArgument", reflect.TypeOf((*ReflectManagedMethodExecuterSoapArgument)(nil)).Elem())
}
type ReflectManagedMethodExecuterSoapFault struct {
types.DynamicData
FaultMsg string `xml:"faultMsg"`
FaultDetail string `xml:"faultDetail,omitempty"`
}
func init() {
types.Add("ReflectManagedMethodExecuterSoapFault", reflect.TypeOf((*ReflectManagedMethodExecuterSoapFault)(nil)).Elem())
}
type ReflectManagedMethodExecuterSoapResult struct {
types.DynamicData
Response string `xml:"response,omitempty"`
Fault *ReflectManagedMethodExecuterSoapFault `xml:"fault,omitempty"`
}
type RetrieveDynamicTypeManagerRequest struct {
This types.ManagedObjectReference `xml:"_this"`
}
type RetrieveDynamicTypeManagerResponse struct {
Returnval *InternalDynamicTypeManager `xml:"urn:vim25 returnval"`
}
type RetrieveManagedMethodExecuterRequest struct {
This types.ManagedObjectReference `xml:"_this"`
}
func init() {
types.Add("RetrieveManagedMethodExecuter", reflect.TypeOf((*RetrieveManagedMethodExecuterRequest)(nil)).Elem())
}
type RetrieveManagedMethodExecuterResponse struct {
Returnval *ReflectManagedMethodExecuter `xml:"urn:vim25 returnval"`
}
type InternalDynamicTypeManager struct {
types.ManagedObjectReference
}
type ReflectManagedMethodExecuter struct {
types.ManagedObjectReference
}
type ExecuteSoapRequest struct {
This types.ManagedObjectReference `xml:"_this"`
Moid string `xml:"moid"`
Version string `xml:"version"`
Method string `xml:"method"`
Argument []ReflectManagedMethodExecuterSoapArgument `xml:"argument,omitempty"`
}
type ExecuteSoapResponse struct {
Returnval *ReflectManagedMethodExecuterSoapResult `xml:"urn:vim25 returnval"`
}

View File

@ -172,3 +172,50 @@ func (m AuthorizationManager) UpdateRole(ctx context.Context, id int32, name str
_, err := methods.UpdateAuthorizationRole(ctx, m.Client(), &req)
return err
}
func (m AuthorizationManager) HasUserPrivilegeOnEntities(ctx context.Context, entities []types.ManagedObjectReference, userName string, privID []string) ([]types.EntityPrivilege, error) {
req := types.HasUserPrivilegeOnEntities{
This: m.Reference(),
Entities: entities,
UserName: userName,
PrivId: privID,
}
res, err := methods.HasUserPrivilegeOnEntities(ctx, m.Client(), &req)
if err != nil {
return nil, err
}
return res.Returnval, nil
}
func (m AuthorizationManager) HasPrivilegeOnEntity(ctx context.Context, entity types.ManagedObjectReference, sessionID string, privID []string) ([]bool, error) {
req := types.HasPrivilegeOnEntity{
This: m.Reference(),
Entity: entity,
SessionId: sessionID,
PrivId: privID,
}
res, err := methods.HasPrivilegeOnEntity(ctx, m.Client(), &req)
if err != nil {
return nil, err
}
return res.Returnval, nil
}
func (m AuthorizationManager) FetchUserPrivilegeOnEntities(ctx context.Context, entities []types.ManagedObjectReference, userName string) ([]types.UserPrivilegeResult, error) {
req := types.FetchUserPrivilegeOnEntities{
This: m.Reference(),
Entities: entities,
UserName: userName,
}
res, err := methods.FetchUserPrivilegeOnEntities(ctx, m.Client(), &req)
if err != nil {
return nil, err
}
return res.Returnval, nil
}

View File

@ -103,3 +103,19 @@ func (p VirtualApp) Suspend(ctx context.Context) (*Task, error) {
return NewTask(p.c, res.Returnval), nil
}
func (p VirtualApp) Clone(ctx context.Context, name string, target types.ManagedObjectReference, spec types.VAppCloneSpec) (*Task, error) {
req := types.CloneVApp_Task{
This: p.Reference(),
Name: name,
Target: target,
Spec: spec,
}
res, err := methods.CloneVApp_Task(ctx, p.c, &req)
if err != nil {
return nil, err
}
return NewTask(p.c, res.Returnval), nil
}

View File

@ -19,6 +19,7 @@ package object
import (
"errors"
"fmt"
"math/rand"
"path/filepath"
"reflect"
"regexp"
@ -67,7 +68,7 @@ func EthernetCardTypes() VirtualDeviceList {
&types.VirtualSriovEthernetCard{},
}).Select(func(device types.BaseVirtualDevice) bool {
c := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
c.GetVirtualDevice().Key = -1
c.GetVirtualDevice().Key = int32(rand.Uint32()) * -1
return true
})
}
@ -134,6 +135,9 @@ func (l VirtualDeviceList) SelectByBackingInfo(backing types.BaseVirtualDeviceBa
b := backing.(*types.VirtualEthernetCardDistributedVirtualPortBackingInfo)
return a.Port.SwitchUuid == b.Port.SwitchUuid &&
a.Port.PortgroupKey == b.Port.PortgroupKey
case *types.VirtualEthernetCardOpaqueNetworkBackingInfo:
b := backing.(*types.VirtualEthernetCardOpaqueNetworkBackingInfo)
return a.OpaqueNetworkId == b.OpaqueNetworkId
case *types.VirtualDiskFlatVer2BackingInfo:
b := backing.(*types.VirtualDiskFlatVer2BackingInfo)
if a.Parent != nil && b.Parent != nil {
@ -433,7 +437,7 @@ func (l VirtualDeviceList) AssignController(device types.BaseVirtualDevice, c ty
d.UnitNumber = new(int32)
*d.UnitNumber = l.newUnitNumber(c)
if d.Key == 0 {
d.Key = -1
d.Key = int32(rand.Uint32()) * -1
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2015-2017 VMware, Inc. All Rights Reserved.
Copyright (c) 2015-2021 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -40,6 +40,34 @@ type VirtualMachine struct {
Common
}
// extractDiskLayoutFiles is a helper function used to extract file keys for
// all disk files attached to the virtual machine at the current point of
// running.
func extractDiskLayoutFiles(diskLayoutList []types.VirtualMachineFileLayoutExDiskLayout) []int {
var result []int
for _, layoutExDisk := range diskLayoutList {
for _, link := range layoutExDisk.Chain {
for i := range link.FileKey { // diskDescriptor, diskExtent pairs
result = append(result, int(link.FileKey[i]))
}
}
}
return result
}
// removeKey is a helper function for removing a specific file key from a list
// of keys associated with disks attached to a virtual machine.
func removeKey(l *[]int, key int) {
for i, k := range *l {
if k == key {
*l = append((*l)[:i], (*l)[i+1:]...)
break
}
}
}
func NewVirtualMachine(c *vim25.Client, ref types.ManagedObjectReference) *VirtualMachine {
return &VirtualMachine{
Common: NewCommon(c, ref),
@ -181,6 +209,20 @@ func (v VirtualMachine) Clone(ctx context.Context, folder *Folder, name string,
return NewTask(v.c, res.Returnval), nil
}
func (v VirtualMachine) InstantClone(ctx context.Context, config types.VirtualMachineInstantCloneSpec) (*Task, error) {
req := types.InstantClone_Task{
This: v.Reference(),
Spec: config,
}
res, err := methods.InstantClone_Task(ctx, v.c, &req)
if err != nil {
return nil, err
}
return NewTask(v.c, res.Returnval), nil
}
func (v VirtualMachine) Customize(ctx context.Context, spec types.CustomizationSpec) (*Task, error) {
req := types.CustomizeVM_Task{
This: v.Reference(),
@ -469,6 +511,41 @@ func (v VirtualMachine) RemoveDevice(ctx context.Context, keepFiles bool, device
return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationRemove, fop, device...)
}
// AttachDisk attaches the given disk to the VirtualMachine
func (v VirtualMachine) AttachDisk(ctx context.Context, id string, datastore *Datastore, controllerKey int32, unitNumber int32) error {
req := types.AttachDisk_Task{
This: v.Reference(),
DiskId: types.ID{Id: id},
Datastore: datastore.Reference(),
ControllerKey: controllerKey,
UnitNumber: &unitNumber,
}
res, err := methods.AttachDisk_Task(ctx, v.c, &req)
if err != nil {
return err
}
task := NewTask(v.c, res.Returnval)
return task.Wait(ctx)
}
// DetachDisk detaches the given disk from the VirtualMachine
func (v VirtualMachine) DetachDisk(ctx context.Context, id string) error {
req := types.DetachDisk_Task{
This: v.Reference(),
DiskId: types.ID{Id: id},
}
res, err := methods.DetachDisk_Task(ctx, v.c, &req)
if err != nil {
return err
}
task := NewTask(v.c, res.Returnval)
return task.Wait(ctx)
}
// BootOptions returns the VirtualMachine's config.bootOptions property.
func (v VirtualMachine) BootOptions(ctx context.Context) (*types.VirtualMachineBootOptions, error) {
var o mo.VirtualMachine
@ -579,6 +656,63 @@ func (m snapshotMap) add(parent string, tree []types.VirtualMachineSnapshotTree)
}
}
// SnapshotSize calculates the size of a given snapshot in bytes. If the
// snapshot is current, disk files not associated with any parent snapshot are
// included in size calculations. This allows for measuring and including the
// growth from the last fixed snapshot to the present state.
func SnapshotSize(info types.ManagedObjectReference, parent *types.ManagedObjectReference, vmlayout *types.VirtualMachineFileLayoutEx, isCurrent bool) int {
var fileKeyList []int
var parentFiles []int
var allSnapshotFiles []int
diskFiles := extractDiskLayoutFiles(vmlayout.Disk)
for _, layout := range vmlayout.Snapshot {
diskLayout := extractDiskLayoutFiles(layout.Disk)
allSnapshotFiles = append(allSnapshotFiles, diskLayout...)
if layout.Key.Value == info.Value {
fileKeyList = append(fileKeyList, int(layout.DataKey)) // The .vmsn file
fileKeyList = append(fileKeyList, diskLayout...) // The .vmdk files
} else if parent != nil && layout.Key.Value == parent.Value {
parentFiles = append(parentFiles, diskLayout...)
}
}
for _, parentFile := range parentFiles {
removeKey(&fileKeyList, parentFile)
}
for _, file := range allSnapshotFiles {
removeKey(&diskFiles, file)
}
fileKeyMap := make(map[int]types.VirtualMachineFileLayoutExFileInfo)
for _, file := range vmlayout.File {
fileKeyMap[int(file.Key)] = file
}
size := 0
for _, fileKey := range fileKeyList {
file := fileKeyMap[fileKey]
if parent != nil ||
(file.Type != string(types.VirtualMachineFileLayoutExFileTypeDiskDescriptor) &&
file.Type != string(types.VirtualMachineFileLayoutExFileTypeDiskExtent)) {
size += int(file.Size)
}
}
if isCurrent {
for _, diskFile := range diskFiles {
file := fileKeyMap[diskFile]
size += int(file.Size)
}
}
return size
}
// FindSnapshot supports snapshot lookup by name, where name can be:
// 1) snapshot ManagedObjectReference.Value (unique)
// 2) snapshot name (may not be unique)

128
vendor/github.com/vmware/govmomi/ovf/cim.go generated vendored Normal file
View File

@ -0,0 +1,128 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"github.com/vmware/govmomi/vim25/types"
)
/*
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_VirtualSystemSettingData.xsd
*/
type CIMVirtualSystemSettingData struct {
ElementName string `xml:"ElementName"`
InstanceID string `xml:"InstanceID"`
AutomaticRecoveryAction *uint8 `xml:"AutomaticRecoveryAction"`
AutomaticShutdownAction *uint8 `xml:"AutomaticShutdownAction"`
AutomaticStartupAction *uint8 `xml:"AutomaticStartupAction"`
AutomaticStartupActionDelay *string `xml:"AutomaticStartupActionDelay>Interval"`
AutomaticStartupActionSequenceNumber *uint16 `xml:"AutomaticStartupActionSequenceNumber"`
Caption *string `xml:"Caption"`
ConfigurationDataRoot *string `xml:"ConfigurationDataRoot"`
ConfigurationFile *string `xml:"ConfigurationFile"`
ConfigurationID *string `xml:"ConfigurationID"`
CreationTime *string `xml:"CreationTime"`
Description *string `xml:"Description"`
LogDataRoot *string `xml:"LogDataRoot"`
Notes []string `xml:"Notes"`
RecoveryFile *string `xml:"RecoveryFile"`
SnapshotDataRoot *string `xml:"SnapshotDataRoot"`
SuspendDataRoot *string `xml:"SuspendDataRoot"`
SwapFileDataRoot *string `xml:"SwapFileDataRoot"`
VirtualSystemIdentifier *string `xml:"VirtualSystemIdentifier"`
VirtualSystemType *string `xml:"VirtualSystemType"`
}
/*
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_ResourceAllocationSettingData.xsd
*/
type CIMResourceAllocationSettingData struct {
ElementName string `xml:"ElementName"`
InstanceID string `xml:"InstanceID"`
ResourceType *uint16 `xml:"ResourceType"`
OtherResourceType *string `xml:"OtherResourceType"`
ResourceSubType *string `xml:"ResourceSubType"`
AddressOnParent *string `xml:"AddressOnParent"`
Address *string `xml:"Address"`
AllocationUnits *string `xml:"AllocationUnits"`
AutomaticAllocation *bool `xml:"AutomaticAllocation"`
AutomaticDeallocation *bool `xml:"AutomaticDeallocation"`
Caption *string `xml:"Caption"`
Connection []string `xml:"Connection"`
ConsumerVisibility *uint16 `xml:"ConsumerVisibility"`
Description *string `xml:"Description"`
HostResource []string `xml:"HostResource"`
Limit *uint64 `xml:"Limit"`
MappingBehavior *uint `xml:"MappingBehavior"`
Parent *string `xml:"Parent"`
PoolID *string `xml:"PoolID"`
Reservation *uint64 `xml:"Reservation"`
VirtualQuantity *uint `xml:"VirtualQuantity"`
VirtualQuantityUnits *string `xml:"VirtualQuantityUnits"`
Weight *uint `xml:"Weight"`
}
/*
Source: http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2.24.0/CIM_StorageAllocationSettingData.xsd
*/
type CIMStorageAllocationSettingData struct {
ElementName string `xml:"ElementName"`
InstanceID string `xml:"InstanceID"`
ResourceType *uint16 `xml:"ResourceType"`
OtherResourceType *string `xml:"OtherResourceType"`
ResourceSubType *string `xml:"ResourceSubType"`
Access *uint16 `xml:"Access"`
Address *string `xml:"Address"`
AddressOnParent *string `xml:"AddressOnParent"`
AllocationUnits *string `xml:"AllocationUnits"`
AutomaticAllocation *bool `xml:"AutomaticAllocation"`
AutomaticDeallocation *bool `xml:"AutomaticDeallocation"`
Caption *string `xml:"Caption"`
ChangeableType *uint16 `xml:"ChangeableType"`
ComponentSetting []types.AnyType `xml:"ComponentSetting"`
ConfigurationName *string `xml:"ConfigurationName"`
Connection []string `xml:"Connection"`
ConsumerVisibility *uint16 `xml:"ConsumerVisibility"`
Description *string `xml:"Description"`
Generation *uint64 `xml:"Generation"`
HostExtentName *string `xml:"HostExtentName"`
HostExtentNameFormat *uint16 `xml:"HostExtentNameFormat"`
HostExtentNameNamespace *uint16 `xml:"HostExtentNameNamespace"`
HostExtentStartingAddress *uint64 `xml:"HostExtentStartingAddress"`
HostResource []string `xml:"HostResource"`
HostResourceBlockSize *uint64 `xml:"HostResourceBlockSize"`
Limit *uint64 `xml:"Limit"`
MappingBehavior *uint `xml:"MappingBehavior"`
OtherHostExtentNameFormat *string `xml:"OtherHostExtentNameFormat"`
OtherHostExtentNameNamespace *string `xml:"OtherHostExtentNameNamespace"`
Parent *string `xml:"Parent"`
PoolID *string `xml:"PoolID"`
Reservation *uint64 `xml:"Reservation"`
SoID *string `xml:"SoID"`
SoOrgID *string `xml:"SoOrgID"`
VirtualQuantity *uint `xml:"VirtualQuantity"`
VirtualQuantityUnits *string `xml:"VirtualQuantityUnits"`
VirtualResourceBlockSize *uint64 `xml:"VirtualResourceBlockSize"`
Weight *uint `xml:"Weight"`
}

25
vendor/github.com/vmware/govmomi/ovf/doc.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
Package ovf provides functionality to unmarshal and inspect the structure
of an OVF file. It is not a complete implementation of the specification and
is intended to be used to import virtual infrastructure into vSphere.
For a complete specification of the OVF standard, refer to:
https://www.dmtf.org/sites/default/files/standards/documents/DSP0243_2.1.0.pdf
*/
package ovf

99
vendor/github.com/vmware/govmomi/ovf/env.go generated vendored Normal file
View File

@ -0,0 +1,99 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"bytes"
"fmt"
"github.com/vmware/govmomi/vim25/xml"
)
const (
ovfEnvHeader = `<Environment
xmlns="http://schemas.dmtf.org/ovf/environment/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
xmlns:ve="http://www.vmware.com/schema/ovfenv"
oe:id=""
ve:esxId="%s">`
ovfEnvPlatformSection = `<PlatformSection>
<Kind>%s</Kind>
<Version>%s</Version>
<Vendor>%s</Vendor>
<Locale>%s</Locale>
</PlatformSection>`
ovfEnvPropertyHeader = `<PropertySection>`
ovfEnvPropertyEntry = `<Property oe:key="%s" oe:value="%s"/>`
ovfEnvPropertyFooter = `</PropertySection>`
ovfEnvFooter = `</Environment>`
)
type Env struct {
XMLName xml.Name `xml:"http://schemas.dmtf.org/ovf/environment/1 Environment"`
ID string `xml:"id,attr"`
EsxID string `xml:"http://www.vmware.com/schema/ovfenv esxId,attr"`
Platform *PlatformSection `xml:"PlatformSection"`
Property *PropertySection `xml:"PropertySection"`
}
type PlatformSection struct {
Kind string `xml:"Kind"`
Version string `xml:"Version"`
Vendor string `xml:"Vendor"`
Locale string `xml:"Locale"`
}
type PropertySection struct {
Properties []EnvProperty `xml:"Property"`
}
type EnvProperty struct {
Key string `xml:"key,attr"`
Value string `xml:"value,attr"`
}
// Marshal marshals Env to xml by using xml.Marshal.
func (e Env) Marshal() (string, error) {
x, err := xml.Marshal(e)
if err != nil {
return "", err
}
return fmt.Sprintf("%s%s", xml.Header, x), nil
}
// MarshalManual manually marshals Env to xml suitable for a vApp guest.
// It exists to overcome the lack of expressiveness in Go's XML namespaces.
func (e Env) MarshalManual() string {
var buffer bytes.Buffer
buffer.WriteString(xml.Header)
buffer.WriteString(fmt.Sprintf(ovfEnvHeader, e.EsxID))
buffer.WriteString(fmt.Sprintf(ovfEnvPlatformSection, e.Platform.Kind, e.Platform.Version, e.Platform.Vendor, e.Platform.Locale))
buffer.WriteString(fmt.Sprint(ovfEnvPropertyHeader))
for _, p := range e.Property.Properties {
buffer.WriteString(fmt.Sprintf(ovfEnvPropertyEntry, p.Key, p.Value))
}
buffer.WriteString(fmt.Sprint(ovfEnvPropertyFooter))
buffer.WriteString(fmt.Sprint(ovfEnvFooter))
return buffer.String()
}

208
vendor/github.com/vmware/govmomi/ovf/envelope.go generated vendored Normal file
View File

@ -0,0 +1,208 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
type Envelope struct {
References []File `xml:"References>File"`
// Package level meta-data
Annotation *AnnotationSection `xml:"AnnotationSection"`
Product *ProductSection `xml:"ProductSection"`
Network *NetworkSection `xml:"NetworkSection"`
Disk *DiskSection `xml:"DiskSection"`
OperatingSystem *OperatingSystemSection `xml:"OperatingSystemSection"`
Eula *EulaSection `xml:"EulaSection"`
VirtualHardware *VirtualHardwareSection `xml:"VirtualHardwareSection"`
ResourceAllocation *ResourceAllocationSection `xml:"ResourceAllocationSection"`
DeploymentOption *DeploymentOptionSection `xml:"DeploymentOptionSection"`
// Content: A VirtualSystem or a VirtualSystemCollection
VirtualSystem *VirtualSystem `xml:"VirtualSystem"`
}
type VirtualSystem struct {
Content
Annotation []AnnotationSection `xml:"AnnotationSection"`
Product []ProductSection `xml:"ProductSection"`
OperatingSystem []OperatingSystemSection `xml:"OperatingSystemSection"`
Eula []EulaSection `xml:"EulaSection"`
VirtualHardware []VirtualHardwareSection `xml:"VirtualHardwareSection"`
}
type File struct {
ID string `xml:"id,attr"`
Href string `xml:"href,attr"`
Size uint `xml:"size,attr"`
Compression *string `xml:"compression,attr"`
ChunkSize *int `xml:"chunkSize,attr"`
}
type Content struct {
ID string `xml:"id,attr"`
Info string `xml:"Info"`
Name *string `xml:"Name"`
}
type Section struct {
Required *bool `xml:"required,attr"`
Info string `xml:"Info"`
}
type AnnotationSection struct {
Section
Annotation string `xml:"Annotation"`
}
type ProductSection struct {
Section
Class *string `xml:"class,attr"`
Instance *string `xml:"instance,attr"`
Product string `xml:"Product"`
Vendor string `xml:"Vendor"`
Version string `xml:"Version"`
FullVersion string `xml:"FullVersion"`
ProductURL string `xml:"ProductUrl"`
VendorURL string `xml:"VendorUrl"`
AppURL string `xml:"AppUrl"`
Property []Property `xml:"Property"`
}
type Property struct {
Key string `xml:"key,attr"`
Type string `xml:"type,attr"`
Qualifiers *string `xml:"qualifiers,attr"`
UserConfigurable *bool `xml:"userConfigurable,attr"`
Default *string `xml:"value,attr"`
Password *bool `xml:"password,attr"`
Label *string `xml:"Label"`
Description *string `xml:"Description"`
Values []PropertyConfigurationValue `xml:"Value"`
}
type PropertyConfigurationValue struct {
Value string `xml:"value,attr"`
Configuration *string `xml:"configuration,attr"`
}
type NetworkSection struct {
Section
Networks []Network `xml:"Network"`
}
type Network struct {
Name string `xml:"name,attr"`
Description string `xml:"Description"`
}
type DiskSection struct {
Section
Disks []VirtualDiskDesc `xml:"Disk"`
}
type VirtualDiskDesc struct {
DiskID string `xml:"diskId,attr"`
FileRef *string `xml:"fileRef,attr"`
Capacity string `xml:"capacity,attr"`
CapacityAllocationUnits *string `xml:"capacityAllocationUnits,attr"`
Format *string `xml:"format,attr"`
PopulatedSize *int `xml:"populatedSize,attr"`
ParentRef *string `xml:"parentRef,attr"`
}
type OperatingSystemSection struct {
Section
ID int16 `xml:"id,attr"`
Version *string `xml:"version,attr"`
OSType *string `xml:"osType,attr"`
Description *string `xml:"Description"`
}
type EulaSection struct {
Section
License string `xml:"License"`
}
type Config struct {
Required *bool `xml:"required,attr"`
Key string `xml:"key,attr"`
Value string `xml:"value,attr"`
}
type VirtualHardwareSection struct {
Section
ID *string `xml:"id,attr"`
Transport *string `xml:"transport,attr"`
System *VirtualSystemSettingData `xml:"System"`
Item []ResourceAllocationSettingData `xml:"Item"`
StorageItem []StorageAllocationSettingData `xml:"StorageItem"`
Config []Config `xml:"Config"`
ExtraConfig []Config `xml:"ExtraConfig"`
}
type VirtualSystemSettingData struct {
CIMVirtualSystemSettingData
}
type ResourceAllocationSettingData struct {
CIMResourceAllocationSettingData
Required *bool `xml:"required,attr"`
Configuration *string `xml:"configuration,attr"`
Bound *string `xml:"bound,attr"`
}
type StorageAllocationSettingData struct {
CIMStorageAllocationSettingData
Required *bool `xml:"required,attr"`
Configuration *string `xml:"configuration,attr"`
Bound *string `xml:"bound,attr"`
}
type ResourceAllocationSection struct {
Section
Item []ResourceAllocationSettingData `xml:"Item"`
}
type DeploymentOptionSection struct {
Section
Configuration []DeploymentOptionConfiguration `xml:"Configuration"`
}
type DeploymentOptionConfiguration struct {
ID string `xml:"id,attr"`
Default *bool `xml:"default,attr"`
Label string `xml:"Label"`
Description string `xml:"Description"`
}

103
vendor/github.com/vmware/govmomi/ovf/manager.go generated vendored Normal file
View File

@ -0,0 +1,103 @@
/*
Copyright (c) 2015-2017 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"context"
"github.com/vmware/govmomi/vim25"
"github.com/vmware/govmomi/vim25/methods"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
)
type Manager struct {
types.ManagedObjectReference
c *vim25.Client
}
func NewManager(c *vim25.Client) *Manager {
return &Manager{*c.ServiceContent.OvfManager, c}
}
// CreateDescriptor wraps methods.CreateDescriptor
func (m *Manager) CreateDescriptor(ctx context.Context, obj mo.Reference, cdp types.OvfCreateDescriptorParams) (*types.OvfCreateDescriptorResult, error) {
req := types.CreateDescriptor{
This: m.Reference(),
Obj: obj.Reference(),
Cdp: cdp,
}
res, err := methods.CreateDescriptor(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// CreateImportSpec wraps methods.CreateImportSpec
func (m *Manager) CreateImportSpec(ctx context.Context, ovfDescriptor string, resourcePool mo.Reference, datastore mo.Reference, cisp types.OvfCreateImportSpecParams) (*types.OvfCreateImportSpecResult, error) {
req := types.CreateImportSpec{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
ResourcePool: resourcePool.Reference(),
Datastore: datastore.Reference(),
Cisp: cisp,
}
res, err := methods.CreateImportSpec(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// ParseDescriptor wraps methods.ParseDescriptor
func (m *Manager) ParseDescriptor(ctx context.Context, ovfDescriptor string, pdp types.OvfParseDescriptorParams) (*types.OvfParseDescriptorResult, error) {
req := types.ParseDescriptor{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
Pdp: pdp,
}
res, err := methods.ParseDescriptor(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}
// ValidateHost wraps methods.ValidateHost
func (m *Manager) ValidateHost(ctx context.Context, ovfDescriptor string, host mo.Reference, vhp types.OvfValidateHostParams) (*types.OvfValidateHostResult, error) {
req := types.ValidateHost{
This: m.Reference(),
OvfDescriptor: ovfDescriptor,
Host: host.Reference(),
Vhp: vhp,
}
res, err := methods.ValidateHost(ctx, m.c, &req)
if err != nil {
return nil, err
}
return &res.Returnval, nil
}

35
vendor/github.com/vmware/govmomi/ovf/ovf.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ovf
import (
"io"
"github.com/vmware/govmomi/vim25/xml"
)
func Unmarshal(r io.Reader) (*Envelope, error) {
var e Envelope
dec := xml.NewDecoder(r)
err := dec.Decode(&e)
if err != nil {
return nil, err
}
return &e, nil
}

View File

@ -281,3 +281,14 @@ func (sm *Manager) CloneSession(ctx context.Context, ticket string) error {
sm.userSession = &res.Returnval
return nil
}
func (sm *Manager) UpdateServiceMessage(ctx context.Context, message string) error {
req := types.UpdateServiceMessage{
This: sm.Reference(),
Message: message,
}
_, err := methods.UpdateServiceMessage(ctx, sm.client, &req)
return err
}

Some files were not shown because too many files have changed in this diff Show More