github.com/vmware/govmomi@v0.51.0/object/datastore_file_manager.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package object 6 7 import ( 8 "bufio" 9 "bytes" 10 "context" 11 "fmt" 12 "io" 13 "log" 14 "path" 15 "strings" 16 17 "github.com/vmware/govmomi/vim25/progress" 18 "github.com/vmware/govmomi/vim25/soap" 19 ) 20 21 // DatastoreFileManager combines FileManager and VirtualDiskManager to manage files on a Datastore 22 type DatastoreFileManager struct { 23 Datacenter *Datacenter 24 Datastore *Datastore 25 FileManager *FileManager 26 VirtualDiskManager *VirtualDiskManager 27 28 Force bool 29 DatacenterTarget *Datacenter 30 } 31 32 // NewFileManager creates a new instance of DatastoreFileManager 33 func (d Datastore) NewFileManager(dc *Datacenter, force bool) *DatastoreFileManager { 34 c := d.Client() 35 36 m := &DatastoreFileManager{ 37 Datacenter: dc, 38 Datastore: &d, 39 FileManager: NewFileManager(c), 40 VirtualDiskManager: NewVirtualDiskManager(c), 41 Force: force, 42 DatacenterTarget: dc, 43 } 44 45 return m 46 } 47 48 func (m *DatastoreFileManager) WithProgress(ctx context.Context, s progress.Sinker) context.Context { 49 return context.WithValue(ctx, m, s) 50 } 51 52 func (m *DatastoreFileManager) wait(ctx context.Context, task *Task) error { 53 var logger progress.Sinker 54 if s, ok := ctx.Value(m).(progress.Sinker); ok { 55 logger = s 56 } 57 _, err := task.WaitForResult(ctx, logger) 58 return err 59 } 60 61 // Delete dispatches to the appropriate Delete method based on file name extension 62 func (m *DatastoreFileManager) Delete(ctx context.Context, name string) error { 63 switch path.Ext(name) { 64 case ".vmdk": 65 return m.DeleteVirtualDisk(ctx, name) 66 default: 67 return m.DeleteFile(ctx, name) 68 } 69 } 70 71 // DeleteFile calls FileManager.DeleteDatastoreFile 72 func (m *DatastoreFileManager) DeleteFile(ctx context.Context, name string) error { 73 p := m.Path(name) 74 75 task, err := m.FileManager.DeleteDatastoreFile(ctx, p.String(), m.Datacenter) 76 if err != nil { 77 return err 78 } 79 80 return m.wait(ctx, task) 81 } 82 83 // DeleteVirtualDisk calls VirtualDiskManager.DeleteVirtualDisk 84 // Regardless of the Datastore type, DeleteVirtualDisk will fail if 'ddb.deletable=false', 85 // so if Force=true this method attempts to set 'ddb.deletable=true' before starting the delete task. 86 func (m *DatastoreFileManager) DeleteVirtualDisk(ctx context.Context, name string) error { 87 p := m.Path(name) 88 89 var merr error 90 91 if m.Force { 92 merr = m.markDiskAsDeletable(ctx, p) 93 } 94 95 task, err := m.VirtualDiskManager.DeleteVirtualDisk(ctx, p.String(), m.Datacenter) 96 if err != nil { 97 log.Printf("markDiskAsDeletable(%s): %s", p, merr) 98 return err 99 } 100 101 return m.wait(ctx, task) 102 } 103 104 // CopyFile calls FileManager.CopyDatastoreFile 105 func (m *DatastoreFileManager) CopyFile(ctx context.Context, src string, dst string) error { 106 srcp := m.Path(src) 107 dstp := m.Path(dst) 108 109 task, err := m.FileManager.CopyDatastoreFile(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force) 110 if err != nil { 111 return err 112 } 113 114 return m.wait(ctx, task) 115 } 116 117 // Copy dispatches to the appropriate FileManager or VirtualDiskManager Copy method based on file name extension 118 func (m *DatastoreFileManager) Copy(ctx context.Context, src string, dst string) error { 119 srcp := m.Path(src) 120 dstp := m.Path(dst) 121 122 f := m.FileManager.CopyDatastoreFile 123 124 if srcp.IsVMDK() { 125 // types.VirtualDiskSpec=nil as it is not implemented by vCenter 126 f = func(ctx context.Context, src string, srcDC *Datacenter, dst string, dstDC *Datacenter, force bool) (*Task, error) { 127 return m.VirtualDiskManager.CopyVirtualDisk(ctx, src, srcDC, dst, dstDC, nil, force) 128 } 129 } 130 131 task, err := f(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force) 132 if err != nil { 133 return err 134 } 135 136 return m.wait(ctx, task) 137 } 138 139 // MoveFile calls FileManager.MoveDatastoreFile 140 func (m *DatastoreFileManager) MoveFile(ctx context.Context, src string, dst string) error { 141 srcp := m.Path(src) 142 dstp := m.Path(dst) 143 144 task, err := m.FileManager.MoveDatastoreFile(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force) 145 if err != nil { 146 return err 147 } 148 149 return m.wait(ctx, task) 150 } 151 152 // Move dispatches to the appropriate FileManager or VirtualDiskManager Move method based on file name extension 153 func (m *DatastoreFileManager) Move(ctx context.Context, src string, dst string) error { 154 srcp := m.Path(src) 155 dstp := m.Path(dst) 156 157 f := m.FileManager.MoveDatastoreFile 158 159 if srcp.IsVMDK() { 160 f = m.VirtualDiskManager.MoveVirtualDisk 161 } 162 163 task, err := f(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force) 164 if err != nil { 165 return err 166 } 167 168 return m.wait(ctx, task) 169 } 170 171 // Path converts path name to a DatastorePath 172 func (m *DatastoreFileManager) Path(name string) *DatastorePath { 173 var p DatastorePath 174 175 if !p.FromString(name) { 176 p.Path = name 177 p.Datastore = m.Datastore.Name() 178 } 179 180 return &p 181 } 182 183 func (m *DatastoreFileManager) markDiskAsDeletable(ctx context.Context, path *DatastorePath) error { 184 r, _, err := m.Datastore.Download(ctx, path.Path, &soap.DefaultDownload) 185 if err != nil { 186 return err 187 } 188 189 defer r.Close() 190 191 hasFlag := false 192 buf := new(bytes.Buffer) 193 194 s := bufio.NewScanner(&io.LimitedReader{R: r, N: 2048}) // should be only a few hundred bytes, limit to be sure 195 196 for s.Scan() { 197 line := s.Text() 198 if strings.HasPrefix(line, "ddb.deletable") { 199 hasFlag = true 200 continue 201 } 202 203 fmt.Fprintln(buf, line) 204 } 205 206 if err := s.Err(); err != nil { 207 return err // any error other than EOF 208 } 209 210 if !hasFlag { 211 return nil // already deletable, so leave as-is 212 } 213 214 // rewrite the .vmdk with ddb.deletable flag removed (the default is true) 215 return m.Datastore.Upload(ctx, buf, path.Path, &soap.DefaultUpload) 216 }