github.com/vmware/govmomi@v0.37.2/simulator/guest_operations_manager.go (about)

     1  /*
     2  Copyright (c) 2020 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 simulator
    18  
    19  import (
    20  	"bufio"
    21  	"fmt"
    22  	"net/url"
    23  	"strings"
    24  	"syscall"
    25  	"time"
    26  
    27  	"github.com/vmware/govmomi/toolbox/process"
    28  	"github.com/vmware/govmomi/toolbox/vix"
    29  	"github.com/vmware/govmomi/vim25/methods"
    30  	"github.com/vmware/govmomi/vim25/mo"
    31  	"github.com/vmware/govmomi/vim25/soap"
    32  	"github.com/vmware/govmomi/vim25/types"
    33  )
    34  
    35  type GuestOperationsManager struct {
    36  	mo.GuestOperationsManager
    37  }
    38  
    39  func (m *GuestOperationsManager) init(r *Registry) {
    40  	fm := new(GuestFileManager)
    41  	if m.FileManager == nil {
    42  		m.FileManager = &types.ManagedObjectReference{
    43  			Type:  "GuestFileManager",
    44  			Value: "guestOperationsFileManager",
    45  		}
    46  	}
    47  	fm.Self = *m.FileManager
    48  	r.Put(fm)
    49  
    50  	pm := new(GuestProcessManager)
    51  	if m.ProcessManager == nil {
    52  		m.ProcessManager = &types.ManagedObjectReference{
    53  			Type:  "GuestProcessManager",
    54  			Value: "guestOperationsProcessManager",
    55  		}
    56  	}
    57  	pm.Self = *m.ProcessManager
    58  	pm.Manager = process.NewManager()
    59  	r.Put(pm)
    60  }
    61  
    62  type GuestFileManager struct {
    63  	mo.GuestFileManager
    64  }
    65  
    66  func guestURL(ctx *Context, vm *VirtualMachine, path string) string {
    67  	return (&url.URL{
    68  		Scheme: ctx.svc.Listen.Scheme,
    69  		Host:   "*", // See guest.FileManager.TransferURL
    70  		Path:   guestPrefix + strings.TrimPrefix(path, "/"),
    71  		RawQuery: url.Values{
    72  			"id":    []string{vm.svm.c.id},
    73  			"token": []string{ctx.Session.Key},
    74  		}.Encode(),
    75  	}).String()
    76  }
    77  
    78  func (m *GuestFileManager) InitiateFileTransferToGuest(ctx *Context, req *types.InitiateFileTransferToGuest) soap.HasFault {
    79  	body := new(methods.InitiateFileTransferToGuestBody)
    80  
    81  	vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
    82  	err := vm.svm.prepareGuestOperation(req.Auth)
    83  	if err != nil {
    84  		body.Fault_ = Fault("", err)
    85  		return body
    86  	}
    87  
    88  	body.Res = &types.InitiateFileTransferToGuestResponse{
    89  		Returnval: guestURL(ctx, vm, req.GuestFilePath),
    90  	}
    91  
    92  	return body
    93  }
    94  
    95  func (m *GuestFileManager) InitiateFileTransferFromGuest(ctx *Context, req *types.InitiateFileTransferFromGuest) soap.HasFault {
    96  	body := new(methods.InitiateFileTransferFromGuestBody)
    97  
    98  	vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
    99  	err := vm.svm.prepareGuestOperation(req.Auth)
   100  	if err != nil {
   101  		body.Fault_ = Fault("", err)
   102  		return body
   103  	}
   104  
   105  	body.Res = &types.InitiateFileTransferFromGuestResponse{
   106  		Returnval: types.FileTransferInformation{
   107  			Attributes: nil, // TODO
   108  			Size:       0,   // TODO
   109  			Url:        guestURL(ctx, vm, req.GuestFilePath),
   110  		},
   111  	}
   112  
   113  	return body
   114  }
   115  
   116  type GuestProcessManager struct {
   117  	mo.GuestProcessManager
   118  	*process.Manager
   119  }
   120  
   121  func (m *GuestProcessManager) StartProgramInGuest(ctx *Context, req *types.StartProgramInGuest) soap.HasFault {
   122  	body := new(methods.StartProgramInGuestBody)
   123  
   124  	spec := req.Spec.(*types.GuestProgramSpec)
   125  	auth := req.Auth.(*types.NamePasswordAuthentication)
   126  
   127  	vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
   128  
   129  	fault := vm.svm.prepareGuestOperation(auth)
   130  	if fault != nil {
   131  		body.Fault_ = Fault("", fault)
   132  	}
   133  
   134  	args := []string{"exec"}
   135  
   136  	if spec.WorkingDirectory != "" {
   137  		args = append(args, "-w", spec.WorkingDirectory)
   138  	}
   139  
   140  	for _, e := range spec.EnvVariables {
   141  		args = append(args, "-e", e)
   142  	}
   143  
   144  	args = append(args, vm.svm.c.id, spec.ProgramPath, spec.Arguments)
   145  
   146  	spec.ProgramPath = "docker"
   147  	spec.Arguments = strings.Join(args, " ")
   148  
   149  	start := &vix.StartProgramRequest{
   150  		ProgramPath: spec.ProgramPath,
   151  		Arguments:   spec.Arguments,
   152  	}
   153  
   154  	proc := process.New()
   155  	proc.Owner = auth.Username
   156  
   157  	pid, err := m.Start(start, proc)
   158  	if err != nil {
   159  		panic(err) // only happens if LookPath("docker") fails, which it should't at this point
   160  	}
   161  
   162  	body.Res = &types.StartProgramInGuestResponse{
   163  		Returnval: pid,
   164  	}
   165  
   166  	return body
   167  }
   168  
   169  func (m *GuestProcessManager) ListProcessesInGuest(ctx *Context, req *types.ListProcessesInGuest) soap.HasFault {
   170  	body := &methods.ListProcessesInGuestBody{
   171  		Res: new(types.ListProcessesInGuestResponse),
   172  	}
   173  
   174  	procs := m.List(req.Pids)
   175  
   176  	for _, proc := range procs {
   177  		var end *time.Time
   178  		if proc.EndTime != 0 {
   179  			end = types.NewTime(time.Unix(proc.EndTime, 0))
   180  		}
   181  
   182  		body.Res.Returnval = append(body.Res.Returnval, types.GuestProcessInfo{
   183  			Name:      proc.Name,
   184  			Pid:       proc.Pid,
   185  			Owner:     proc.Owner,
   186  			CmdLine:   proc.Name + " " + proc.Args,
   187  			StartTime: time.Unix(proc.StartTime, 0),
   188  			EndTime:   end,
   189  			ExitCode:  proc.ExitCode,
   190  		})
   191  	}
   192  
   193  	return body
   194  }
   195  
   196  func (m *GuestProcessManager) TerminateProcessInGuest(ctx *Context, req *types.TerminateProcessInGuest) soap.HasFault {
   197  	body := new(methods.TerminateProcessInGuestBody)
   198  
   199  	if m.Kill(req.Pid) {
   200  		body.Res = new(types.TerminateProcessInGuestResponse)
   201  	} else {
   202  		body.Fault_ = Fault("", &types.GuestProcessNotFound{Pid: req.Pid})
   203  	}
   204  
   205  	return body
   206  }
   207  
   208  func (m *GuestFileManager) mktemp(ctx *Context, req *types.CreateTemporaryFileInGuest, dir bool) (string, types.BaseMethodFault) {
   209  	args := []string{"mktemp", fmt.Sprintf("--tmpdir=%s", req.DirectoryPath), req.Prefix + "vcsim-XXXXX" + req.Suffix}
   210  	if dir {
   211  		args = append(args, "-d")
   212  	}
   213  
   214  	vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
   215  
   216  	return vm.svm.exec(ctx, req.Auth, args)
   217  }
   218  
   219  func (m *GuestFileManager) CreateTemporaryFileInGuest(ctx *Context, req *types.CreateTemporaryFileInGuest) soap.HasFault {
   220  	body := new(methods.CreateTemporaryFileInGuestBody)
   221  
   222  	res, fault := m.mktemp(ctx, req, false)
   223  	if fault != nil {
   224  		body.Fault_ = Fault("", fault)
   225  		return body
   226  	}
   227  
   228  	body.Res = &types.CreateTemporaryFileInGuestResponse{Returnval: res}
   229  
   230  	return body
   231  }
   232  
   233  func (m *GuestFileManager) CreateTemporaryDirectoryInGuest(ctx *Context, req *types.CreateTemporaryDirectoryInGuest) soap.HasFault {
   234  	body := new(methods.CreateTemporaryDirectoryInGuestBody)
   235  
   236  	dir := types.CreateTemporaryFileInGuest(*req)
   237  	res, fault := m.mktemp(ctx, &dir, true)
   238  	if fault != nil {
   239  		body.Fault_ = Fault("", fault)
   240  		return body
   241  	}
   242  
   243  	body.Res = &types.CreateTemporaryDirectoryInGuestResponse{Returnval: res}
   244  
   245  	return body
   246  }
   247  
   248  func listFiles(req *types.ListFilesInGuest) []string {
   249  	args := []string{"find", req.FilePath}
   250  	if req.MatchPattern != "" {
   251  		args = append(args, "-name", req.MatchPattern)
   252  	}
   253  	return append(args, "-maxdepth", "1", "-exec", "stat", "-c", "%s %u %g %f %X %Y %n", "{}", "+")
   254  }
   255  
   256  func toFileInfo(s string) []types.GuestFileInfo {
   257  	var res []types.GuestFileInfo
   258  
   259  	scanner := bufio.NewScanner(strings.NewReader(s))
   260  
   261  	for scanner.Scan() {
   262  		var mode, atime, mtime int64
   263  		attr := &types.GuestPosixFileAttributes{OwnerId: new(int32), GroupId: new(int32)}
   264  		info := types.GuestFileInfo{Attributes: attr}
   265  
   266  		_, err := fmt.Sscanf(scanner.Text(), "%d %d %d %x %d %d %s",
   267  			&info.Size, attr.OwnerId, attr.GroupId, &mode, &atime, &mtime, &info.Path)
   268  		if err != nil {
   269  			panic(err)
   270  		}
   271  
   272  		attr.AccessTime = types.NewTime(time.Unix(atime, 0))
   273  		attr.ModificationTime = types.NewTime(time.Unix(mtime, 0))
   274  		attr.Permissions = mode & 0777
   275  
   276  		switch mode & syscall.S_IFMT {
   277  		case syscall.S_IFDIR:
   278  			info.Type = string(types.GuestFileTypeDirectory)
   279  		case syscall.S_IFLNK:
   280  			info.Type = string(types.GuestFileTypeSymlink)
   281  		default:
   282  			info.Type = string(types.GuestFileTypeFile)
   283  		}
   284  
   285  		res = append(res, info)
   286  	}
   287  
   288  	return res
   289  }
   290  
   291  func (m *GuestFileManager) ListFilesInGuest(ctx *Context, req *types.ListFilesInGuest) soap.HasFault {
   292  	body := new(methods.ListFilesInGuestBody)
   293  
   294  	vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
   295  
   296  	if req.FilePath == "" {
   297  		body.Fault_ = Fault("", new(types.InvalidArgument))
   298  		return body
   299  	}
   300  
   301  	res, fault := vm.svm.exec(ctx, req.Auth, listFiles(req))
   302  	if fault != nil {
   303  		body.Fault_ = Fault("", fault)
   304  		return body
   305  	}
   306  
   307  	body.Res = new(types.ListFilesInGuestResponse)
   308  	body.Res.Returnval.Files = toFileInfo(res)
   309  
   310  	return body
   311  }
   312  
   313  func (m *GuestFileManager) DeleteFileInGuest(ctx *Context, req *types.DeleteFileInGuest) soap.HasFault {
   314  	body := new(methods.DeleteFileInGuestBody)
   315  
   316  	args := []string{"rm", req.FilePath}
   317  
   318  	vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
   319  
   320  	_, fault := vm.svm.exec(ctx, req.Auth, args)
   321  	if fault != nil {
   322  		body.Fault_ = Fault("", fault)
   323  		return body
   324  	}
   325  
   326  	body.Res = new(types.DeleteFileInGuestResponse)
   327  
   328  	return body
   329  }
   330  
   331  func (m *GuestFileManager) DeleteDirectoryInGuest(ctx *Context, req *types.DeleteDirectoryInGuest) soap.HasFault {
   332  	body := new(methods.DeleteDirectoryInGuestBody)
   333  
   334  	args := []string{"rmdir", req.DirectoryPath}
   335  	if req.Recursive {
   336  		args = []string{"rm", "-rf", req.DirectoryPath}
   337  	}
   338  
   339  	vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
   340  
   341  	_, fault := vm.svm.exec(ctx, req.Auth, args)
   342  	if fault != nil {
   343  		body.Fault_ = Fault("", fault)
   344  		return body
   345  	}
   346  
   347  	body.Res = new(types.DeleteDirectoryInGuestResponse)
   348  
   349  	return body
   350  }
   351  
   352  func (m *GuestFileManager) MakeDirectoryInGuest(ctx *Context, req *types.MakeDirectoryInGuest) soap.HasFault {
   353  	body := new(methods.MakeDirectoryInGuestBody)
   354  
   355  	args := []string{"mkdir", req.DirectoryPath}
   356  	if req.CreateParentDirectories {
   357  		args = []string{"mkdir", "-p", req.DirectoryPath}
   358  	}
   359  
   360  	vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
   361  
   362  	_, fault := vm.svm.exec(ctx, req.Auth, args)
   363  	if fault != nil {
   364  		body.Fault_ = Fault("", fault)
   365  		return body
   366  	}
   367  
   368  	body.Res = new(types.MakeDirectoryInGuestResponse)
   369  
   370  	return body
   371  }
   372  
   373  func (m *GuestFileManager) MoveFileInGuest(ctx *Context, req *types.MoveFileInGuest) soap.HasFault {
   374  	body := new(methods.MoveFileInGuestBody)
   375  
   376  	args := []string{"mv"}
   377  	if !req.Overwrite {
   378  		args = append(args, "-n")
   379  	}
   380  	args = append(args, req.SrcFilePath, req.DstFilePath)
   381  
   382  	vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
   383  
   384  	_, fault := vm.svm.exec(ctx, req.Auth, args)
   385  	if fault != nil {
   386  		body.Fault_ = Fault("", fault)
   387  		return body
   388  	}
   389  
   390  	body.Res = new(types.MoveFileInGuestResponse)
   391  
   392  	return body
   393  }
   394  
   395  func (m *GuestFileManager) MoveDirectoryInGuest(ctx *Context, req *types.MoveDirectoryInGuest) soap.HasFault {
   396  	body := new(methods.MoveDirectoryInGuestBody)
   397  
   398  	args := []string{"mv", req.SrcDirectoryPath, req.DstDirectoryPath}
   399  
   400  	vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
   401  
   402  	_, fault := vm.svm.exec(ctx, req.Auth, args)
   403  	if fault != nil {
   404  		body.Fault_ = Fault("", fault)
   405  		return body
   406  	}
   407  
   408  	body.Res = new(types.MoveDirectoryInGuestResponse)
   409  
   410  	return body
   411  }
   412  
   413  func (m *GuestFileManager) ChangeFileAttributesInGuest(ctx *Context, req *types.ChangeFileAttributesInGuest) soap.HasFault {
   414  	body := new(methods.ChangeFileAttributesInGuestBody)
   415  
   416  	vm := ctx.Map.Get(req.Vm).(*VirtualMachine)
   417  
   418  	attr, ok := req.FileAttributes.(*types.GuestPosixFileAttributes)
   419  	if !ok {
   420  		body.Fault_ = Fault("", new(types.OperationNotSupportedByGuest))
   421  		return body
   422  	}
   423  
   424  	if attr.Permissions != 0 {
   425  		args := []string{"chmod", fmt.Sprintf("%#o", attr.Permissions), req.GuestFilePath}
   426  
   427  		_, fault := vm.svm.exec(ctx, req.Auth, args)
   428  		if fault != nil {
   429  			body.Fault_ = Fault("", fault)
   430  			return body
   431  		}
   432  	}
   433  
   434  	change := []struct {
   435  		cmd string
   436  		id  *int32
   437  	}{
   438  		{"chown", attr.OwnerId},
   439  		{"chgrp", attr.GroupId},
   440  	}
   441  
   442  	for _, c := range change {
   443  		if c.id != nil {
   444  			args := []string{c.cmd, fmt.Sprintf("%d", *c.id), req.GuestFilePath}
   445  
   446  			_, fault := vm.svm.exec(ctx, req.Auth, args)
   447  			if fault != nil {
   448  				body.Fault_ = Fault("", fault)
   449  				return body
   450  			}
   451  		}
   452  	}
   453  
   454  	body.Res = new(types.ChangeFileAttributesInGuestResponse)
   455  
   456  	return body
   457  }