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