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