github.com/vmware/govmomi@v0.51.0/guest/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 guest 6 7 import ( 8 "context" 9 "fmt" 10 "net" 11 "net/url" 12 "os" 13 "sync" 14 15 "github.com/vmware/govmomi/internal" 16 "github.com/vmware/govmomi/property" 17 "github.com/vmware/govmomi/vim25" 18 "github.com/vmware/govmomi/vim25/methods" 19 "github.com/vmware/govmomi/vim25/mo" 20 "github.com/vmware/govmomi/vim25/types" 21 ) 22 23 type FileManager struct { 24 types.ManagedObjectReference 25 26 vm types.ManagedObjectReference 27 28 c *vim25.Client 29 30 mu *sync.Mutex 31 hosts map[string]string 32 } 33 34 func (m FileManager) Reference() types.ManagedObjectReference { 35 return m.ManagedObjectReference 36 } 37 38 func (m FileManager) ChangeFileAttributes(ctx context.Context, auth types.BaseGuestAuthentication, guestFilePath string, fileAttributes types.BaseGuestFileAttributes) error { 39 req := types.ChangeFileAttributesInGuest{ 40 This: m.Reference(), 41 Vm: m.vm, 42 Auth: auth, 43 GuestFilePath: guestFilePath, 44 FileAttributes: fileAttributes, 45 } 46 47 _, err := methods.ChangeFileAttributesInGuest(ctx, m.c, &req) 48 return err 49 } 50 51 func (m FileManager) CreateTemporaryDirectory(ctx context.Context, auth types.BaseGuestAuthentication, prefix, suffix string, path string) (string, error) { 52 req := types.CreateTemporaryDirectoryInGuest{ 53 This: m.Reference(), 54 Vm: m.vm, 55 Auth: auth, 56 Prefix: prefix, 57 Suffix: suffix, 58 DirectoryPath: path, 59 } 60 61 res, err := methods.CreateTemporaryDirectoryInGuest(ctx, m.c, &req) 62 if err != nil { 63 return "", err 64 } 65 66 return res.Returnval, nil 67 } 68 69 func (m FileManager) CreateTemporaryFile(ctx context.Context, auth types.BaseGuestAuthentication, prefix, suffix string, path string) (string, error) { 70 req := types.CreateTemporaryFileInGuest{ 71 This: m.Reference(), 72 Vm: m.vm, 73 Auth: auth, 74 Prefix: prefix, 75 Suffix: suffix, 76 DirectoryPath: path, 77 } 78 79 res, err := methods.CreateTemporaryFileInGuest(ctx, m.c, &req) 80 if err != nil { 81 return "", err 82 } 83 84 return res.Returnval, nil 85 } 86 87 func (m FileManager) DeleteDirectory(ctx context.Context, auth types.BaseGuestAuthentication, directoryPath string, recursive bool) error { 88 req := types.DeleteDirectoryInGuest{ 89 This: m.Reference(), 90 Vm: m.vm, 91 Auth: auth, 92 DirectoryPath: directoryPath, 93 Recursive: recursive, 94 } 95 96 _, err := methods.DeleteDirectoryInGuest(ctx, m.c, &req) 97 return err 98 } 99 100 func (m FileManager) DeleteFile(ctx context.Context, auth types.BaseGuestAuthentication, filePath string) error { 101 req := types.DeleteFileInGuest{ 102 This: m.Reference(), 103 Vm: m.vm, 104 Auth: auth, 105 FilePath: filePath, 106 } 107 108 _, err := methods.DeleteFileInGuest(ctx, m.c, &req) 109 return err 110 } 111 112 // escape hatch to disable the preference to use ESX host management IP for guest file transfer 113 var useGuestTransferIP = os.Getenv("GOVMOMI_USE_GUEST_TRANSFER_IP") != "false" 114 115 // TransferURL rewrites the url with a valid hostname and adds the host's thumbprint. 116 // The InitiateFileTransfer{From,To}Guest methods return a URL with the host set to "*" when connected directly to ESX, 117 // but return the address of VM's runtime host when connected to vCenter. 118 func (m FileManager) TransferURL(ctx context.Context, u string) (*url.URL, error) { 119 turl, err := url.Parse(u) 120 if err != nil { 121 return nil, err 122 } 123 124 if turl.Hostname() == "*" { 125 turl.Host = m.c.URL().Host // Also use Client's port, to support port forwarding 126 } 127 128 if !m.c.IsVC() { 129 return turl, nil // we already connected to the ESX host and have its thumbprint 130 } 131 132 name := turl.Hostname() 133 port := turl.Port() 134 isHostname := net.ParseIP(name) == nil 135 136 m.mu.Lock() 137 mname, ok := m.hosts[name] 138 m.mu.Unlock() 139 140 if ok { 141 turl.Host = mname 142 return turl, nil 143 } else { 144 mname = turl.Host 145 } 146 147 c := property.DefaultCollector(m.c) 148 149 var vm mo.VirtualMachine 150 err = c.RetrieveOne(ctx, m.vm, []string{"name", "runtime.host"}, &vm) 151 if err != nil { 152 return nil, err 153 } 154 155 if vm.Runtime.Host == nil { 156 return turl, nil // won't matter if the VM was powered off since the call to InitiateFileTransfer will fail 157 } 158 159 // VC supports the use of a Unix domain socket for guest file transfers. 160 if internal.UsingEnvoySidecar(m.c) { 161 // Rewrite the URL in the format unix:// 162 // Reciever must use a custom dialer. 163 // Nil check performed above, so Host is safe to access. 164 return internal.HostGatewayTransferURL(turl, *vm.Runtime.Host), nil 165 } 166 167 // Determine host thumbprint, address etc. to be able to trust host. 168 props := []string{ 169 "name", 170 "runtime.connectionState", 171 "summary.config.sslThumbprint", 172 } 173 174 if isHostname { 175 props = append(props, "config.virtualNicManagerInfo.netConfig") 176 } 177 178 var host mo.HostSystem 179 err = c.RetrieveOne(ctx, *vm.Runtime.Host, props, &host) 180 if err != nil { 181 return nil, err 182 } 183 184 if isHostname { 185 if host.Config == nil { 186 return nil, fmt.Errorf("guest TransferURL failed for vm %q (%s): host %q (%s) config==nil, connectionState==%s", 187 vm.Name, vm.Self, 188 host.Name, host.Self, host.Runtime.ConnectionState) 189 } 190 191 // InitiateFileTransfer{To,From}Guest methods return an ESX host's inventory name (HostSystem.Name). 192 // This name was used to add the host to vCenter and cannot be changed (unless the host is removed from inventory and added back with another name). 193 // The name used when adding to VC may not resolvable by this client's DNS, so we prefer an ESX management IP. 194 // However, if there is more than one management vNIC, we don't know which IP(s) the client has a route to. 195 // Leave the hostname as-is in that case or if the env var has disabled the preference. 196 ips := internal.HostSystemManagementIPs(host.Config.VirtualNicManagerInfo.NetConfig) 197 if len(ips) == 1 && useGuestTransferIP { 198 mname = net.JoinHostPort(ips[0].String(), port) 199 200 turl.Host = mname 201 } 202 } 203 204 m.mu.Lock() 205 m.hosts[name] = mname 206 m.mu.Unlock() 207 208 m.c.SetThumbprint(turl.Host, host.Summary.Config.SslThumbprint) 209 210 return turl, nil 211 } 212 213 func (m FileManager) InitiateFileTransferFromGuest(ctx context.Context, auth types.BaseGuestAuthentication, guestFilePath string) (*types.FileTransferInformation, error) { 214 req := types.InitiateFileTransferFromGuest{ 215 This: m.Reference(), 216 Vm: m.vm, 217 Auth: auth, 218 GuestFilePath: guestFilePath, 219 } 220 221 res, err := methods.InitiateFileTransferFromGuest(ctx, m.c, &req) 222 if err != nil { 223 return nil, err 224 } 225 226 return &res.Returnval, nil 227 } 228 229 func (m FileManager) InitiateFileTransferToGuest(ctx context.Context, auth types.BaseGuestAuthentication, guestFilePath string, fileAttributes types.BaseGuestFileAttributes, fileSize int64, overwrite bool) (string, error) { 230 req := types.InitiateFileTransferToGuest{ 231 This: m.Reference(), 232 Vm: m.vm, 233 Auth: auth, 234 GuestFilePath: guestFilePath, 235 FileAttributes: fileAttributes, 236 FileSize: fileSize, 237 Overwrite: overwrite, 238 } 239 240 res, err := methods.InitiateFileTransferToGuest(ctx, m.c, &req) 241 if err != nil { 242 return "", err 243 } 244 245 return res.Returnval, nil 246 } 247 248 func (m FileManager) ListFiles(ctx context.Context, auth types.BaseGuestAuthentication, filePath string, index int32, maxResults int32, matchPattern string) (*types.GuestListFileInfo, error) { 249 req := types.ListFilesInGuest{ 250 This: m.Reference(), 251 Vm: m.vm, 252 Auth: auth, 253 FilePath: filePath, 254 Index: index, 255 MaxResults: maxResults, 256 MatchPattern: matchPattern, 257 } 258 259 res, err := methods.ListFilesInGuest(ctx, m.c, &req) 260 if err != nil { 261 return nil, err 262 } 263 264 return &res.Returnval, nil 265 } 266 267 func (m FileManager) MakeDirectory(ctx context.Context, auth types.BaseGuestAuthentication, directoryPath string, createParentDirectories bool) error { 268 req := types.MakeDirectoryInGuest{ 269 This: m.Reference(), 270 Vm: m.vm, 271 Auth: auth, 272 DirectoryPath: directoryPath, 273 CreateParentDirectories: createParentDirectories, 274 } 275 276 _, err := methods.MakeDirectoryInGuest(ctx, m.c, &req) 277 return err 278 } 279 280 func (m FileManager) MoveDirectory(ctx context.Context, auth types.BaseGuestAuthentication, srcDirectoryPath string, dstDirectoryPath string) error { 281 req := types.MoveDirectoryInGuest{ 282 This: m.Reference(), 283 Vm: m.vm, 284 Auth: auth, 285 SrcDirectoryPath: srcDirectoryPath, 286 DstDirectoryPath: dstDirectoryPath, 287 } 288 289 _, err := methods.MoveDirectoryInGuest(ctx, m.c, &req) 290 return err 291 } 292 293 func (m FileManager) MoveFile(ctx context.Context, auth types.BaseGuestAuthentication, srcFilePath string, dstFilePath string, overwrite bool) error { 294 req := types.MoveFileInGuest{ 295 This: m.Reference(), 296 Vm: m.vm, 297 Auth: auth, 298 SrcFilePath: srcFilePath, 299 DstFilePath: dstFilePath, 300 Overwrite: overwrite, 301 } 302 303 _, err := methods.MoveFileInGuest(ctx, m.c, &req) 304 return err 305 }