From 0469bd8d804e9f35923270c3b7a558f2b96af480 Mon Sep 17 00:00:00 2001 From: jacdavi <86626873+jacdavi@users.noreply.github.com> Date: Fri, 12 Jul 2024 23:53:32 +0000 Subject: [PATCH 1/7] fix error message after successful memory snapshot --- src/go/web/handlers.go | 4 +++- src/js/src/components/RunningExperiment.vue | 26 +++++++++++---------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/go/web/handlers.go b/src/go/web/handlers.go index 33792b8e..f5ce2439 100644 --- a/src/go/web/handlers.go +++ b/src/go/web/handlers.go @@ -2438,10 +2438,12 @@ func CreateVMMemorySnapshot(w http.ResponseWriter, r *http.Request) { return } + marshalled, _ := json.Marshal(util.WithRoot("disk", filename)) + broker.Broadcast( bt.NewRequestPolicy("vms/memorySnapshot", "create", fmt.Sprintf("%s/%s", exp, name)), bt.NewResource("experiment/vm/memorySnapshot", exp+"/"+name, "commit"), - nil, + marshalled, ) w.WriteHeader(http.StatusNoContent) diff --git a/src/js/src/components/RunningExperiment.vue b/src/js/src/components/RunningExperiment.vue index 1f2b6032..3dfcc8ac 100644 --- a/src/js/src/components/RunningExperiment.vue +++ b/src/js/src/components/RunningExperiment.vue @@ -1288,21 +1288,23 @@ case 'commit': { for ( let i = 0; i < vms.length; i++ ) { if ( vms[i].name == vm[ 1 ] ) { + console.log(vm) + console.log(vms) + console.log(msg) vms[i].busy = false; - vms[i] = msg.result.vm; - let disk = msg.result.disk; - - this.$buefy.toast.open({ - message: 'A memory snapshot was created with name ' + disk + ' for the ' + vm[ 1 ] + ' VM was successfully created.', - type: 'is-success', - duration: 4000 - }); - this.experiment.vms = [ ...vms ]; - break; - } + let disk = msg.result.disk; + + this.$buefy.toast.open({ + message: 'A memory snapshot was created with name ' + disk + ' for the ' + vm[ 1 ] + ' VM was successfully created.', + type: 'is-success', + duration: 4000 + }); + this.experiment.vms = [ ...vms ]; + break; } - break; } + break; + } case 'committing': { for ( let i = 0; i < vms.length; i++ ) { From 81d17c6b3535864d6e47188421fe87d3c5e4dda5 Mon Sep 17 00:00:00 2001 From: jacdavi <86626873+jacdavi@users.noreply.github.com> Date: Sat, 13 Jul 2024 01:22:36 +0000 Subject: [PATCH 2/7] fix errors in web console --- src/js/src/components/RunningExperiment.vue | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/js/src/components/RunningExperiment.vue b/src/js/src/components/RunningExperiment.vue index 3dfcc8ac..4d46daec 100644 --- a/src/js/src/components/RunningExperiment.vue +++ b/src/js/src/components/RunningExperiment.vue @@ -552,7 +552,6 @@

- @@ -629,7 +628,7 @@

- +

@@ -1258,11 +1257,7 @@ } case 'progress': { - //this.$buefy.toast.open({ - // message: 'PROGRESS', - // duration: 200 - // }); - let percent = ( msg.result.percent * 100 ).toFixed( 0 ); + let percent = Math.round( msg.result.percent * 100 ); for ( let i = 0; i < vms.length; i++ ) { if ( vms[i].name == vm[ 1 ] ) { @@ -1345,6 +1340,9 @@ case 'experiment/vm/screenshot': { let vm = msg.resource.name.split( '/' ); let vms = this.experiment.vms; + if (!vms) { + break; + } switch ( msg.resource.action ) { case 'update': { @@ -1418,6 +1416,9 @@ case 'experiment/vm/snapshot': { let vm = msg.resource.name.split( '/' ); let vms = this.experiment.vms; + if (!vms) { + break; + } switch ( msg.resource.action ) { case 'create': { @@ -1456,7 +1457,7 @@ } case 'progress': { - let percent = ( msg.result.percent * 100 ).toFixed( 0 ); + let percent = Math.round(msg.result.percent * 100 ); for ( let i = 0; i < vms.length; i++ ) { if ( vms[i].name == vm[ 1 ] ) { From ed55ae6d21e91a966cdf75bbede367d152ad693a Mon Sep 17 00:00:00 2001 From: jacdavi <86626873+jacdavi@users.noreply.github.com> Date: Wed, 14 Aug 2024 00:54:51 +0000 Subject: [PATCH 3/7] dont rely on ps for mm path; use new mm "args" cmd --- src/go/api/cluster/files.go | 7 ++-- src/go/api/vm/vm.go | 3 +- src/go/util/file.go | 80 ------------------------------------- src/go/util/mm/file.go | 39 ++++++++++++++++++ src/go/util/mm/minimega.go | 6 +++ src/go/util/mm/mm.go | 1 + src/go/util/mm/package.go | 4 ++ 7 files changed, 54 insertions(+), 86 deletions(-) delete mode 100644 src/go/util/file.go create mode 100644 src/go/util/mm/file.go diff --git a/src/go/api/cluster/files.go b/src/go/api/cluster/files.go index be8fa871..3c5507d9 100644 --- a/src/go/api/cluster/files.go +++ b/src/go/api/cluster/files.go @@ -7,7 +7,6 @@ import ( "strings" "phenix/api/experiment" - "phenix/util" "phenix/util/mm" "phenix/util/mm/mmcli" ) @@ -30,7 +29,7 @@ type ImageDetails struct { } var DefaultClusterFiles ClusterFiles = new(MMClusterFiles) -var mmFilesDirectory = util.GetMMFilesDirectory() +var mmFilesDirectory = mm.GetMMFilesDirectory() type ClusterFiles interface { // Get list of VM disk images, container filesystems, or both. @@ -298,7 +297,7 @@ func getAllFiles(details map[string]ImageDetails) error { image := ImageDetails{ Name: baseName, - FullPath: util.GetMMFullPath(row["name"]), + FullPath: mm.GetMMFullPath(row["name"]), } if strings.HasSuffix(image.Name, ".qc2") || strings.HasSuffix(image.Name, ".qcow2") { @@ -367,7 +366,7 @@ func getTopologyFiles(expName string, details map[string]ImageDetails) error { image := ImageDetails{ Name: baseName, - FullPath: util.GetMMFullPath(row["name"]), + FullPath: mm.GetMMFullPath(row["name"]), Kind: VM_IMAGE, } diff --git a/src/go/api/vm/vm.go b/src/go/api/vm/vm.go index 457cb177..9d1267e6 100644 --- a/src/go/api/vm/vm.go +++ b/src/go/api/vm/vm.go @@ -15,7 +15,6 @@ import ( "time" "phenix/api/experiment" - "phenix/util" "phenix/util/common" "phenix/util/file" "phenix/util/mm" @@ -184,7 +183,7 @@ func Get(expName, vmName string) (*mm.VM, error) { Experiment: exp.Spec.ExperimentName(), CPUs: node.Hardware().VCPU(), RAM: node.Hardware().Memory(), - Disk: util.GetMMFullPath(node.Hardware().Drives()[0].Image()), + Disk: mm.GetMMFullPath(node.Hardware().Drives()[0].Image()), Interfaces: make(map[string]string), DoNotBoot: *node.General().DoNotBoot(), OSType: string(node.Hardware().OSType()), diff --git a/src/go/util/file.go b/src/go/util/file.go deleted file mode 100644 index 3021855a..00000000 --- a/src/go/util/file.go +++ /dev/null @@ -1,80 +0,0 @@ -package util - -import ( - "bufio" - "fmt" - "os/exec" - "path/filepath" - "regexp" - "strings" - - "phenix/util/common" -) - -var ( - filePathRe = regexp.MustCompile(`filepath=([^ ]+)`) - mmFilesDirectory = GetMMFilesDirectory() -) - -// Returns the full path relative to the minimega files directory -func GetMMFullPath(path string) string { - // If there is no leading file seperator, assume a relative - // path to the minimega files directory - if !strings.HasPrefix(path, "/") { - return filepath.Join(mmFilesDirectory, path) - } else { - return path - } - -} - -// Tries to extract the minimega files directory from a process listing -func GetMMFilesDirectory() string { - defaultMMFilesDirectory := fmt.Sprintf("%s/images", common.PhenixBase) - - cmd := "ps" - psPath, err := exec.LookPath(cmd) - - if err != nil { - return defaultMMFilesDirectory - } - - cmd = "grep" - grepPath, err := exec.LookPath(cmd) - - if err != nil { - return defaultMMFilesDirectory - } - - psCmd := exec.Command(psPath, "-au") - psStdout, _ := psCmd.StdoutPipe() - defer psStdout.Close() - - grepCmd := exec.Command(grepPath, "minimega") - grepCmd.Stdin = psStdout - - psCmd.Start() - - output, _ := grepCmd.Output() - - scanner := bufio.NewScanner(strings.NewReader(string(output))) - scanner.Split(bufio.ScanLines) - - // Try to find the minimega files directory - for scanner.Scan() { - - if !strings.Contains(scanner.Text(), "-filepath=") { - continue - } - - filesDirectory := filePathRe.FindAllStringSubmatch(scanner.Text(), -1) - if len(filesDirectory) == 0 { - return defaultMMFilesDirectory - } else { - return filesDirectory[0][1] - } - } - - return defaultMMFilesDirectory - -} diff --git a/src/go/util/mm/file.go b/src/go/util/mm/file.go new file mode 100644 index 00000000..d7ed7d02 --- /dev/null +++ b/src/go/util/mm/file.go @@ -0,0 +1,39 @@ +package mm + +import ( + "fmt" + "path/filepath" + "strings" + + "phenix/util/common" + "phenix/util/plog" +) + +var ( + mmFilesDirectory = GetMMFilesDirectory() +) + +// Returns the full path relative to the minimega files directory +func GetMMFullPath(path string) string { + // If there is no leading file separator, assume a relative + // path to the minimega files directory + if !strings.HasPrefix(path, "/") { + return filepath.Join(mmFilesDirectory, path) + } else { + return path + } + +} + +// Tries to extract the minimega files directory from a process listing +func GetMMFilesDirectory() string { + defaultMMFilesDirectory := fmt.Sprintf("%s/images", common.PhenixBase) + + path, ok := GetMMArgs()["filepath"] + if !ok { + plog.Warn("Could not get mm files directory from minimega") + return defaultMMFilesDirectory + } + + return path +} diff --git a/src/go/util/mm/minimega.go b/src/go/util/mm/minimega.go index f83e1b0e..5f403abc 100644 --- a/src/go/util/mm/minimega.go +++ b/src/go/util/mm/minimega.go @@ -816,6 +816,12 @@ func (this Minimega) IsHeadnode(node string) bool { return node == this.Headnode() } +func (this Minimega) GetMMArgs() map[string]string { + cmd := mmcli.NewCommand() + cmd.Command = "args" + return mmcli.RunTabular(cmd)[0] +} + func (Minimega) GetVLANs(opts ...Option) (map[string]int, error) { o := NewOptions(opts...) diff --git a/src/go/util/mm/mm.go b/src/go/util/mm/mm.go index 7f5fa3bd..4629e08d 100644 --- a/src/go/util/mm/mm.go +++ b/src/go/util/mm/mm.go @@ -37,6 +37,7 @@ type MM interface { GetNamespaceHosts(string) (Hosts, error) Headnode() string IsHeadnode(string) bool + GetMMArgs() map[string]string GetVLANs(...Option) (map[string]int, error) IsC2ClientActive(...C2Option) error diff --git a/src/go/util/mm/package.go b/src/go/util/mm/package.go index e5d84cb5..e790270b 100644 --- a/src/go/util/mm/package.go +++ b/src/go/util/mm/package.go @@ -108,6 +108,10 @@ func IsHeadnode(node string) bool { return DefaultMM.IsHeadnode(node) } +func GetMMArgs() map[string]string { + return DefaultMM.GetMMArgs() +} + func GetVLANs(opts ...Option) (map[string]int, error) { return DefaultMM.GetVLANs(opts...) } From 3ef9927800fa3ee78b30a37f246b29fa96cfdd35 Mon Sep 17 00:00:00 2001 From: jacdavi <86626873+jacdavi@users.noreply.github.com> Date: Mon, 19 Aug 2024 22:20:55 +0000 Subject: [PATCH 4/7] fix miniccc not working after restoring snapshot change to maintain uuid after restore --- src/go/api/vm/vm.go | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/go/api/vm/vm.go b/src/go/api/vm/vm.go index 9d1267e6..289461ba 100644 --- a/src/go/api/vm/vm.go +++ b/src/go/api/vm/vm.go @@ -19,6 +19,7 @@ import ( "phenix/util/file" "phenix/util/mm" "phenix/util/mm/mmcli" + "phenix/util/plog" "golang.org/x/sync/errgroup" ) @@ -873,53 +874,57 @@ func Restore(expName, vmName, snap string) error { snap = fmt.Sprintf("%s/files/%s", expName, snap) + details := mm.GetVMInfo(mm.NS(expName), mm.VMName(vmName)) + if len(details) == 0 { + return fmt.Errorf("error getting vm details") + } + cmd := mmcli.NewNamespacedCommand(expName) cmd.Command = fmt.Sprintf("vm config clone %s", vmName) - if err := mmcli.ErrorResponse(mmcli.Run(cmd)); err != nil { return fmt.Errorf("cloning config for VM %s: %w", vmName, err) } - cmd.Command = fmt.Sprintf("vm config migrate %s.SNAP", snap) + // Have to copy over UUID separate from clone. + // Needs to stay the same for miniccc agent to connect + plog.Info("Setting UUID", "uuid", details[0].UUID) + cmd.Command = fmt.Sprintf("vm config uuid %s", details[0].UUID) + if err := mmcli.ErrorResponse(mmcli.Run(cmd)); err != nil { + return fmt.Errorf("setting uuid for VM %s: %w", vmName, err) + } + cmd.Command = fmt.Sprintf("vm config migrate %s.SNAP", snap) if err := mmcli.ErrorResponse(mmcli.Run(cmd)); err != nil { return fmt.Errorf("configuring migrate file for VM %s: %w", vmName, err) } cmd.Command = fmt.Sprintf("vm config disk %s.qc2,writeback", snap) - if err := mmcli.ErrorResponse(mmcli.Run(cmd)); err != nil { return fmt.Errorf("configuring disk file for VM %s: %w", vmName, err) } cmd.Command = fmt.Sprintf("vm kill %s", vmName) - if err := mmcli.ErrorResponse(mmcli.Run(cmd)); err != nil { return fmt.Errorf("killing VM %s: %w", vmName, err) } - // TODO: explicitly flush killed VM by name once we start using that version - // of minimega. - cmd.Command = "vm flush" + cmd.Command = fmt.Sprintf("vm flush %s", vmName) if err := mmcli.ErrorResponse(mmcli.Run(cmd)); err != nil { return fmt.Errorf("flushing VMs: %w", err) } cmd.Command = fmt.Sprintf("vm launch kvm %s", vmName) - if err := mmcli.ErrorResponse(mmcli.Run(cmd)); err != nil { return fmt.Errorf("relaunching VM %s: %w", vmName, err) } cmd.Command = "vm launch" - if err := mmcli.ErrorResponse(mmcli.Run(cmd)); err != nil { return fmt.Errorf("scheduling VM %s: %w", vmName, err) } cmd.Command = fmt.Sprintf("vm start %s", vmName) - if err := mmcli.ErrorResponse(mmcli.Run(cmd)); err != nil { return fmt.Errorf("starting VM %s: %w", vmName, err) } From 9de4018c7099d74d499693dda116dbce9ced6ce0 Mon Sep 17 00:00:00 2001 From: jacdavi <86626873+jacdavi@users.noreply.github.com> Date: Fri, 12 Jul 2024 23:53:32 +0000 Subject: [PATCH 5/7] return available files in GetMountFiles alongside any error When Readdir returns an error, it may still have a list of files read up until that point. Previously we returned early if there was an error, rather than returning those files. This was prevalent when looking in certain directories such as "/etc" of an Ubuntu desktop. Output should not match results of "ls -la" in mounted directory --- src/go/web/mount.go | 19 ++++++++----------- src/go/web/rbac/known_policy.go | 2 +- src/js/src/components/RunningExperiment.vue | 1 + src/js/src/components/VMMountBrowserModal.vue | 9 ++++++--- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/go/web/mount.go b/src/go/web/mount.go index 8cc32885..23b10f16 100644 --- a/src/go/web/mount.go +++ b/src/go/web/mount.go @@ -17,7 +17,6 @@ import ( "phenix/util/mm" "phenix/util/plog" "phenix/web/rbac" - "phenix/web/util" "github.com/gorilla/mux" ) @@ -121,6 +120,7 @@ func UnmountVM(w http.ResponseWriter, r *http.Request) { } // GET /experiments/{exp}/vms/{name}/mount/files?path= +// Note: error may be returned inside json body as Readdir can return an error with entries func GetMountFiles(w http.ResponseWriter, r *http.Request) { var ( vars = mux.Vars(r) @@ -202,15 +202,6 @@ func GetMountFiles(w http.ResponseWriter, r *http.Request) { select { case <-done: - if err != nil { - errString := fmt.Sprintf("Error getting files in %s: %v", combinedPath, err) - - plog.Error(errString) - http.Error(w, errString, http.StatusInternalServerError) - - return - } - var files file.Files for _, e := range dirEntries { @@ -222,7 +213,13 @@ func GetMountFiles(w http.ResponseWriter, r *http.Request) { files = append(files, file) } - body, _ := json.Marshal(util.WithRoot("files", files)) + resp := map[string]any {"error": "", "files": files } + if err != nil { + plog.Error(fmt.Sprintf("Error getting files in %s. Still read %d entries: %v", combinedPath, len(dirEntries), err)) + resp["error"] = strings.Replace(fmt.Sprintf("%v", err), basePath, "", -1) + } + + body, _ := json.Marshal(resp) w.Write(body) case <-time.After(2 * time.Second): err := fmt.Sprintf("timeout getting files in %s", combinedPath) diff --git a/src/go/web/rbac/known_policy.go b/src/go/web/rbac/known_policy.go index b1fa8767..b62fc25a 100644 --- a/src/go/web/rbac/known_policy.go +++ b/src/go/web/rbac/known_policy.go @@ -1,5 +1,5 @@ // Code generated by go generate; DO NOT EDIT. -// This file was generated at build time 2024-06-14 09:29:39.356147531 -0600 MDT m=+0.125261219 +// This file was generated at build time 2024-08-26 18:54:07.214583704 +0000 UTC m=+1.058053131 // This contains all known role checks used in codebase package rbac diff --git a/src/js/src/components/RunningExperiment.vue b/src/js/src/components/RunningExperiment.vue index 4d46daec..b360ae29 100644 --- a/src/js/src/components/RunningExperiment.vue +++ b/src/js/src/components/RunningExperiment.vue @@ -552,6 +552,7 @@

+ diff --git a/src/js/src/components/VMMountBrowserModal.vue b/src/js/src/components/VMMountBrowserModal.vue index 0acf609d..d4848d34 100644 --- a/src/js/src/components/VMMountBrowserModal.vue +++ b/src/js/src/components/VMMountBrowserModal.vue @@ -27,6 +27,7 @@
No Files in Directory
+

{{ error }}