From f1f7c4e10c66573fe0c0686cc9eaa46f1ef47370 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Sat, 23 Mar 2019 22:02:36 +0100 Subject: [PATCH] * added function ParseSnapshotData to parse a string representation of a VBox snapshot tree * fixed bugs --- builder/virtualbox/common/snapshot.go | 74 +++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/builder/virtualbox/common/snapshot.go b/builder/virtualbox/common/snapshot.go index cd5449f78..cc7a3499d 100644 --- a/builder/virtualbox/common/snapshot.go +++ b/builder/virtualbox/common/snapshot.go @@ -1,7 +1,12 @@ package common import ( + "bufio" + "log" + "regexp" "strings" + + "github.com/golang-collections/collections/stack" ) // VBoxSnapshot stores the hierarchy of snapshots for a VM instance @@ -13,6 +18,66 @@ type VBoxSnapshot struct { Children []*VBoxSnapshot } +// ParseSnapshotData parses the machinereadable representation of a virtualbox snapshot tree +func ParseSnapshotData(snapshotData string) (*VBoxSnapshot, error) { + scanner := bufio.NewScanner(strings.NewReader(snapshotData)) + SnapshotNamePartsRe := regexp.MustCompile("Snapshot(?PName|UUID)(?P(-[1-9]+)*)=\"(?P[^\"]*)\"") + var currentIndicator string + parentStack := stack.New() + var node *VBoxSnapshot + var rootNode *VBoxSnapshot + + for scanner.Scan() { + txt := scanner.Text() + idx := strings.Index(txt, "=") + if idx > 0 { + if strings.HasPrefix(txt, "Current") { + node.IsCurrent = true + } else { + matches := SnapshotNamePartsRe.FindStringSubmatch(txt) + log.Printf("************ Snapshot %s name parts", txt) + log.Printf("Matches %#v\n", matches) + log.Printf("Node %s\n", matches[0]) + log.Printf("Type %s\n", matches[1]) + log.Printf("Path %s\n", matches[2]) + log.Printf("Leaf %s\n", matches[3]) + log.Printf("Value %s\n", matches[4]) + if matches[1] == "Name" { + if nil == rootNode { + node = new(VBoxSnapshot) + rootNode = node + currentIndicator = matches[2] + } else { + pathLenCur := strings.Count(currentIndicator, "-") + pathLen := strings.Count(matches[2], "-") + if pathLen > pathLenCur { + currentIndicator = matches[2] + parentStack.Push(node) + } else if pathLen < pathLenCur { + currentIndicator = matches[2] + for i := 0; i < pathLenCur-1; i++ { + parentStack.Pop() + } + } + node = new(VBoxSnapshot) + parent := parentStack.Peek().(*VBoxSnapshot) + if nil != parent { + node.Parent = parent + parent.Children = append(parent.Children, node) + } + } + node.Name = matches[4] + } else if matches[1] == "UUID" { + node.UUID = matches[4] + } + } + } else { + log.Printf("Invalid key,value pair [%s]", txt) + } + } + return rootNode, nil +} + // IsChildOf verifies if the current snaphot is a child of the passed as argument func (sn *VBoxSnapshot) IsChildOf(candidate *VBoxSnapshot) bool { if nil == candidate { @@ -37,9 +102,8 @@ func walk(sn *VBoxSnapshot, ch chan *VBoxSnapshot) { for _, child := range sn.Children { walk(child, ch) } - } else { - ch <- sn } + ch <- sn } func walker(sn *VBoxSnapshot) <-chan *VBoxSnapshot { @@ -76,7 +140,7 @@ func (sn *VBoxSnapshot) GetSnapshotsByName(name string) []*VBoxSnapshot { for { node, ok := <-ch if !ok { - panic("Internal channel error while traversing the snapshot tree") + break } if strings.EqualFold(node.Name, name) { result = append(result, node) @@ -92,7 +156,7 @@ func (sn *VBoxSnapshot) GetSnapshotByUUID(uuid string) *VBoxSnapshot { for { node, ok := <-ch if !ok { - panic("Internal channel error while traversing the snapshot tree") + break } if strings.EqualFold(node.UUID, uuid) { return node @@ -108,7 +172,7 @@ func (sn *VBoxSnapshot) GetCurrentSnapshot() *VBoxSnapshot { for { node, ok := <-ch if !ok { - panic("Internal channel error while traversing the snapshot tree") + break } if node.IsCurrent { return node