github.com/vmware/govmomi@v0.37.2/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  }