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 }