Add content_library_destination to import VM template to a Content Library (#9551)
This commit is contained in:
parent
61b79b6e53
commit
9c1409dbba
|
@ -104,6 +104,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
},
|
||||
)
|
||||
|
||||
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,
|
||||
|
|
|
@ -33,8 +33,13 @@ type Config struct {
|
|||
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
|
||||
}
|
||||
|
@ -67,6 +72,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
if c.Export != nil {
|
||||
errs = packer.MultiErrorAppend(errs, c.Export.Prepare(&c.ctx, &c.LocationConfig, &c.PackerConfig)...)
|
||||
}
|
||||
if c.ContentLibraryDestinationConfig != nil {
|
||||
errs = packer.MultiErrorAppend(errs, c.ContentLibraryDestinationConfig.Prepare(&c.LocationConfig)...)
|
||||
}
|
||||
|
||||
if len(errs.Errors) > 0 {
|
||||
return nil, errs
|
||||
|
|
|
@ -10,109 +10,110 @@ import (
|
|||
// 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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
Notes *string `mapstructure:"notes" cty:"notes" hcl:"notes"`
|
||||
VAppConfig *FlatvAppConfig `mapstructure:"vapp" cty:"vapp" hcl:"vapp"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
Notes *string `mapstructure:"notes" cty:"notes" hcl:"notes"`
|
||||
VAppConfig *FlatvAppConfig `mapstructure:"vapp" cty:"vapp" hcl:"vapp"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_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"`
|
||||
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.
|
||||
|
@ -230,6 +231,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"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
|
||||
}
|
||||
|
|
|
@ -44,8 +44,8 @@ type StepConnect struct {
|
|||
Config *ConnectConfig
|
||||
}
|
||||
|
||||
func (s *StepConnect) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
d, err := driver.NewDriver(&driver.ConnectConfig{
|
||||
func (s *StepConnect) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
d, err := driver.NewDriver(ctx, &driver.ConnectConfig{
|
||||
VCenterServer: s.Config.VCenterServer,
|
||||
Username: s.Config.Username,
|
||||
Password: s.Config.Password,
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type ContentLibraryDestinationConfig
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
"github.com/vmware/govmomi/vapi/vcenter"
|
||||
)
|
||||
|
||||
// With this configuration Packer creates a library item in a content library whose content is a virtual machine template created from the just built VM.
|
||||
// The virtual machine template is stored in a newly created library item.
|
||||
type ContentLibraryDestinationConfig struct {
|
||||
// Name of the library in which the new library item containing the VM template should be created.
|
||||
// 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. The name of the item should be different from [vm_name](#vm_name).
|
||||
// Defaults to [vm_name](#vm_name) + timestamp.
|
||||
Name string `mapstructure:"name"`
|
||||
// Description of the library item that will be created. 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.
|
||||
// Defaults to [cluster](#cluster).
|
||||
Cluster string `mapstructure:"cluster"`
|
||||
// Virtual machine folder into which the virtual machine template should be placed.
|
||||
// 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.
|
||||
// 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.
|
||||
// Defaults to the storage backing associated with the library specified by library.
|
||||
Datastore string `mapstructure:"datastore"`
|
||||
}
|
||||
|
||||
func (c *ContentLibraryDestinationConfig) Prepare(lc *LocationConfig) []error {
|
||||
var errs *packer.MultiError
|
||||
|
||||
if c.Library == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("a library name must be provided"))
|
||||
}
|
||||
if c.Name == lc.VMName {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("the content library destination name must be different from the VM name"))
|
||||
}
|
||||
|
||||
if c.Name == "" {
|
||||
// Add timestamp to the 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 = packer.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").(packer.Ui)
|
||||
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Importing VM template %s to Content Library...", s.ContentLibConfig.Name))
|
||||
err := vm.ImportToContentLibrary(template)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Failed to import VM template %s: %s", s.ContentLibConfig.Name, err.Error()))
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepImportToContentLibrary) Cleanup(multistep.StateBag) {
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// 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"`
|
||||
}
|
||||
|
||||
// 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},
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
@ -44,7 +45,7 @@ func TestConn(t *testing.T) *driver.Driver {
|
|||
password = "jetbrains"
|
||||
}
|
||||
|
||||
d, err := driver.NewDriver(&driver.ConnectConfig{
|
||||
d, err := driver.NewDriver(context.TODO(), &driver.ConnectConfig{
|
||||
VCenterServer: "vcenter.vsphere65.test",
|
||||
Username: username,
|
||||
Password: password,
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package driver
|
||||
|
||||
import "github.com/vmware/govmomi/object"
|
||||
|
||||
type Cluster struct {
|
||||
driver *Driver
|
||||
cluster *object.ClusterComputeResource
|
||||
}
|
||||
|
||||
func (d *Driver) 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
|
||||
}
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/vmware/govmomi/find"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/session"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
)
|
||||
|
@ -17,6 +18,7 @@ import (
|
|||
type Driver struct {
|
||||
ctx context.Context
|
||||
client *govmomi.Client
|
||||
restClient *rest.Client
|
||||
finder *find.Finder
|
||||
datacenter *object.Datacenter
|
||||
}
|
||||
|
@ -29,9 +31,7 @@ type ConnectConfig struct {
|
|||
Datacenter string
|
||||
}
|
||||
|
||||
func NewDriver(config *ConnectConfig) (*Driver, error) {
|
||||
ctx := context.TODO()
|
||||
|
||||
func NewDriver(ctx context.Context, config *ConnectConfig) (*Driver, error) {
|
||||
vcenterUrl, err := url.Parse(fmt.Sprintf("https://%v/sdk", config.VCenterServer))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -56,6 +56,12 @@ func NewDriver(config *ConnectConfig) (*Driver, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
restClient := rest.NewClient(vimClient)
|
||||
err = restClient.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 {
|
||||
|
@ -66,6 +72,7 @@ func NewDriver(config *ConnectConfig) (*Driver, error) {
|
|||
d := Driver{
|
||||
ctx: ctx,
|
||||
client: client,
|
||||
restClient: restClient,
|
||||
datacenter: datacenter,
|
||||
finder: finder,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package driver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
@ -21,7 +22,7 @@ func newTestDriver(t *testing.T) *Driver {
|
|||
password = "jetbrains"
|
||||
}
|
||||
|
||||
d, err := NewDriver(&ConnectConfig{
|
||||
d, err := NewDriver(context.TODO(), &ConnectConfig{
|
||||
VCenterServer: "vcenter.vsphere65.test",
|
||||
Username: username,
|
||||
Password: password,
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package driver
|
||||
|
||||
import "github.com/vmware/govmomi/vapi/library"
|
||||
|
||||
type Library struct {
|
||||
driver *Driver
|
||||
library *library.Library
|
||||
}
|
||||
|
||||
func (d *Driver) FindContentLibrary(name string) (*Library, error) {
|
||||
lm := library.NewManager(d.restClient)
|
||||
l, err := lm.GetLibraryByName(d.ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Library{
|
||||
library: l,
|
||||
driver: d,
|
||||
}, nil
|
||||
}
|
|
@ -11,12 +11,12 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
||||
"github.com/vmware/govmomi/find"
|
||||
"github.com/vmware/govmomi/nfc"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/ovf"
|
||||
"github.com/vmware/govmomi/property"
|
||||
"github.com/vmware/govmomi/vapi/vcenter"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
@ -667,6 +667,60 @@ func (vm *VirtualMachine) ConvertToTemplate() error {
|
|||
return vm.vm.MarkAsTemplate(vm.driver.ctx)
|
||||
}
|
||||
|
||||
func (vm *VirtualMachine) ImportToContentLibrary(template vcenter.Template) error {
|
||||
template.SourceVM = vm.vm.Reference().Value
|
||||
|
||||
l, err := vm.driver.FindContentLibrary(template.Library)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if l.library.Type != "LOCAL" {
|
||||
return fmt.Errorf("can not deploy a VM to the content library %s of type %s; the content library must be of type LOCAL", template.Library, l.library.Type)
|
||||
}
|
||||
template.Library = l.library.ID
|
||||
|
||||
if template.Placement.Cluster != "" {
|
||||
c, err := vm.driver.FindCluster(template.Placement.Cluster)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
template.Placement.Cluster = c.cluster.Reference().Value
|
||||
}
|
||||
if template.Placement.Folder != "" {
|
||||
f, err := vm.driver.FindFolder(template.Placement.Folder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
template.Placement.Folder = f.folder.Reference().Value
|
||||
}
|
||||
if template.Placement.Host != "" {
|
||||
h, err := vm.driver.FindHost(template.Placement.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
template.Placement.Host = h.host.Reference().Value
|
||||
}
|
||||
if template.Placement.ResourcePool != "" {
|
||||
rp, err := vm.driver.FindResourcePool(template.Placement.Cluster, template.Placement.Host, template.Placement.ResourcePool)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
template.Placement.ResourcePool = rp.pool.Reference().Value
|
||||
}
|
||||
|
||||
if template.VMHomeStorage != nil {
|
||||
d, err := vm.driver.FindDatastore(template.VMHomeStorage.Datastore, template.Placement.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
template.VMHomeStorage.Datastore = d.ds.Reference().Value
|
||||
}
|
||||
|
||||
vcm := vcenter.NewManager(vm.driver.restClient)
|
||||
_, err = vcm.CreateTemplate(vm.driver.ctx, template)
|
||||
return err
|
||||
}
|
||||
|
||||
func (vm *VirtualMachine) GetDir() (string, error) {
|
||||
vmInfo, err := vm.Info("name", "layoutEx.file")
|
||||
if err != nil {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
)
|
||||
|
||||
func main() {
|
||||
d, err := driver.NewDriver(&driver.ConnectConfig{
|
||||
d, err := driver.NewDriver(context.TODO(), &driver.ConnectConfig{
|
||||
VCenterServer: "vcenter.vsphere65.test",
|
||||
Username: "root",
|
||||
Password: "jetbrains",
|
||||
|
|
|
@ -145,6 +145,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
},
|
||||
)
|
||||
|
||||
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,
|
||||
|
|
|
@ -42,6 +42,10 @@ type Config struct {
|
|||
// 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
|
||||
}
|
||||
|
@ -83,6 +87,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
if c.Export != nil {
|
||||
errs = packer.MultiErrorAppend(errs, c.Export.Prepare(&c.ctx, &c.LocationConfig, &c.PackerConfig)...)
|
||||
}
|
||||
if c.ContentLibraryDestinationConfig != nil {
|
||||
errs = packer.MultiErrorAppend(errs, c.ContentLibraryDestinationConfig.Prepare(&c.LocationConfig)...)
|
||||
}
|
||||
|
||||
if len(errs.Errors) > 0 {
|
||||
return warnings, errs
|
||||
|
|
|
@ -10,122 +10,123 @@ import (
|
|||
// 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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
Firmware *string `mapstructure:"firmware" cty:"firmware" hcl:"firmware"`
|
||||
DiskControllerType *string `mapstructure:"disk_controller_type" cty:"disk_controller_type" hcl:"disk_controller_type"`
|
||||
Storage []FlatDiskConfig `mapstructure:"storage" cty:"storage" hcl:"storage"`
|
||||
NICs []FlatNIC `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
USBController *bool `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"`
|
||||
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"`
|
||||
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"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
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"`
|
||||
Firmware *string `mapstructure:"firmware" cty:"firmware" hcl:"firmware"`
|
||||
DiskControllerType *string `mapstructure:"disk_controller_type" cty:"disk_controller_type" hcl:"disk_controller_type"`
|
||||
Storage []FlatDiskConfig `mapstructure:"storage" cty:"storage" hcl:"storage"`
|
||||
NICs []FlatNIC `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
USBController *bool `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"`
|
||||
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"`
|
||||
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"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_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"`
|
||||
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.
|
||||
|
@ -256,6 +257,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"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
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ func (s *StepRemoveFloppy) Run(_ context.Context, state multistep.StateBag) mult
|
|||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Remove("uploaded_floppy_path")
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
|
|
|
@ -35,6 +35,8 @@ func (p *HCL2PostProcessor) HCL2Prepare(buildVars map[string]interface{}) error
|
|||
switch v := v.(type) {
|
||||
case string:
|
||||
buildValues[k] = cty.StringVal(v)
|
||||
case int64:
|
||||
buildValues[k] = cty.NumberIntVal(v)
|
||||
default:
|
||||
return fmt.Errorf("unhandled buildvar type: %T", v)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package hcl2template
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
@ -35,6 +34,8 @@ func (p *HCL2Provisioner) HCL2Prepare(buildVars map[string]interface{}) error {
|
|||
switch v := v.(type) {
|
||||
case string:
|
||||
buildValues[k] = cty.StringVal(v)
|
||||
case int64:
|
||||
buildValues[k] = cty.NumberIntVal(v)
|
||||
default:
|
||||
return fmt.Errorf("unhandled buildvar type: %T", v)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ type StateBag interface {
|
|||
Get(string) interface{}
|
||||
GetOk(string) (interface{}, bool)
|
||||
Put(string, interface{})
|
||||
Remove(string)
|
||||
}
|
||||
|
||||
// BasicStateBag implements StateBag by using a normal map underneath
|
||||
|
@ -45,3 +46,7 @@ func (b *BasicStateBag) Put(k string, v interface{}) {
|
|||
// Write the data
|
||||
b.data[k] = v
|
||||
}
|
||||
|
||||
func (b *BasicStateBag) Remove(k string) {
|
||||
delete(b.data, k)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 (
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// VAPI REST Paths
|
||||
const (
|
||||
SessionPath = "/com/vmware/cis/session"
|
||||
CategoryPath = "/com/vmware/cis/tagging/category"
|
||||
TagPath = "/com/vmware/cis/tagging/tag"
|
||||
AssociationPath = "/com/vmware/cis/tagging/tag-association"
|
||||
LibraryPath = "/com/vmware/content/library"
|
||||
LibraryItemFileData = "/com/vmware/cis/data"
|
||||
LibraryItemPath = "/com/vmware/content/library/item"
|
||||
LibraryItemFilePath = "/com/vmware/content/library/item/file"
|
||||
LibraryItemUpdateSession = "/com/vmware/content/library/item/update-session"
|
||||
LibraryItemUpdateSessionFile = "/com/vmware/content/library/item/updatesession/file"
|
||||
LibraryItemDownloadSession = "/com/vmware/content/library/item/download-session"
|
||||
LibraryItemDownloadSessionFile = "/com/vmware/content/library/item/downloadsession/file"
|
||||
LocalLibraryPath = "/com/vmware/content/local-library"
|
||||
SubscribedLibraryPath = "/com/vmware/content/subscribed-library"
|
||||
SubscribedLibraryItem = "/com/vmware/content/library/subscribed-item"
|
||||
VCenterOVFLibraryItem = "/com/vmware/vcenter/ovf/library-item"
|
||||
VCenterVMTXLibraryItem = "/vcenter/vm-template/library-items"
|
||||
VCenterVM = "/vcenter/vm"
|
||||
SessionCookieName = "vmware-api-session-id"
|
||||
)
|
||||
|
||||
// AssociatedObject is the same structure as types.ManagedObjectReference,
|
||||
// just with a different field name (ID instead of Value).
|
||||
// In the API we use mo.Reference, this type is only used for wire transfer.
|
||||
type AssociatedObject struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"id"`
|
||||
}
|
||||
|
||||
// Reference implements mo.Reference
|
||||
func (o AssociatedObject) Reference() types.ManagedObjectReference {
|
||||
return types.ManagedObjectReference(o)
|
||||
}
|
||||
|
||||
// Association for tag-association requests.
|
||||
type Association struct {
|
||||
ObjectID *AssociatedObject `json:"object_id,omitempty"`
|
||||
}
|
||||
|
||||
// NewAssociation returns an Association, converting ref to an AssociatedObject.
|
||||
func NewAssociation(ref mo.Reference) Association {
|
||||
obj := AssociatedObject(ref.Reference())
|
||||
return Association{
|
||||
ObjectID: &obj,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
)
|
||||
|
||||
// StorageBackings for Content Libraries
|
||||
type StorageBackings struct {
|
||||
DatastoreID string `json:"datastore_id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// Library provides methods to create, read, update, delete, and enumerate libraries.
|
||||
type Library struct {
|
||||
CreationTime *time.Time `json:"creation_time,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
LastModifiedTime *time.Time `json:"last_modified_time,omitempty"`
|
||||
LastSyncTime *time.Time `json:"last_sync_time,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Storage []StorageBackings `json:"storage_backings,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
Subscription *Subscription `json:"subscription_info,omitempty"`
|
||||
}
|
||||
|
||||
// Subscription info
|
||||
type Subscription struct {
|
||||
AuthenticationMethod string `json:"authentication_method"`
|
||||
AutomaticSyncEnabled *bool `json:"automatic_sync_enabled,omitempty"`
|
||||
OnDemand *bool `json:"on_demand,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
SslThumbprint string `json:"ssl_thumbprint,omitempty"`
|
||||
SubscriptionURL string `json:"subscription_url,omitempty"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
}
|
||||
|
||||
// Patch merges updates from the given src.
|
||||
func (l *Library) Patch(src *Library) {
|
||||
if src.Name != "" {
|
||||
l.Name = src.Name
|
||||
}
|
||||
if src.Description != "" {
|
||||
l.Description = src.Description
|
||||
}
|
||||
if src.Version != "" {
|
||||
l.Version = src.Version
|
||||
}
|
||||
}
|
||||
|
||||
// Manager extends rest.Client, adding content library related methods.
|
||||
type Manager struct {
|
||||
*rest.Client
|
||||
}
|
||||
|
||||
// NewManager creates a new Manager instance with the given client.
|
||||
func NewManager(client *rest.Client) *Manager {
|
||||
return &Manager{
|
||||
Client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Find is the search criteria for finding libraries.
|
||||
type Find struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// FindLibrary returns one or more libraries that match the provided search
|
||||
// criteria.
|
||||
//
|
||||
// The provided name is case-insensitive.
|
||||
//
|
||||
// Either the name or type of library may be set to empty values in order
|
||||
// to search for all libraries, all libraries with a specific name, regardless
|
||||
// of type, or all libraries of a specified type.
|
||||
func (c *Manager) FindLibrary(ctx context.Context, search Find) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryPath).WithAction("find")
|
||||
spec := struct {
|
||||
Spec Find `json:"spec"`
|
||||
}{search}
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// CreateLibrary creates a new library with the given Type, Name,
|
||||
// Description, and CategoryID.
|
||||
func (c *Manager) CreateLibrary(ctx context.Context, library Library) (string, error) {
|
||||
spec := struct {
|
||||
Library Library `json:"create_spec"`
|
||||
}{library}
|
||||
path := internal.LocalLibraryPath
|
||||
if library.Type == "SUBSCRIBED" {
|
||||
path = internal.SubscribedLibraryPath
|
||||
sub := library.Subscription
|
||||
u, err := url.Parse(sub.SubscriptionURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if u.Scheme == "https" && sub.SslThumbprint == "" {
|
||||
thumbprint := c.Thumbprint(u.Host)
|
||||
if thumbprint == "" {
|
||||
t := c.Transport.(*http.Transport)
|
||||
if t.TLSClientConfig.InsecureSkipVerify {
|
||||
var info object.HostCertificateInfo
|
||||
_ = info.FromURL(u, t.TLSClientConfig)
|
||||
thumbprint = info.ThumbprintSHA1
|
||||
}
|
||||
sub.SslThumbprint = thumbprint
|
||||
}
|
||||
}
|
||||
}
|
||||
url := c.Resource(path)
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// SyncLibrary syncs a subscribed library.
|
||||
func (c *Manager) SyncLibrary(ctx context.Context, library *Library) error {
|
||||
path := internal.SubscribedLibraryPath
|
||||
url := c.Resource(path).WithID(library.ID).WithAction("sync")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// DeleteLibrary deletes an existing library.
|
||||
func (c *Manager) DeleteLibrary(ctx context.Context, library *Library) error {
|
||||
path := internal.LocalLibraryPath
|
||||
if library.Type == "SUBSCRIBED" {
|
||||
path = internal.SubscribedLibraryPath
|
||||
}
|
||||
url := c.Resource(path).WithID(library.ID)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// ListLibraries returns a list of all content library IDs in the system.
|
||||
func (c *Manager) ListLibraries(ctx context.Context) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryPath)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryByID returns information on a library for the given ID.
|
||||
func (c *Manager) GetLibraryByID(ctx context.Context, id string) (*Library, error) {
|
||||
url := c.Resource(internal.LibraryPath).WithID(id)
|
||||
var res Library
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryByName returns information on a library for the given name.
|
||||
func (c *Manager) GetLibraryByName(ctx context.Context, name string) (*Library, error) {
|
||||
// Lookup by name
|
||||
libraries, err := c.GetLibraries(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range libraries {
|
||||
if libraries[i].Name == name {
|
||||
return &libraries[i], nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("library name (%s) not found", name)
|
||||
}
|
||||
|
||||
// GetLibraries returns a list of all content library details in the system.
|
||||
func (c *Manager) GetLibraries(ctx context.Context) ([]Library, error) {
|
||||
ids, err := c.ListLibraries(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get libraries failed for: %s", err)
|
||||
}
|
||||
|
||||
var libraries []Library
|
||||
for _, id := range ids {
|
||||
library, err := c.GetLibraryByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get library %s failed for %s", id, err)
|
||||
}
|
||||
|
||||
libraries = append(libraries, *library)
|
||||
|
||||
}
|
||||
return libraries, nil
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
)
|
||||
|
||||
// Checksum provides checksum information on library item files.
|
||||
type Checksum struct {
|
||||
Algorithm string `json:"algorithm,omitempty"`
|
||||
Checksum string `json:"checksum"`
|
||||
}
|
||||
|
||||
// File provides methods to get information on library item files.
|
||||
type File struct {
|
||||
Cached *bool `json:"cached,omitempty"`
|
||||
Checksum *Checksum `json:"checksum_info,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Size *int64 `json:"size,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// ListLibraryItemFiles returns a list of all the files for a library item.
|
||||
func (c *Manager) ListLibraryItemFiles(ctx context.Context, id string) ([]File, error) {
|
||||
url := c.Resource(internal.LibraryItemFilePath).WithParam("library_item_id", id)
|
||||
var res []File
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItemFile returns a file with the provided name for a library item.
|
||||
func (c *Manager) GetLibraryItemFile(ctx context.Context, id, fileName string) (*File, error) {
|
||||
url := c.Resource(internal.LibraryItemFilePath).WithID(id).WithAction("get")
|
||||
spec := struct {
|
||||
Name string `json:"name"`
|
||||
}{fileName}
|
||||
var res File
|
||||
return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
)
|
||||
|
||||
const (
|
||||
ItemTypeISO = "iso"
|
||||
ItemTypeOVF = "ovf"
|
||||
ItemTypeVMTX = "vm-template"
|
||||
)
|
||||
|
||||
// Item provides methods to create, read, update, delete, and enumerate library items.
|
||||
type Item struct {
|
||||
Cached bool `json:"cached,omitempty"`
|
||||
ContentVersion string `json:"content_version,omitempty"`
|
||||
CreationTime *time.Time `json:"creation_time,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
LastModifiedTime *time.Time `json:"last_modified_time,omitempty"`
|
||||
LastSyncTime *time.Time `json:"last_sync_time,omitempty"`
|
||||
LibraryID string `json:"library_id,omitempty"`
|
||||
MetadataVersion string `json:"metadata_version,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
SourceID string `json:"source_id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// Patch merges updates from the given src.
|
||||
func (i *Item) Patch(src *Item) {
|
||||
if src.Name != "" {
|
||||
i.Name = src.Name
|
||||
}
|
||||
if src.Description != "" {
|
||||
i.Description = src.Description
|
||||
}
|
||||
if src.Type != "" {
|
||||
i.Type = src.Type
|
||||
}
|
||||
if src.Version != "" {
|
||||
i.Version = src.Version
|
||||
}
|
||||
}
|
||||
|
||||
// CreateLibraryItem creates a new library item
|
||||
func (c *Manager) CreateLibraryItem(ctx context.Context, item Item) (string, error) {
|
||||
type createItemSpec struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
LibraryID string `json:"library_id,omitempty"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
spec := struct {
|
||||
Item createItemSpec `json:"create_spec"`
|
||||
}{
|
||||
Item: createItemSpec{
|
||||
Name: item.Name,
|
||||
Description: item.Description,
|
||||
LibraryID: item.LibraryID,
|
||||
Type: item.Type,
|
||||
},
|
||||
}
|
||||
url := c.Resource(internal.LibraryItemPath)
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// SyncLibraryItem syncs a subscribed library item
|
||||
func (c *Manager) SyncLibraryItem(ctx context.Context, item *Item, force bool) error {
|
||||
body := struct {
|
||||
Force bool `json:"force_sync_content"`
|
||||
}{force}
|
||||
url := c.Resource(internal.SubscribedLibraryItem).WithID(item.ID).WithAction("sync")
|
||||
return c.Do(ctx, url.Request(http.MethodPost, body), nil)
|
||||
}
|
||||
|
||||
// DeleteLibraryItem deletes an existing library item.
|
||||
func (c *Manager) DeleteLibraryItem(ctx context.Context, item *Item) error {
|
||||
url := c.Resource(internal.LibraryItemPath).WithID(item.ID)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// ListLibraryItems returns a list of all items in a content library.
|
||||
func (c *Manager) ListLibraryItems(ctx context.Context, id string) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryItemPath).WithParam("library_id", id)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItem returns information on a library item for the given ID.
|
||||
func (c *Manager) GetLibraryItem(ctx context.Context, id string) (*Item, error) {
|
||||
url := c.Resource(internal.LibraryItemPath).WithID(id)
|
||||
var res Item
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItems returns a list of all the library items for the specified library.
|
||||
func (c *Manager) GetLibraryItems(ctx context.Context, libraryID string) ([]Item, error) {
|
||||
ids, err := c.ListLibraryItems(ctx, libraryID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get library items failed for: %s", err)
|
||||
}
|
||||
var items []Item
|
||||
for _, id := range ids {
|
||||
item, err := c.GetLibraryItem(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get library item for %s failed for %s", id, err)
|
||||
}
|
||||
items = append(items, *item)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// FindItem is the search criteria for finding library items.
|
||||
type FindItem struct {
|
||||
Cached *bool `json:"cached,omitempty"`
|
||||
LibraryID string `json:"library_id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
SourceID string `json:"source_id,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
// FindLibraryItems returns the IDs of all the library items that match the
|
||||
// search criteria.
|
||||
func (c *Manager) FindLibraryItems(
|
||||
ctx context.Context, search FindItem) ([]string, error) {
|
||||
|
||||
url := c.Resource(internal.LibraryItemPath).WithAction("find")
|
||||
spec := struct {
|
||||
Spec FindItem `json:"spec"`
|
||||
}{search}
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
71
vendor/github.com/vmware/govmomi/vapi/library/library_item_downloadsession_file.go
generated
vendored
Normal file
71
vendor/github.com/vmware/govmomi/vapi/library/library_item_downloadsession_file.go
generated
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
)
|
||||
|
||||
// DownloadFile is the specification for the downloadsession
|
||||
// operations file:add, file:get, and file:list.
|
||||
type DownloadFile struct {
|
||||
BytesTransferred int64 `json:"bytes_transferred"`
|
||||
Checksum *Checksum `json:"checksum_info,omitempty"`
|
||||
DownloadEndpoint *TransferEndpoint `json:"download_endpoint,omitempty"`
|
||||
ErrorMessage *rest.LocalizableMessage `json:"error_message,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// GetLibraryItemDownloadSessionFile retrieves information about a specific file that is a part of an download session.
|
||||
func (c *Manager) GetLibraryItemDownloadSessionFile(ctx context.Context, sessionID string, name string) (*DownloadFile, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSessionFile).WithID(sessionID).WithAction("get")
|
||||
spec := struct {
|
||||
Name string `json:"file_name"`
|
||||
}{name}
|
||||
var res DownloadFile
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Status == "ERROR" {
|
||||
return nil, res.ErrorMessage
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// ListLibraryItemDownloadSessionFile retrieves information about a specific file that is a part of an download session.
|
||||
func (c *Manager) ListLibraryItemDownloadSessionFile(ctx context.Context, sessionID string) ([]DownloadFile, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSessionFile).WithParam("download_session_id", sessionID)
|
||||
var res []DownloadFile
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// PrepareLibraryItemDownloadSessionFile retrieves information about a specific file that is a part of an download session.
|
||||
func (c *Manager) PrepareLibraryItemDownloadSessionFile(ctx context.Context, sessionID string, name string) (*DownloadFile, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSessionFile).WithID(sessionID).WithAction("prepare")
|
||||
spec := struct {
|
||||
Name string `json:"file_name"`
|
||||
}{name}
|
||||
var res DownloadFile
|
||||
return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
165
vendor/github.com/vmware/govmomi/vapi/library/library_item_updatesession.go
generated
vendored
Normal file
165
vendor/github.com/vmware/govmomi/vapi/library/library_item_updatesession.go
generated
vendored
Normal file
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
)
|
||||
|
||||
// Session is used to create an initial update or download session
|
||||
type Session struct {
|
||||
ClientProgress int64 `json:"client_progress,omitempty"`
|
||||
ErrorMessage *rest.LocalizableMessage `json:"error_message,omitempty"`
|
||||
ExpirationTime *time.Time `json:"expiration_time,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
LibraryItemContentVersion string `json:"library_item_content_version,omitempty"`
|
||||
LibraryItemID string `json:"library_item_id,omitempty"`
|
||||
State string `json:"state,omitempty"`
|
||||
}
|
||||
|
||||
// CreateLibraryItemUpdateSession creates a new library item
|
||||
func (c *Manager) CreateLibraryItemUpdateSession(ctx context.Context, session Session) (string, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession)
|
||||
spec := struct {
|
||||
CreateSpec Session `json:"create_spec"`
|
||||
}{session}
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItemUpdateSession gets the update session information with status
|
||||
func (c *Manager) GetLibraryItemUpdateSession(ctx context.Context, id string) (*Session, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id)
|
||||
var res Session
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// ListLibraryItemUpdateSession gets the list of update sessions
|
||||
func (c *Manager) ListLibraryItemUpdateSession(ctx context.Context) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// CancelLibraryItemUpdateSession cancels an update session
|
||||
func (c *Manager) CancelLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("cancel")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// CompleteLibraryItemUpdateSession completes an update session
|
||||
func (c *Manager) CompleteLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("complete")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// DeleteLibraryItemUpdateSession deletes an update session
|
||||
func (c *Manager) DeleteLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// FailLibraryItemUpdateSession fails an update session
|
||||
func (c *Manager) FailLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("fail")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// KeepAliveLibraryItemUpdateSession keeps an inactive update session alive.
|
||||
func (c *Manager) KeepAliveLibraryItemUpdateSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemUpdateSession).WithID(id).WithAction("keep-alive")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// WaitOnLibraryItemUpdateSession blocks until the update session is no longer
|
||||
// in the ACTIVE state.
|
||||
func (c *Manager) WaitOnLibraryItemUpdateSession(
|
||||
ctx context.Context, sessionID string,
|
||||
interval time.Duration, intervalCallback func()) error {
|
||||
|
||||
// Wait until the upload operation is complete to return.
|
||||
for {
|
||||
session, err := c.GetLibraryItemUpdateSession(ctx, sessionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if session.State != "ACTIVE" {
|
||||
if session.State == "ERROR" {
|
||||
return session.ErrorMessage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
time.Sleep(interval)
|
||||
if intervalCallback != nil {
|
||||
intervalCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CreateLibraryItemDownloadSession creates a new library item
|
||||
func (c *Manager) CreateLibraryItemDownloadSession(ctx context.Context, session Session) (string, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession)
|
||||
spec := struct {
|
||||
CreateSpec Session `json:"create_spec"`
|
||||
}{session}
|
||||
var res string
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// GetLibraryItemDownloadSession gets the download session information with status
|
||||
func (c *Manager) GetLibraryItemDownloadSession(ctx context.Context, id string) (*Session, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id)
|
||||
var res Session
|
||||
return &res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// ListLibraryItemDownloadSession gets the list of download sessions
|
||||
func (c *Manager) ListLibraryItemDownloadSession(ctx context.Context) ([]string, error) {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession)
|
||||
var res []string
|
||||
return res, c.Do(ctx, url.Request(http.MethodGet), &res)
|
||||
}
|
||||
|
||||
// CancelLibraryItemDownloadSession cancels an download session
|
||||
func (c *Manager) CancelLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id).WithAction("cancel")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// DeleteLibraryItemDownloadSession deletes an download session
|
||||
func (c *Manager) DeleteLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id)
|
||||
return c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
}
|
||||
|
||||
// FailLibraryItemDownloadSession fails an download session
|
||||
func (c *Manager) FailLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id).WithAction("fail")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
||||
|
||||
// KeepAliveLibraryItemDownloadSession keeps an inactive download session alive.
|
||||
func (c *Manager) KeepAliveLibraryItemDownloadSession(ctx context.Context, id string) error {
|
||||
url := c.Resource(internal.LibraryItemDownloadSession).WithID(id).WithAction("keep-alive")
|
||||
return c.Do(ctx, url.Request(http.MethodPost), nil)
|
||||
}
|
149
vendor/github.com/vmware/govmomi/vapi/library/library_item_updatesession_file.go
generated
vendored
Normal file
149
vendor/github.com/vmware/govmomi/vapi/library/library_item_updatesession_file.go
generated
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 library
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
)
|
||||
|
||||
// TransferEndpoint provides information on the source of a library item file.
|
||||
type TransferEndpoint struct {
|
||||
URI string `json:"uri,omitempty"`
|
||||
SSLCertificateThumbprint string `json:"ssl_certificate_thumbprint,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateFile is the specification for the updatesession
|
||||
// operations file:add, file:get, and file:list.
|
||||
type UpdateFile struct {
|
||||
BytesTransferred int64 `json:"bytes_transferred,omitempty"`
|
||||
Checksum *Checksum `json:"checksum_info,omitempty"`
|
||||
ErrorMessage *rest.LocalizableMessage `json:"error_message,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
SourceEndpoint *TransferEndpoint `json:"source_endpoint,omitempty"`
|
||||
SourceType string `json:"source_type"`
|
||||
Status string `json:"status,omitempty"`
|
||||
UploadEndpoint *TransferEndpoint `json:"upload_endpoint,omitempty"`
|
||||
}
|
||||
|
||||
// AddLibraryItemFile adds a file
|
||||
func (c *Manager) AddLibraryItemFile(ctx context.Context, sessionID string, updateFile UpdateFile) (*UpdateFile, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("add")
|
||||
spec := struct {
|
||||
FileSpec UpdateFile `json:"file_spec"`
|
||||
}{updateFile}
|
||||
var res UpdateFile
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Status == "ERROR" {
|
||||
return nil, res.ErrorMessage
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// AddLibraryItemFileFromURI adds a file from a remote URI.
|
||||
func (c *Manager) AddLibraryItemFileFromURI(
|
||||
ctx context.Context,
|
||||
sessionID, fileName, uri string) (*UpdateFile, error) {
|
||||
|
||||
n, fingerprint, err := c.getContentLengthAndFingerprint(ctx, uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info, err := c.AddLibraryItemFile(ctx, sessionID, UpdateFile{
|
||||
Name: fileName,
|
||||
SourceType: "PULL",
|
||||
Size: n,
|
||||
SourceEndpoint: &TransferEndpoint{
|
||||
URI: uri,
|
||||
SSLCertificateThumbprint: fingerprint,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return info, c.CompleteLibraryItemUpdateSession(ctx, sessionID)
|
||||
}
|
||||
|
||||
// GetLibraryItemUpdateSessionFile retrieves information about a specific file
|
||||
// that is a part of an update session.
|
||||
func (c *Manager) GetLibraryItemUpdateSessionFile(ctx context.Context, sessionID string, fileName string) (*UpdateFile, error) {
|
||||
url := c.Resource(internal.LibraryItemUpdateSessionFile).WithID(sessionID).WithAction("get")
|
||||
spec := struct {
|
||||
Name string `json:"file_name"`
|
||||
}{fileName}
|
||||
var res UpdateFile
|
||||
return &res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// getContentLengthAndFingerprint gets the number of bytes returned
|
||||
// by the URI as well as the SHA1 fingerprint of the peer certificate
|
||||
// if the URI's scheme is https.
|
||||
func (c *Manager) getContentLengthAndFingerprint(
|
||||
ctx context.Context, uri string) (int64, string, error) {
|
||||
resp, err := c.Head(uri)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
if resp.TLS == nil || len(resp.TLS.PeerCertificates) == 0 {
|
||||
return resp.ContentLength, "", nil
|
||||
}
|
||||
fingerprint := c.Thumbprint(resp.Request.URL.Host)
|
||||
if fingerprint == "" {
|
||||
if c.Transport.(*http.Transport).TLSClientConfig.InsecureSkipVerify {
|
||||
fingerprint = soap.ThumbprintSHA1(resp.TLS.PeerCertificates[0])
|
||||
}
|
||||
}
|
||||
return resp.ContentLength, fingerprint, nil
|
||||
}
|
||||
|
||||
// ReadManifest converts an ovf manifest to a map of file name -> Checksum.
|
||||
func ReadManifest(m io.Reader) (map[string]*Checksum, error) {
|
||||
// expected format: openssl sha1 *.{ovf,vmdk}
|
||||
c := make(map[string]*Checksum)
|
||||
|
||||
scanner := bufio.NewScanner(m)
|
||||
for scanner.Scan() {
|
||||
line := strings.SplitN(scanner.Text(), ")=", 2)
|
||||
if len(line) != 2 {
|
||||
continue
|
||||
}
|
||||
name := strings.SplitN(line[0], "(", 2)
|
||||
if len(name) != 2 {
|
||||
continue
|
||||
}
|
||||
sum := &Checksum{
|
||||
Algorithm: strings.TrimSpace(name[0]),
|
||||
Checksum: strings.TrimSpace(line[1]),
|
||||
}
|
||||
c[name[1]] = sum
|
||||
}
|
||||
|
||||
return c, scanner.Err()
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 rest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
)
|
||||
|
||||
// Client extends soap.Client to support JSON encoding, while inheriting security features, debug tracing and session persistence.
|
||||
type Client struct {
|
||||
*soap.Client
|
||||
}
|
||||
|
||||
// Session information
|
||||
type Session struct {
|
||||
User string `json:"user"`
|
||||
Created time.Time `json:"created_time"`
|
||||
LastAccessed time.Time `json:"last_accessed_time"`
|
||||
}
|
||||
|
||||
// LocalizableMessage represents a localizable error
|
||||
type LocalizableMessage struct {
|
||||
Args []string `json:"args,omitempty"`
|
||||
DefaultMessage string `json:"default_message,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
func (m *LocalizableMessage) Error() string {
|
||||
return m.DefaultMessage
|
||||
}
|
||||
|
||||
// NewClient creates a new Client instance.
|
||||
func NewClient(c *vim25.Client) *Client {
|
||||
sc := c.Client.NewServiceClient(Path, "")
|
||||
|
||||
return &Client{sc}
|
||||
}
|
||||
|
||||
// Resource helper for the given path.
|
||||
func (c *Client) Resource(path string) *Resource {
|
||||
r := &Resource{u: c.URL()}
|
||||
r.u.Path = Path + path
|
||||
return r
|
||||
}
|
||||
|
||||
type Signer interface {
|
||||
SignRequest(*http.Request) error
|
||||
}
|
||||
|
||||
type signerContext struct{}
|
||||
|
||||
func (c *Client) WithSigner(ctx context.Context, s Signer) context.Context {
|
||||
return context.WithValue(ctx, signerContext{}, s)
|
||||
}
|
||||
|
||||
type statusError struct {
|
||||
res *http.Response
|
||||
}
|
||||
|
||||
func (e *statusError) Error() string {
|
||||
return fmt.Sprintf("%s %s: %s", e.res.Request.Method, e.res.Request.URL, e.res.Status)
|
||||
}
|
||||
|
||||
// Do sends the http.Request, decoding resBody if provided.
|
||||
func (c *Client) Do(ctx context.Context, req *http.Request, resBody interface{}) error {
|
||||
switch req.Method {
|
||||
case http.MethodPost, http.MethodPatch:
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
if s, ok := ctx.Value(signerContext{}).(Signer); ok {
|
||||
if err := s.SignRequest(req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return c.Client.Do(ctx, req, func(res *http.Response) error {
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
case http.StatusBadRequest:
|
||||
// TODO: structured error types
|
||||
detail, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("%s: %s", res.Status, bytes.TrimSpace(detail))
|
||||
default:
|
||||
return &statusError{res}
|
||||
}
|
||||
|
||||
if resBody == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch b := resBody.(type) {
|
||||
case io.Writer:
|
||||
_, err := io.Copy(b, res.Body)
|
||||
return err
|
||||
default:
|
||||
val := struct {
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
}{
|
||||
resBody,
|
||||
}
|
||||
return json.NewDecoder(res.Body).Decode(&val)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Login creates a new session via Basic Authentication with the given url.Userinfo.
|
||||
func (c *Client) Login(ctx context.Context, user *url.Userinfo) error {
|
||||
req := c.Resource(internal.SessionPath).Request(http.MethodPost)
|
||||
|
||||
if user != nil {
|
||||
if password, ok := user.Password(); ok {
|
||||
req.SetBasicAuth(user.Username(), password)
|
||||
}
|
||||
}
|
||||
|
||||
return c.Do(ctx, req, nil)
|
||||
}
|
||||
|
||||
func (c *Client) LoginByToken(ctx context.Context) error {
|
||||
return c.Login(ctx, nil)
|
||||
}
|
||||
|
||||
// Session returns the user's current session.
|
||||
// Nil is returned if the session is not authenticated.
|
||||
func (c *Client) Session(ctx context.Context) (*Session, error) {
|
||||
var s Session
|
||||
req := c.Resource(internal.SessionPath).WithAction("get").Request(http.MethodPost)
|
||||
err := c.Do(ctx, req, &s)
|
||||
if err != nil {
|
||||
if e, ok := err.(*statusError); ok {
|
||||
if e.res.StatusCode == http.StatusUnauthorized {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
// Logout deletes the current session.
|
||||
func (c *Client) Logout(ctx context.Context) error {
|
||||
req := c.Resource(internal.SessionPath).Request(http.MethodDelete)
|
||||
return c.Do(ctx, req, nil)
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Copyright (c) 2019 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 rest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const (
|
||||
Path = "/rest"
|
||||
)
|
||||
|
||||
// Resource wraps url.URL with helpers
|
||||
type Resource struct {
|
||||
u *url.URL
|
||||
}
|
||||
|
||||
func (r *Resource) String() string {
|
||||
return r.u.String()
|
||||
}
|
||||
|
||||
// WithID appends id to the URL.Path
|
||||
func (r *Resource) WithID(id string) *Resource {
|
||||
r.u.Path += "/id:" + id
|
||||
return r
|
||||
}
|
||||
|
||||
// WithAction sets adds action to the URL.RawQuery
|
||||
func (r *Resource) WithAction(action string) *Resource {
|
||||
return r.WithParam("~action", action)
|
||||
}
|
||||
|
||||
// WithParam sets adds a parameter to the URL.RawQuery
|
||||
func (r *Resource) WithParam(name string, value string) *Resource {
|
||||
r.u.RawQuery = url.Values{
|
||||
name: []string{value},
|
||||
}.Encode()
|
||||
return r
|
||||
}
|
||||
|
||||
// Request returns a new http.Request for the given method.
|
||||
// An optional body can be provided for POST and PATCH methods.
|
||||
func (r *Resource) Request(method string, body ...interface{}) *http.Request {
|
||||
rdr := io.MultiReader() // empty body by default
|
||||
if len(body) != 0 {
|
||||
rdr = encode(body[0])
|
||||
}
|
||||
req, err := http.NewRequest(method, r.u.String(), rdr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
||||
type errorReader struct {
|
||||
e error
|
||||
}
|
||||
|
||||
func (e errorReader) Read([]byte) (int, error) {
|
||||
return -1, e.e
|
||||
}
|
||||
|
||||
// encode body as JSON, deferring any errors until io.Reader is used.
|
||||
func encode(body interface{}) io.Reader {
|
||||
var b bytes.Buffer
|
||||
err := json.NewEncoder(&b).Encode(body)
|
||||
if err != nil {
|
||||
return errorReader{err}
|
||||
}
|
||||
return &b
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
Copyright (c) 2018 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 vcenter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/rest"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// AdditionalParams are additional OVF parameters which can be specified for a deployment target.
|
||||
// This structure is a union where based on Type, only one of each commented section will be set.
|
||||
type AdditionalParams struct {
|
||||
Class string `json:"@class"`
|
||||
Type string `json:"type"`
|
||||
|
||||
// DeploymentOptionParams
|
||||
SelectedKey string `json:"selected_key,omitempty"`
|
||||
DeploymentOptions []DeploymentOption `json:"deployment_options,omitempty"`
|
||||
|
||||
// ExtraConfigs
|
||||
ExtraConfig []ExtraConfig `json:"extra_configs,omitempty"`
|
||||
|
||||
// PropertyParams
|
||||
Properties []Property `json:"properties,omitempty"`
|
||||
|
||||
// SizeParams
|
||||
ApproximateSparseDeploymentSize int64 `json:"approximate_sparse_deployment_size,omitempty"`
|
||||
VariableDiskSize bool `json:"variable_disk_size,omitempty"`
|
||||
ApproximateDownloadSize int64 `json:"approximate_download_size,omitempty"`
|
||||
ApproximateFlatDeploymentSize int64 `json:"approximate_flat_deployment_size,omitempty"`
|
||||
|
||||
// IpAllocationParams
|
||||
SupportedAllocationScheme []string `json:"supported_allocation_scheme,omitempty"`
|
||||
SupportedIPProtocol []string `json:"supported_ip_protocol,omitempty"`
|
||||
SupportedIPAllocationPolicy []string `json:"supported_ip_allocation_policy,omitempty"`
|
||||
IPAllocationPolicy string `json:"ip_allocation_policy,omitempty"`
|
||||
IPProtocol string `json:"ip_protocol,omitempty"`
|
||||
|
||||
// UnknownSections
|
||||
UnknownSections []UnknownSection `json:"unknown_sections,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
ClassOvfParams = "com.vmware.vcenter.ovf.ovf_params"
|
||||
ClassPropertyParams = "com.vmware.vcenter.ovf.property_params"
|
||||
TypeDeploymentOptionParams = "DeploymentOptionParams"
|
||||
TypeExtraConfigParams = "ExtraConfigParams"
|
||||
TypeExtraConfigs = "ExtraConfigs"
|
||||
TypeIPAllocationParams = "IpAllocationParams"
|
||||
TypePropertyParams = "PropertyParams"
|
||||
TypeSizeParams = "SizeParams"
|
||||
)
|
||||
|
||||
// DeploymentOption contains the information about a deployment option as defined in the OVF specification
|
||||
type DeploymentOption struct {
|
||||
Key string `json:"key,omitempty"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
DefaultChoice bool `json:"default_choice,omitempty"`
|
||||
}
|
||||
|
||||
// ExtraConfig contains information about a vmw:ExtraConfig OVF element
|
||||
type ExtraConfig struct {
|
||||
Key string `json:"key,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
VirtualSystemID string `json:"virtual_system_id,omitempty"`
|
||||
}
|
||||
|
||||
// Property contains information about a property in an OVF package
|
||||
type Property struct {
|
||||
Category string `json:"category,omitempty"`
|
||||
ClassID string `json:"class_id,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
InstanceID string `json:"instance_id,omitempty"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
UIOptional bool `json:"ui_optional,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// UnknownSection contains information about an unknown section in an OVF package
|
||||
type UnknownSection struct {
|
||||
Tag string `json:"tag,omitempty"`
|
||||
Info string `json:"info,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkMapping specifies the target network to use for sections of type ovf:NetworkSection in the OVF descriptor
|
||||
type NetworkMapping struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// StorageGroupMapping defines the storage deployment target and storage provisioning type for a section of type vmw:StorageGroupSection in the OVF descriptor
|
||||
type StorageGroupMapping struct {
|
||||
Type string `json:"type"`
|
||||
StorageProfileID string `json:"storage_profile_id,omitempty"`
|
||||
DatastoreID string `json:"datastore_id,omitempty"`
|
||||
Provisioning string `json:"provisioning,omitempty"`
|
||||
}
|
||||
|
||||
// StorageMapping specifies the target storage to use for sections of type vmw:StorageGroupSection in the OVF descriptor
|
||||
type StorageMapping struct {
|
||||
Key string `json:"key"`
|
||||
Value StorageGroupMapping `json:"value"`
|
||||
}
|
||||
|
||||
// DeploymentSpec is the deployment specification for the deployment
|
||||
type DeploymentSpec struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Annotation string `json:"annotation,omitempty"`
|
||||
AcceptAllEULA bool `json:"accept_all_EULA,omitempty"`
|
||||
NetworkMappings []NetworkMapping `json:"network_mappings,omitempty"`
|
||||
StorageMappings []StorageMapping `json:"storage_mappings,omitempty"`
|
||||
StorageProvisioning string `json:"storage_provisioning,omitempty"`
|
||||
StorageProfileID string `json:"storage_profile_id,omitempty"`
|
||||
Locale string `json:"locale,omitempty"`
|
||||
Flags []string `json:"flags,omitempty"`
|
||||
AdditionalParams []AdditionalParams `json:"additional_parameters,omitempty"`
|
||||
DefaultDatastoreID string `json:"default_datastore_id,omitempty"`
|
||||
}
|
||||
|
||||
// Target is the target for the deployment
|
||||
type Target struct {
|
||||
ResourcePoolID string `json:"resource_pool_id,omitempty"`
|
||||
HostID string `json:"host_id,omitempty"`
|
||||
FolderID string `json:"folder_id,omitempty"`
|
||||
}
|
||||
|
||||
// Deploy contains the information to start the deployment of a library OVF
|
||||
type Deploy struct {
|
||||
DeploymentSpec `json:"deployment_spec,omitempty"`
|
||||
Target `json:"target,omitempty"`
|
||||
}
|
||||
|
||||
// Error is a SERVER error
|
||||
type Error struct {
|
||||
Class string `json:"@class,omitempty"`
|
||||
Messages []rest.LocalizableMessage `json:"messages,omitempty"`
|
||||
}
|
||||
|
||||
// ParseIssue is a parse issue struct
|
||||
type ParseIssue struct {
|
||||
Category string `json:"@classcategory,omitempty"`
|
||||
File string `json:"file,omitempty"`
|
||||
LineNumber int64 `json:"line_number,omitempty"`
|
||||
ColumnNumber int64 `json:"column_number,omitempty"`
|
||||
Message rest.LocalizableMessage `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// OVFError is a list of errors from create or deploy
|
||||
type OVFError struct {
|
||||
Category string `json:"category,omitempty"`
|
||||
Error *Error `json:"error,omitempty"`
|
||||
Issues []ParseIssue `json:"issues,omitempty"`
|
||||
Message *rest.LocalizableMessage `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// ResourceID is a managed object reference for a deployed resource.
|
||||
type ResourceID struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Value string `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// DeploymentError is an error that occurs when deploying and OVF from
|
||||
// a library item.
|
||||
type DeploymentError struct {
|
||||
Errors []OVFError `json:"errors,omitempty"`
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (e *DeploymentError) Error() string {
|
||||
msg := ""
|
||||
if len(e.Errors) != 0 {
|
||||
err := e.Errors[0]
|
||||
if err.Message != nil {
|
||||
msg = err.Message.DefaultMessage
|
||||
} else if err.Error != nil && len(err.Error.Messages) != 0 {
|
||||
msg = err.Error.Messages[0].DefaultMessage
|
||||
}
|
||||
}
|
||||
if msg == "" {
|
||||
msg = fmt.Sprintf("%#v", e)
|
||||
}
|
||||
return "deploy error: " + msg
|
||||
}
|
||||
|
||||
// Deployment is the results from issuing a library OVF deployment
|
||||
type Deployment struct {
|
||||
Succeeded bool `json:"succeeded,omitempty"`
|
||||
ResourceID *ResourceID `json:"resource_id,omitempty"`
|
||||
Error *DeploymentError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// FilterRequest contains the information to start a vcenter filter call
|
||||
type FilterRequest struct {
|
||||
Target `json:"target,omitempty"`
|
||||
}
|
||||
|
||||
// FilterResponse returns information from the vcenter filter call
|
||||
type FilterResponse struct {
|
||||
EULAs []string `json:"EULAs,omitempty"`
|
||||
AdditionalParams []AdditionalParams `json:"additional_params,omitempty"`
|
||||
Annotation string `json:"Annotation,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Networks []string `json:"Networks,omitempty"`
|
||||
StorageGroups []string `json:"storage_groups,omitempty"`
|
||||
}
|
||||
|
||||
// Manager extends rest.Client, adding content library related methods.
|
||||
type Manager struct {
|
||||
*rest.Client
|
||||
}
|
||||
|
||||
// NewManager creates a new Manager instance with the given client.
|
||||
func NewManager(client *rest.Client) *Manager {
|
||||
return &Manager{
|
||||
Client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// DeployLibraryItem deploys a library OVF
|
||||
func (c *Manager) DeployLibraryItem(ctx context.Context, libraryItemID string, deploy Deploy) (*types.ManagedObjectReference, error) {
|
||||
url := c.Resource(internal.VCenterOVFLibraryItem).WithID(libraryItemID).WithAction("deploy")
|
||||
var res Deployment
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, deploy), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if res.Succeeded {
|
||||
ref := types.ManagedObjectReference(*res.ResourceID)
|
||||
return &ref, nil
|
||||
}
|
||||
return nil, res.Error
|
||||
}
|
||||
|
||||
// FilterLibraryItem deploys a library OVF
|
||||
func (c *Manager) FilterLibraryItem(ctx context.Context, libraryItemID string, filter FilterRequest) (FilterResponse, error) {
|
||||
url := c.Resource(internal.VCenterOVFLibraryItem).WithID(libraryItemID).WithAction("filter")
|
||||
var res FilterResponse
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, filter), &res)
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
Copyright (c) 2019 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 vcenter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/vmware/govmomi/vapi/internal"
|
||||
"github.com/vmware/govmomi/vapi/library"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
// vcenter vm template
|
||||
// The vcenter.vm_template API provides structures and services that will let its client manage VMTX template in Content Library.
|
||||
// http://vmware.github.io/vsphere-automation-sdk-rest/6.7.1/index.html#SVC_com.vmware.vcenter.vm_template.library_items
|
||||
|
||||
// Template create spec
|
||||
type Template struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
DiskStorage *DiskStorage `json:"disk_storage,omitempty"`
|
||||
DiskStorageOverrides []DiskStorageOverride `json:"disk_storage_overrides,omitempty"`
|
||||
Library string `json:"library,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Placement *Placement `json:"placement,omitempty"`
|
||||
SourceVM string `json:"source_vm,omitempty"`
|
||||
VMHomeStorage *DiskStorage `json:"vm_home_storage,omitempty"`
|
||||
}
|
||||
|
||||
// TemplateInfo for a VM template contained in an existing library item
|
||||
type TemplateInfo struct {
|
||||
GuestOS string `json:"guest_OS,omitempty"`
|
||||
// TODO...
|
||||
}
|
||||
|
||||
// Placement information used to place the virtual machine template
|
||||
type Placement struct {
|
||||
ResourcePool string `json:"resource_pool,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
Folder string `json:"folder,omitempty"`
|
||||
Cluster string `json:"cluster,omitempty"`
|
||||
}
|
||||
|
||||
// StoragePolicy for DiskStorage
|
||||
type StoragePolicy struct {
|
||||
Policy string `json:"policy,omitempty"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// DiskStorage defines the storage specification for VM files
|
||||
type DiskStorage struct {
|
||||
Datastore string `json:"datastore,omitempty"`
|
||||
StoragePolicy *StoragePolicy `json:"storage_policy,omitempty"`
|
||||
}
|
||||
|
||||
// DiskStorageOverride storage specification for individual disks in the virtual machine template
|
||||
type DiskStorageOverride struct {
|
||||
Key string `json:"key"`
|
||||
Value DiskStorage `json:"value"`
|
||||
}
|
||||
|
||||
// GuestCustomization spec to apply to the deployed VM
|
||||
type GuestCustomization struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// HardwareCustomization spec which specifies updates to the deployed VM
|
||||
type HardwareCustomization struct {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// DeployTemplate specification of how a library VM template clone should be deployed.
|
||||
type DeployTemplate struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
DiskStorage *DiskStorage `json:"disk_storage,omitempty"`
|
||||
DiskStorageOverrides []DiskStorageOverride `json:"disk_storage_overrides,omitempty"`
|
||||
GuestCustomization *GuestCustomization `json:"guest_customization,omitempty"`
|
||||
HardwareCustomization *HardwareCustomization `json:"hardware_customization,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Placement *Placement `json:"placement,omitempty"`
|
||||
PoweredOn bool `json:"powered_on"`
|
||||
VMHomeStorage *DiskStorage `json:"vm_home_storage,omitempty"`
|
||||
}
|
||||
|
||||
// CheckOut specification
|
||||
type CheckOut struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Placement *Placement `json:"placement,omitempty"`
|
||||
PoweredOn bool `json:"powered_on,omitempty"`
|
||||
}
|
||||
|
||||
// CheckIn specification
|
||||
type CheckIn struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// CreateTemplate creates a library item in content library from an existing VM
|
||||
func (c *Manager) CreateTemplate(ctx context.Context, vmtx Template) (string, error) {
|
||||
url := c.Resource(internal.VCenterVMTXLibraryItem)
|
||||
var res string
|
||||
spec := struct {
|
||||
Template `json:"spec"`
|
||||
}{vmtx}
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// DeployTemplateLibraryItem deploys a VM as a copy of the source VM template contained in the given library item
|
||||
func (c *Manager) DeployTemplateLibraryItem(ctx context.Context, libraryItemID string, deploy DeployTemplate) (*types.ManagedObjectReference, error) {
|
||||
url := c.Resource(path.Join(internal.VCenterVMTXLibraryItem, libraryItemID)).WithParam("action", "deploy")
|
||||
var res string
|
||||
spec := struct {
|
||||
DeployTemplate `json:"spec"`
|
||||
}{deploy}
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.ManagedObjectReference{Type: "VirtualMachine", Value: res}, nil
|
||||
}
|
||||
|
||||
// CheckOut a library item containing a VM template.
|
||||
func (c *Manager) CheckOut(ctx context.Context, libraryItemID string, checkout *CheckOut) (*types.ManagedObjectReference, error) {
|
||||
url := c.Resource(path.Join(internal.VCenterVMTXLibraryItem, libraryItemID, "check-outs")).WithParam("action", "check-out")
|
||||
var res string
|
||||
spec := struct {
|
||||
*CheckOut `json:"spec"`
|
||||
}{checkout}
|
||||
err := c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.ManagedObjectReference{Type: "VirtualMachine", Value: res}, nil
|
||||
}
|
||||
|
||||
// CheckIn a VM into the library item.
|
||||
func (c *Manager) CheckIn(ctx context.Context, libraryItemID string, vm mo.Reference, checkin *CheckIn) (string, error) {
|
||||
p := path.Join(internal.VCenterVMTXLibraryItem, libraryItemID, "check-outs", vm.Reference().Value)
|
||||
url := c.Resource(p).WithParam("action", "check-in")
|
||||
var res string
|
||||
spec := struct {
|
||||
*CheckIn `json:"spec"`
|
||||
}{checkin}
|
||||
return res, c.Do(ctx, url.Request(http.MethodPost, spec), &res)
|
||||
}
|
||||
|
||||
// TemplateLibrary params for synchronizing subscription library OVF items to VM Template items
|
||||
type TemplateLibrary struct {
|
||||
Source library.Library
|
||||
Destination library.Library
|
||||
Placement Target
|
||||
Include func(library.Item, *library.Item) bool
|
||||
SyncItem func(context.Context, library.Item, *Deploy, *Template) error
|
||||
}
|
||||
|
||||
func (c *Manager) includeTemplateLibraryItem(src library.Item, dst *library.Item) bool {
|
||||
return dst == nil
|
||||
}
|
||||
|
||||
// SyncTemplateLibraryItem deploys an Library OVF item from which a VM template (vmtx) Library item is created.
|
||||
// The deployed VM is deleted after being converted to a Library vmtx item.
|
||||
func (c *Manager) SyncTemplateLibraryItem(ctx context.Context, item library.Item, deploy *Deploy, spec *Template) error {
|
||||
destroy := false
|
||||
if spec.SourceVM == "" {
|
||||
ref, err := c.DeployLibraryItem(ctx, item.ID, *deploy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destroy = true
|
||||
spec.SourceVM = ref.Value
|
||||
}
|
||||
|
||||
_, err := c.CreateTemplate(ctx, *spec)
|
||||
|
||||
if destroy {
|
||||
// Delete source VM regardless of CreateTemplate result
|
||||
url := c.Resource("/vcenter/vm/" + spec.SourceVM)
|
||||
derr := c.Do(ctx, url.Request(http.MethodDelete), nil)
|
||||
if derr != nil {
|
||||
if err == nil {
|
||||
// Return Delete error if CreateTemplate was successful
|
||||
return derr
|
||||
}
|
||||
// Return CreateTemplate error and just log Delete error
|
||||
log.Printf("destroy %s: %s", spec.SourceVM, derr)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func vmtxSourceName(l library.Library, item library.Item) string {
|
||||
sum := sha1.Sum([]byte(path.Join(l.Name, item.Name)))
|
||||
return fmt.Sprintf("vmtx-src-%x", sum)
|
||||
}
|
||||
|
||||
// SyncTemplateLibrary converts TemplateLibrary.Source OVF items to VM Template items within TemplateLibrary.Destination
|
||||
// The optional TemplateLibrary.Include func can be used to filter which items are synced.
|
||||
// By default all items that don't exist in the Destination library are synced.
|
||||
// The optional TemplateLibrary.SyncItem func can be used to change how the item is synced, by default SyncTemplateLibraryItem is used.
|
||||
func (c *Manager) SyncTemplateLibrary(ctx context.Context, l TemplateLibrary, items ...library.Item) error {
|
||||
m := library.NewManager(c.Client)
|
||||
var err error
|
||||
if len(items) == 0 {
|
||||
items, err = m.GetLibraryItems(ctx, l.Source.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
templates, err := m.GetLibraryItems(ctx, l.Destination.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existing := make(map[string]*library.Item)
|
||||
for i := range templates {
|
||||
existing[templates[i].Name] = &templates[i]
|
||||
}
|
||||
|
||||
include := l.Include
|
||||
if include == nil {
|
||||
include = c.includeTemplateLibraryItem
|
||||
}
|
||||
|
||||
sync := l.SyncItem
|
||||
if sync == nil {
|
||||
sync = c.SyncTemplateLibraryItem
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
if item.Type != library.ItemTypeOVF {
|
||||
continue
|
||||
}
|
||||
|
||||
// Deploy source VM from library ovf item
|
||||
deploy := Deploy{
|
||||
DeploymentSpec: DeploymentSpec{
|
||||
Name: vmtxSourceName(l.Destination, item),
|
||||
DefaultDatastoreID: l.Destination.Storage[0].DatastoreID,
|
||||
AcceptAllEULA: true,
|
||||
},
|
||||
Target: l.Placement,
|
||||
}
|
||||
|
||||
// Create library vmtx item from source VM
|
||||
storage := &DiskStorage{
|
||||
Datastore: deploy.DeploymentSpec.DefaultDatastoreID,
|
||||
}
|
||||
spec := Template{
|
||||
Name: item.Name,
|
||||
Library: l.Destination.ID,
|
||||
DiskStorage: storage,
|
||||
VMHomeStorage: storage,
|
||||
Placement: &Placement{
|
||||
Folder: deploy.Target.FolderID,
|
||||
ResourcePool: deploy.Target.ResourcePoolID,
|
||||
},
|
||||
}
|
||||
|
||||
if !l.Include(item, existing[item.Name]) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err = sync(ctx, item, &deploy, &spec); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -580,6 +580,10 @@ github.com/vmware/govmomi/ovf
|
|||
github.com/vmware/govmomi/property
|
||||
github.com/vmware/govmomi/session
|
||||
github.com/vmware/govmomi/task
|
||||
github.com/vmware/govmomi/vapi/internal
|
||||
github.com/vmware/govmomi/vapi/library
|
||||
github.com/vmware/govmomi/vapi/rest
|
||||
github.com/vmware/govmomi/vapi/vcenter
|
||||
github.com/vmware/govmomi/vim25
|
||||
github.com/vmware/govmomi/vim25/debug
|
||||
github.com/vmware/govmomi/vim25/methods
|
||||
|
|
|
@ -171,6 +171,35 @@ can be done via environment variable:
|
|||
|
||||
@include 'builder/vsphere/common/OutputConfig-not-required.mdx'
|
||||
|
||||
### Content Library Import Configuration
|
||||
|
||||
@include 'builder/vsphere/common/ContentLibraryDestinationConfig.mdx'
|
||||
|
||||
@include 'builder/vsphere/common/ContentLibraryDestinationConfig-not-required.mdx'
|
||||
|
||||
Minimal example of usage:
|
||||
|
||||
<Tabs>
|
||||
<Tab heading="JSON">
|
||||
|
||||
```json
|
||||
"content_library_destination" : {
|
||||
"library": "Packer Library Test"
|
||||
}
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab heading="HCL2">
|
||||
|
||||
```hcl
|
||||
content_library_destination {
|
||||
library = "Packer Library Test"
|
||||
}
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Working with Clusters
|
||||
|
||||
#### Standalone Hosts
|
||||
|
|
|
@ -174,6 +174,35 @@ from the datastore. Example:
|
|||
|
||||
@include 'builder/vsphere/common/OutputConfig-not-required.mdx'
|
||||
|
||||
### Content Library Import Configuration
|
||||
|
||||
@include 'builder/vsphere/common/ContentLibraryDestinationConfig.mdx'
|
||||
|
||||
@include 'builder/vsphere/common/ContentLibraryDestinationConfig-not-required.mdx'
|
||||
|
||||
Minimal example of usage:
|
||||
|
||||
<Tabs>
|
||||
<Tab heading="JSON">
|
||||
|
||||
```json
|
||||
"content_library_destination" : {
|
||||
"library": "Packer Library Test"
|
||||
}
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab heading="HCL2">
|
||||
|
||||
```hcl
|
||||
content_library_destination {
|
||||
library = "Packer Library Test"
|
||||
}
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Extra Configuration Parameters
|
||||
|
||||
@include 'builder/vsphere/common/ConfigParamsConfig-not-required.mdx'
|
||||
|
|
|
@ -5,4 +5,10 @@
|
|||
|
||||
- `convert_to_template` (bool) - Convert VM to a template. Defaults to `false`.
|
||||
|
||||
- `export` (\*common.ExportConfig) - Export
|
||||
- `export` (\*common.ExportConfig) - Configuration for exporting VM to an ovf file.
|
||||
The VM will not be exported if no [Export Configuration](#export-configuration) is specified.
|
||||
|
||||
- `content_library_destination` (\*common.ContentLibraryDestinationConfig) - 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.
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<!-- Code generated from the comments of the ContentLibraryDestinationConfig struct in builder/vsphere/common/step_import_to_content_library.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `library` (string) - Name of the library in which the new library item containing the VM template should be created.
|
||||
The Content Library should be of type Local to allow deploying virtual machines.
|
||||
|
||||
- `name` (string) - Name of the library item that will be created. The name of the item should be different from [vm_name](#vm_name).
|
||||
Defaults to [vm_name](#vm_name) + timestamp.
|
||||
|
||||
- `description` (string) - Description of the library item that will be created. Defaults to "Packer imported [vm_name](#vm_name) VM template".
|
||||
|
||||
- `cluster` (string) - 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.
|
||||
Defaults to [cluster](#cluster).
|
||||
|
||||
- `folder` (string) - Virtual machine folder into which the virtual machine template should be placed.
|
||||
Defaults to the same folder as the source virtual machine.
|
||||
|
||||
- `host` (string) - 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.
|
||||
Defaults to [host](#host).
|
||||
|
||||
- `resource_pool` (string) - 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.
|
||||
|
||||
- `datastore` (string) - The datastore for the virtual machine template's configuration and log files.
|
||||
Defaults to the storage backing associated with the library specified by library.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<!-- Code generated from the comments of the ContentLibraryDestinationConfig struct in builder/vsphere/common/step_import_to_content_library.go; DO NOT EDIT MANUALLY -->
|
||||
With this configuration Packer creates a library item in a content library whose content is a virtual machine template created from the just built VM.
|
||||
The virtual machine template is stored in a newly created library item.
|
|
@ -7,4 +7,8 @@
|
|||
|
||||
- `export` (\*common.ExportConfig) - Configuration for exporting VM to an ovf file.
|
||||
The VM will not be exported if no [Export Configuration](#export-configuration) is specified.
|
||||
|
||||
- `content_library_destination` (\*common.ContentLibraryDestinationConfig) - 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.
|
||||
|
Loading…
Reference in New Issue