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