diff --git a/builder/xenserver/common/common_config.go b/builder/xenserver/common/common_config.go index c49c544..1be0a8d 100644 --- a/builder/xenserver/common/common_config.go +++ b/builder/xenserver/common/common_config.go @@ -22,6 +22,7 @@ type CommonConfig struct { VMDescription string `mapstructure:"vm_description"` SrName string `mapstructure:"sr_name"` SrISOName string `mapstructure:"sr_iso_name" required:"false"` + CDFiles []string `mapstructure:"cd_files"` FloppyFiles []string `mapstructure:"floppy_files"` NetworkNames []string `mapstructure:"network_names"` ExportNetworkNames []string `mapstructure:"export_network_names"` diff --git a/builder/xenserver/common/config.hcl2spec.go b/builder/xenserver/common/config.hcl2spec.go index 80d8ce8..ea3c16d 100644 --- a/builder/xenserver/common/config.hcl2spec.go +++ b/builder/xenserver/common/config.hcl2spec.go @@ -26,6 +26,7 @@ type FlatConfig struct { VMDescription *string `mapstructure:"vm_description" cty:"vm_description" hcl:"vm_description"` SrName *string `mapstructure:"sr_name" cty:"sr_name" hcl:"sr_name"` SrISOName *string `mapstructure:"sr_iso_name" required:"false" cty:"sr_iso_name" hcl:"sr_iso_name"` + CDFiles []string `mapstructure:"cd_files" cty:"cd_files" hcl:"cd_files"` FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"` NetworkNames []string `mapstructure:"network_names" cty:"network_names" hcl:"network_names"` ExportNetworkNames []string `mapstructure:"export_network_names" cty:"export_network_names" hcl:"export_network_names"` @@ -142,6 +143,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "vm_description": &hcldec.AttrSpec{Name: "vm_description", Type: cty.String, Required: false}, "sr_name": &hcldec.AttrSpec{Name: "sr_name", Type: cty.String, Required: false}, "sr_iso_name": &hcldec.AttrSpec{Name: "sr_iso_name", Type: cty.String, Required: false}, + "cd_files": &hcldec.AttrSpec{Name: "cd_files", Type: cty.List(cty.String), Required: false}, "floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false}, "network_names": &hcldec.AttrSpec{Name: "network_names", Type: cty.List(cty.String), Required: false}, "export_network_names": &hcldec.AttrSpec{Name: "export_network_names", Type: cty.List(cty.String), Required: false}, diff --git a/builder/xenserver/iso/builder.go b/builder/xenserver/iso/builder.go index 725ebf3..29c07bf 100644 --- a/builder/xenserver/iso/builder.go +++ b/builder/xenserver/iso/builder.go @@ -195,6 +195,10 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p Force: self.config.PackerForce, Path: self.config.OutputDir, }, + &commonsteps.StepCreateCD{ + Files: self.config.CDFiles, + Label: "cidata", + }, &commonsteps.StepCreateFloppy{ Files: self.config.FloppyFiles, Label: "cidata", @@ -202,6 +206,18 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p &xscommon.StepHTTPServer{ Chan: httpReqChan, }, + &xscommon.StepUploadVdi{ + VdiNameFunc: func() string { + return "Packer-CD" + }, + ImagePathFunc: func() string { + if cdPath, ok := state.GetOk("cd_path"); ok { + return cdPath.(string) + } + return "" + }, + VdiUuidKey: "cd_vdi_uuid", + }, &xscommon.StepUploadVdi{ VdiNameFunc: func() string { return "Packer-floppy-disk" @@ -258,6 +274,10 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p VdiUuidKey: "tools_vdi_uuid", VdiType: xsclient.VbdTypeCD, }, + &xscommon.StepAttachVdi{ + VdiUuidKey: "cd_vdi_uuid", + VdiType: xsclient.VbdTypeCD, + }, new(xscommon.StepStartVmPaused), new(xscommon.StepSetVmHostSshAddress), // &xscommon.StepForwardPortOverSSH{ @@ -307,6 +327,9 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p &xscommon.StepDetachVdi{ VdiUuidKey: "tools_vdi_uuid", }, + &xscommon.StepDetachVdi{ + VdiUuidKey: "cd_vdi_uuid", + }, &xscommon.StepDetachVdi{ VdiUuidKey: "floppy_vdi_uuid", }, diff --git a/builder/xenserver/xva/builder.go b/builder/xenserver/xva/builder.go index e948413..42439b9 100644 --- a/builder/xenserver/xva/builder.go +++ b/builder/xenserver/xva/builder.go @@ -118,6 +118,10 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p Force: self.config.PackerForce, Path: self.config.OutputDir, }, + &commonsteps.StepCreateCD{ + Files: self.config.CDFiles, + Label: "cidata", + }, &commonsteps.StepCreateFloppy{ Files: self.config.FloppyFiles, Label: "cidata", @@ -125,6 +129,18 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p &xscommon.StepHTTPServer{ Chan: httpReqChan, }, + &xscommon.StepUploadVdi{ + VdiNameFunc: func() string { + return "Packer-CD" + }, + ImagePathFunc: func() string { + if cdPath, ok := state.GetOk("cd_path"); ok { + return cdPath.(string) + } + return "" + }, + VdiUuidKey: "cd_vdi_uuid", + }, &xscommon.StepUploadVdi{ VdiNameFunc: func() string { return "Packer-floppy-disk" @@ -153,6 +169,10 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p VdiUuidKey: "tools_vdi_uuid", VdiType: xsclient.VbdTypeCD, }, + &xscommon.StepAttachVdi{ + VdiUuidKey: "cd_vdi_uuid", + VdiType: xsclient.VbdTypeCD, + }, new(xscommon.StepStartVmPaused), new(xscommon.StepSetVmHostSshAddress), new(xscommon.StepBootWait), @@ -182,6 +202,9 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p &xscommon.StepDetachVdi{ VdiUuidKey: "tools_vdi_uuid", }, + &xscommon.StepDetachVdi{ + VdiUuidKey: "cd_vdi_uuid", + }, &xscommon.StepDetachVdi{ VdiUuidKey: "floppy_vdi_uuid", }, diff --git a/docs/builders/iso/xenserver-iso.html.markdown b/docs/builders/iso/xenserver-iso.html.markdown index 8272d78..51542e8 100644 --- a/docs/builders/iso/xenserver-iso.html.markdown +++ b/docs/builders/iso/xenserver-iso.html.markdown @@ -83,6 +83,16 @@ each category, the available options are alphabetized and described. five seconds and one minute 30 seconds, respectively. If this isn't specified, the default is 10 seconds. +* `cd_files` (array of strings) - A list of files to place onto a CD + that is attached when the VM is booted. This is most useful + for unattended Windows installs, which look for an `Autounattend.xml` file + on removable media. By default, no CD will be attached. All files + listed in this setting get placed into the root directory of the CD + and the CD is attached as the first CD device. Currently, no + support exists for creating sub-directories on the CD. Wildcard + characters (\*, ?, and []) are allowed. Directory names are also allowed, + which will add all the files found in the directory to the CD. + * `clone_template` (string) - The template to clone. Defaults to "Other install media", this is "other", but you can get _dramatic_ performance improvements by setting this to the proper value. To view all available values for this diff --git a/examples/http/ubuntu-2404/meta-data b/examples/http/ubuntu-2404/meta-data new file mode 100644 index 0000000..e69de29 diff --git a/examples/http/ubuntu-2404/user-data b/examples/http/ubuntu-2404/user-data new file mode 100644 index 0000000..324876f --- /dev/null +++ b/examples/http/ubuntu-2404/user-data @@ -0,0 +1,49 @@ +#cloud-config +autoinstall: + version: 1 + identity: + hostname: ubuntu-server + # This is the crypted pass of 'ubuntu' + password: "$6$exDY1mhS4KUYCE/2$zmn9ToZwTKLhCw.b4/b.ZRTIZM30JZ4QrOQ2aOXJ8yk96xpcCof0kxKwuX1kqLG/ygbJ1f8wxED22bTL4F46P0" + username: testuser + ssh: + install-server: true + allow-pw: true + + packages: + - apt-transport-https + - ca-certificates + - curl + - software-properties-common + package_upgrade: false + + # late-commands are run at the very end of the *installer*, before the first + # reboot. However users are actually created by cloud-init after the first + # reboot. So these commands can't affect anything that requires a user to + # actually exist. + late-commands: + # Add xen-guest-agent - this is impossible (!) on Ubuntu 24.04 using + # the normal apt/packages autoinstall configuration, because (a) the + # xen-guest-agent apt repository does not use the "components" field + # (odd, but legal); (b) curtin's apt-config process hard requires + # deb822 .sources to have a "Components:" key, and (c) even if we + # try to configure it with a .list entry rather than .source entry + # under "apt:", on Ubuntu 24 curtin force-converts "deb" lines to + # deb822 format. I wasted days on this. Have to just do it the + # old-fashioned way here. + - 'echo "deb [trusted=yes] https://gitlab.com/api/v4/projects/xen-project%252Fxen-guest-agent/packages/generic/deb-amd64/ release/" > /target/etc/apt/sources.list.d/xen.list' + - curtin in-target --target /target -- apt-get update + - curtin in-target --target /target -- apt-get install -y xen-guest-agent + + # If you want to do anything custom with files from "cd_files", + # uncomment these lines + #- mount /dev/sr1 /mnt + #- cp /mnt/my-cool.conf /target/etc/path/cool.conf + #- umount /mnt + + # User-data is passed to the "real" cloud-init, which runs after the first + # reboot after the installer has finished. So you can run commands which + # modify users here, such as added 'testuser' to a new group. + #user-data: + # runcmd: + # - [ 'usermod', '-a', '-G', 'backup', 'testuser' ] diff --git a/examples/ubuntu/ubuntu-2404.pkr.hcl b/examples/ubuntu/ubuntu-2404.pkr.hcl new file mode 100644 index 0000000..e2e9fd9 --- /dev/null +++ b/examples/ubuntu/ubuntu-2404.pkr.hcl @@ -0,0 +1,125 @@ +packer { + required_plugins { + xenserver= { + version = ">= v0.6.0" + source = "github.com/ddelnano/xenserver" + } + } +} + +# The ubuntu_version value determines what Ubuntu iso URL and sha256 hash we lookup. Updating +# this will allow a new version to be pulled in. +data "null" "ubuntu_version" { + input = "24.04" +} + +locals { + timestamp = regex_replace(timestamp(), "[- TZ:]", "") + ubuntu_version = data.null.ubuntu_version.output + + # Update this map depending on what templates are available on your Xen server. + ubuntu_template_name = { + 24.04 = "Ubuntu Focal Fossa 20.04" + } +} + +# TODO(ddelnano): Update this to use a local once https://github.com/hashicorp/packer/issues/11011 +# is fixed. +data "http" "ubuntu_sha_and_release" { + url = "https://releases.ubuntu.com/${data.null.ubuntu_version.output}/SHA256SUMS" +} + +local "ubuntu_sha256" { + expression = regex("([A-Za-z0-9]+)[\\s\\*]+ubuntu-.*server", data.http.ubuntu_sha_and_release.body) +} + +variable "remote_host" { + type = string + description = "The ip or fqdn of your XenServer. This will be pulled from the env var 'PKR_VAR_remote_host'" + sensitive = true + default = null +} + +variable "remote_password" { + type = string + description = "The password used to interact with your XenServer. This will be pulled from the env var 'PKR_VAR_remote_password'" + sensitive = true + default = null +} + +variable "remote_username" { + type = string + description = "The username used to interact with your XenServer. This will be pulled from the env var 'PKR_VAR_remote_username'" + sensitive = true + default = null +} + +variable "sr_iso_name" { + type = string + description = "The name of the SR packer will use to store the installation ISO" + default = "" +} + +variable "sr_name" { + type = string + description = "The name of the SR packer will use to create the VM" + default = "" +} + +source "xenserver-iso" "ubuntu-2404" { + iso_checksum = "sha256:${local.ubuntu_sha256.0}" + iso_url = "https://releases.ubuntu.com/${local.ubuntu_version}/ubuntu-${local.ubuntu_version}-live-server-amd64.iso" + + sr_name = var.sr_name + sr_iso_name = var.sr_iso_name + remote_host = var.remote_host + remote_password = var.remote_password + remote_username = var.remote_username + + clone_template = local.ubuntu_template_name[data.null.ubuntu_version.output] + vm_name = "ubuntu-${data.null.ubuntu_version.output}-gold" + vm_description = "Built at ${local.timestamp}" + vm_memory = 8192 + disk_size = 10240 + vcpus_max = 4 + vcpus_atstartup = 4 + + boot_command = [ + "c", + "set gfxpayload=keep", + "linux /casper/vmlinuz autoinstall ---", + "initrd /casper/initrd", + "boot" + ] + cd_files = [ + "examples/http/ubuntu-2404/meta-data", + "examples/http/ubuntu-2404/user-data" + ] + + # The xenserver plugin needs to SSH in to the new VM, so we give it + # the information to do so + ssh_username = "testuser" + ssh_password = "ubuntu" + ssh_wait_timeout = "60000s" + ssh_handshake_attempts = 10000 + + + output_directory = null + keep_vm = "always" +} + +build { + sources = ["xenserver-iso.ubuntu-2404"] + + # Things to do on the new VM once it's past first reboot: + + # Wait for cloud-init to finish everything. We need to do this as a + # packer provisioner to prevent packer-plugin-xenserver from shutting + # the VM down before all cloud-init processing is complete. + provisioner "shell" { + inline = [ + "echo ubuntu | sudo -S cloud-init status --wait" + ] + valid_exit_codes = [0, 2] + } +}