Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User Annotation Improvements #182

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions src/go/api/soh/soh.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ func Get(expName, statusFilter string) (*Network, error) {
// Create an empty network
network := new(Network)

// Create structure to format nodes' font
font := Font{
Color: "whitesmoke",
Align: "center",
}

exp, err := experiment.Get(expName)
if err != nil {
return nil, fmt.Errorf("unable to get experiment %s: %w", expName, err)
Expand Down Expand Up @@ -107,7 +101,7 @@ func Get(expName, statusFilter string) (*Network, error) {
ID: vm.ID,
Label: vm.Name,
Image: vm.OSType,
Fonts: font,
Tags: vm.Tags,
Status: vmState,
}

Expand Down Expand Up @@ -140,7 +134,7 @@ func Get(expName, statusFilter string) (*Network, error) {
ID: ifaceCount,
Label: vmIface,
Image: "switch",
Fonts: font,
Tags: vm.Tags,
Status: "ignore",
}

Expand Down
17 changes: 6 additions & 11 deletions src/go/api/soh/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,13 @@ import (
"time"
)

type Font struct {
Color string `json:"color"`
Align string `json:"align"`
}

type Node struct {
ID int `json:"id"`
Label string `json:"label"`
Image string `json:"image"`
Fonts Font `json:"font"`
Status string `json:"status"`
SOH *HostState `json:"soh"`
ID int `json:"id"`
Label string `json:"label"`
Image string `json:"image"`
Tags map[string]string `json:"tags"`
Status string `json:"status"`
SOH *HostState `json:"soh"`
}

type Edge struct {
Expand Down
33 changes: 21 additions & 12 deletions src/go/api/vm/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ type iface struct {
}

type updateOptions struct {
exp string
vm string
cpu int
mem int
disk string
partition int
dnb *bool
iface *iface
host *string
snapshot *bool
exp string
vm string
cpu int
mem int
disk string
partition int
dnb *bool
iface *iface
host *string
snapshot *bool
appendTags bool
tags *map[string]string
}

func newUpdateOptions(opts ...UpdateOption) updateOptions {
Expand Down Expand Up @@ -78,15 +80,22 @@ func UpdateWithDNB(b bool) UpdateOption {
}
}

func UpdateWithHost(h string) UpdateOption {
return func(o *updateOptions) {
o.host = &h
}
}

func UpdateWithSnapshot(b bool) UpdateOption {
return func(o *updateOptions) {
o.snapshot = &b
}
}

func UpdateWithHost(h string) UpdateOption {
func UpdateWithTags(t map[string]string, appendTags bool) UpdateOption {
return func(o *updateOptions) {
o.host = &h
o.appendTags = appendTags
o.tags = &t
}
}

Expand Down
90 changes: 62 additions & 28 deletions src/go/api/vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ func List(expName string) ([]mm.VM, error) {
Type: node.Type(),
OSType: node.Hardware().OSType(),
Snapshot: snapshot,
Tags: node.Labels(),
}

for _, iface := range node.Network().Interfaces() {
Expand Down Expand Up @@ -197,10 +198,11 @@ func Get(expName, vmName string) (*mm.VM, error) {
Interfaces: make(map[string]string),
DoNotBoot: *node.General().DoNotBoot(),
OSType: string(node.Hardware().OSType()),
Snapshot: *node.General().Snapshot(),
Metadata: make(map[string]interface{}),
Labels: node.Labels(),
Tags: node.Labels(),
Annotations: node.Annotations(),
Snapshot: *node.General().Snapshot(),
}

for _, iface := range node.Network().Interfaces() {
Expand Down Expand Up @@ -284,22 +286,6 @@ func Update(opts ...UpdateOption) error {
return fmt.Errorf("experiment or VM name not provided")
}

running := experiment.Running(o.exp)

if running && o.iface == nil {
return fmt.Errorf("only interface connections can be updated while experiment is running")
}

// The only setting that can be updated while an experiment is running is the
// VLAN an interface is connected to.
if running {
if o.iface.vlan == "" {
return Disonnect(o.exp, o.vm, o.iface.index)
} else {
return Connect(o.exp, o.vm, o.iface.index, o.iface.vlan)
}
}

exp, err := experiment.Get(o.exp)
if err != nil {
return fmt.Errorf("unable to get experiment %s: %w", o.exp, err)
Expand All @@ -310,6 +296,50 @@ func Update(opts ...UpdateOption) error {
return fmt.Errorf("unable to find VM %s in experiment %s", o.vm, o.exp)
}

// if appending, copy over old labels (keep newer version if present)
if o.tags != nil && o.appendTags {
for k, v := range vm.Labels() {
if _, ok := (*o.tags)[k]; !ok {
(*o.tags)[k] = v
}
}
}

running := experiment.Running(o.exp)

// The only settings that can be updated while an experiment is running is the
// VLAN an interface is connected to and the vm's tags
if running {
if o.iface == nil && o.tags == nil {
return fmt.Errorf("only interface connections and tags can be updated while experiment is running")
}

if o.iface != nil {
if o.iface.vlan == "" {
if err := Disonnect(o.exp, o.vm, o.iface.index); err != nil {
return err
}
} else {
if err := Connect(o.exp, o.vm, o.iface.index, o.iface.vlan); err != nil {
return err
}
}
}

if o.tags != nil {
// update both the live minimega tags and the experiment spec labels
if err := mm.SetVMTags(mm.NS(o.exp), mm.VMName(o.vm), mm.Tags(*o.tags)); err != nil {
return err
}

vm.SetLabels(*o.tags)
if err := experiment.Save(experiment.SaveWithName(o.exp), experiment.SaveWithSpec(exp.Spec)); err != nil {
return fmt.Errorf("unable to save experiment with updated VM: %w", err)
}
}
return nil
}

if o.cpu != 0 {
vm.Hardware().SetVCPU(o.cpu)
}
Expand All @@ -330,6 +360,10 @@ func Update(opts ...UpdateOption) error {
vm.General().SetDoNotBoot(*o.dnb)
}

if o.tags != nil {
vm.SetLabels(*o.tags)
}

if o.host != nil {
if *o.host == "" {
delete(exp.Spec.Schedules(), o.vm)
Expand Down Expand Up @@ -396,7 +430,7 @@ func Restart(expName, vmName string) error {
state, err := mm.GetVMState(mm.NS(expName), mm.VMName(vmName))

if err != nil {
return fmt.Errorf("Retrieving state for VM %s in experiment %s: %w", vmName, expName, err)
return fmt.Errorf("retrieving state for VM %s in experiment %s: %w", vmName, expName, err)
}

//Using "system_reset" on a VM that is in the "QUIT" state fails
Expand All @@ -406,7 +440,7 @@ func Restart(expName, vmName string) error {
}

cmd := mmcli.NewNamespacedCommand(expName)
qmp := fmt.Sprintf(`{ "execute": "system_reset" }`)
qmp := `{ "execute": "system_reset" }`
cmd.Command = fmt.Sprintf("vm qmp %s '%s'", vmName, qmp)

_, err = mmcli.SingleResponse(mmcli.Run(cmd))
Expand Down Expand Up @@ -541,7 +575,7 @@ func ResetDiskState(expName, vmName string) error {
cmd.Command = "vm kill " + vmName

if err := mmcli.ErrorResponse(mmcli.Run(cmd)); err != nil {
return fmt.Errorf("Killing VM %s in experiment %s: %w", vmName, expName, err)
return fmt.Errorf("killing VM %s in experiment %s: %w", vmName, expName, err)
}

}
Expand Down Expand Up @@ -724,7 +758,7 @@ func Snapshot(expName, vmName, out string, cb func(string)) error {
fp = fmt.Sprintf("%s/%s", common.MinimegaBase, status[0]["id"])
)

qmp := fmt.Sprintf(`{ "execute": "query-block" }`)
qmp := `{ "execute": "query-block" }`
cmd.Command = fmt.Sprintf("vm qmp %s '%s'", vmName, qmp)

res, err := mmcli.SingleResponse(mmcli.Run(cmd))
Expand Down Expand Up @@ -755,7 +789,7 @@ func Snapshot(expName, vmName, out string, cb func(string)) error {
return fmt.Errorf("starting disk snapshot for VM %s: %w", vmName, err)
}

qmp = fmt.Sprintf(`{ "execute": "query-block-jobs" }`)
qmp = `{ "execute": "query-block-jobs" }`
cmd.Command = fmt.Sprintf(`vm qmp %s '%s'`, vmName, qmp)

for {
Expand Down Expand Up @@ -1248,7 +1282,7 @@ func MemorySnapshot(expName, vmName, out string, cb func(string)) (string, error

}

qmp = fmt.Sprintf(`{ "execute": "query-dump" }`)
qmp = `{ "execute": "query-dump" }`
cmd.Command = fmt.Sprintf("vm qmp %s '%s'", vmName, qmp)

var (
Expand All @@ -1274,15 +1308,15 @@ func MemorySnapshot(expName, vmName, out string, cb func(string)) (string, error
if cb != nil {
cb("failed")
}
return "", fmt.Errorf("no status available for %s: %s", vmName, v)
return "", fmt.Errorf("no status available for %s: %v", vmName, v)

}

if v.Return.Status == "failed" {
if cb != nil {
cb("failed")
}
return "failed", fmt.Errorf("failed to create memory snapshot for %s: %s", vmName, v)
return "failed", fmt.Errorf("failed to create memory snapshot for %s: %v", vmName, v)

}

Expand Down Expand Up @@ -1334,13 +1368,13 @@ func CaptureSubnet(expName, subnet string, vmList []string) ([]mm.Capture, error
vms, err := List(expName)

if err != nil {
return nil, fmt.Errorf("Getting vm list for %s failed", expName)
return nil, fmt.Errorf("getting vm list for %s failed", expName)
}

_, refNet, err := net.ParseCIDR(subnet)

if err != nil {
return nil, fmt.Errorf("Unable to parse %s", subnet)
return nil, fmt.Errorf("unable to parse %s", subnet)
}

// Use empty struct for code consistency and
Expand Down Expand Up @@ -1439,7 +1473,7 @@ func StopCaptureSubnet(expName, subnet string, vmList []string) ([]string, error
vms, err := List(expName)

if err != nil {
return nil, fmt.Errorf("Getting vm list for %s failed", expName)
return nil, fmt.Errorf("getting vm list for %s failed", expName)
}

_, refNet, err := net.ParseCIDR(subnet)
Expand Down
2 changes: 1 addition & 1 deletion src/go/tmpl/templates/minimega_script.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ vm config {{ $config }} {{ $value }}
vm config qemu-override "{{ $match }}" "{{ $replacement }}"
{{- end }}
{{- range $label, $value := .Labels }}
vm config tags {{ $label }} {{ $value }}
vm config tags "{{ $label }}" "{{ escapeNewline $value }}"
{{- end }}
vm launch {{ .General.VMType }} {{ .General.Hostname }}
{{- end }}
Expand Down
3 changes: 3 additions & 0 deletions src/go/tmpl/tmpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ func GenerateFromTemplate(name string, data interface{}, w io.Writer) error {
"stringsJoin": func(s []string, sep string) string {
return strings.Join(s, sep)
},
"escapeNewline": func(s string) string {
return strings.ReplaceAll(s, "\n", "\\n")
},
}

tmpl := template.Must(template.New(name).Funcs(funcs).Parse(string(MustAsset(name))))
Expand Down
1 change: 1 addition & 0 deletions src/go/types/interfaces/topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type NodeSpec interface {

SetInjections([]NodeInjection)

SetLabels(map[string]string)
AddLabel(string, string)
AddHardware(string, int, int) NodeHardware
AddNetworkInterface(string, string, string) NodeNetworkInterface
Expand Down
4 changes: 4 additions & 0 deletions src/go/types/version/v0/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ func (this *Node) SetInjections(injections []ifaces.NodeInjection) {
this.InjectionsF = injects
}

func (this *Node) SetLabels(m map[string]string) {
this.LabelsF = m
}

func (this *Node) AddLabel(k, v string) {
this.LabelsF[k] = v
}
Expand Down
4 changes: 4 additions & 0 deletions src/go/types/version/v1/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ func (this *Node) SetInjections(injections []ifaces.NodeInjection) {
this.InjectionsF = injects
}

func (this *Node) SetLabels(m map[string]string) {
this.LabelsF = m
}

func (this *Node) AddLabel(k, v string) {
if this.LabelsF == nil {
this.LabelsF = make(map[string]string)
Expand Down
Loading