@@ -9,6 +9,7 @@ package clone
99import (
1010 "context"
1111 "fmt"
12+ "log"
1213 "path"
1314 "strings"
1415
@@ -17,6 +18,7 @@ import (
1718 "github.com/hashicorp/packer-plugin-sdk/packerbuilderdata"
1819 "github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/common"
1920 "github.com/hashicorp/packer-plugin-vsphere/builder/vsphere/driver"
21+ "github.com/vmware/govmomi/vim25/types"
2022)
2123
2224type vAppConfig struct {
@@ -159,13 +161,61 @@ func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multist
159161 })
160162 }
161163
164+ datastoreName := s .Location .Datastore
165+ var primaryDatastore driver.Datastore
166+ if ds , ok := state .GetOk ("datastore" ); ok {
167+ primaryDatastore = ds .(driver.Datastore )
168+ datastoreName = primaryDatastore .Name ()
169+ }
170+
171+ // If no datastore was resolved and no datastore was specified, return an error.
172+ if datastoreName == "" && s .Location .DatastoreCluster == "" {
173+ state .Put ("error" , fmt .Errorf ("no datastore specified and no datastore resolved from cluster" ))
174+ return multistep .ActionHalt
175+ }
176+
177+ // Handle multi-disk placement when using a datastore cluster.
178+ var datastoreRefs []* types.ManagedObjectReference
179+ if s .Location .DatastoreCluster != "" && len (disks ) > 1 {
180+ if vcDriver , ok := d .(* driver.VCenterDriver ); ok {
181+ // Request Storage DRS recommendations for all disks at once for optimal placement.
182+ ui .Sayf ("Requesting Storage DRS recommendations for %d disks..." , len (disks ))
183+
184+ diskDatastores , method , err := vcDriver .SelectDatastoresForDisks (s .Location .DatastoreCluster , disks )
185+ if err != nil {
186+ ui .Errorf ("Warning: Failed to get Storage DRS recommendations: %s. Using primary datastore." , err )
187+ if primaryDatastore != nil {
188+ ref := primaryDatastore .Reference ()
189+ for i := 0 ; i < len (disks ); i ++ {
190+ datastoreRefs = append (datastoreRefs , & ref )
191+ }
192+ }
193+ } else {
194+ // Use the first disk's datastore as the primary datastore.
195+ if len (diskDatastores ) > 0 {
196+ datastoreName = diskDatastores [0 ].Name ()
197+ }
198+
199+ for i , ds := range diskDatastores {
200+ ref := ds .Reference ()
201+ if method == driver .SelectionMethodDRS {
202+ log .Printf ("[INFO] Disk %d: Storage DRS selected datastore '%s'" , i + 1 , ds .Name ())
203+ } else {
204+ log .Printf ("[INFO] Disk %d: Using first available datastore '%s'" , i + 1 , ds .Name ())
205+ }
206+ datastoreRefs = append (datastoreRefs , & ref )
207+ }
208+ }
209+ }
210+ }
211+
162212 vm , err := template .Clone (ctx , & driver.CloneConfig {
163213 Name : s .Location .VMName ,
164214 Folder : s .Location .Folder ,
165215 Cluster : s .Location .Cluster ,
166216 Host : s .Location .Host ,
167217 ResourcePool : s .Location .ResourcePool ,
168- Datastore : s . Location . Datastore ,
218+ Datastore : datastoreName ,
169219 LinkedClone : s .Config .LinkedClone ,
170220 Network : s .Config .Network ,
171221 MacAddress : strings .ToLower (s .Config .MacAddress ),
@@ -175,6 +225,7 @@ func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multist
175225 StorageConfig : driver.StorageConfig {
176226 DiskControllerType : s .Config .StorageConfig .DiskControllerType ,
177227 Storage : disks ,
228+ DatastoreRefs : datastoreRefs ,
178229 },
179230 })
180231 if err != nil {
0 commit comments