/
vmconfig_cli.go
308 lines (235 loc) · 9.06 KB
/
vmconfig_cli.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
// Copyright 2015-2021 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government retains certain
// rights in this software.
package main
import (
"errors"
"fmt"
"minicli"
"strconv"
)
// vmconfigCLIHandlers are special cases that are not worth generating via
// vmconfiger.
var vmconfigCLIHandlers = []minicli.Handler{
{ // vm config
HelpShort: "display, save, or restore the current VM configuration",
HelpLong: `
Display, save, or restore the current VM configuration. Note that saving and
restoring configuration applies to all VM configurations including KVM-based VM
configurations.
To display the current configuration, call vm config with no arguments.
List the current saved configurations with 'vm config restore'.
To save a configuration:
vm config save <config name>
To restore a configuration:
vm config restore <config name>
To clone the configuration of an existing VM:
vm config clone <vm name>
Clone reparses the original network "vm config net". If the cloned VM was
configured with a static MAC, the VM config will not be launchable. Clone also
clears the UUID.
Calling clear vm config will clear all VM configuration options, but will not
remove saved configurations.`,
Patterns: []string{
"vm config",
"vm config <save,> <name>",
"vm config <restore,> [name]",
"vm config <clone,> <vm name>",
},
Suggest: wrapVMSuggest(VM_ANY_STATE, false),
Call: wrapSimpleCLI(cliVMConfig),
},
{ // vm config disk
HelpShort: "specify disks for VM",
HelpLong: `Specify one or more disks to be connected to a VM. Any disk image supported by QEMU is a valid parameter.
Optionally, you may specify the drive interface for QEMU to use. By default,
"ide" is used. Supported interfaces are "ahci", "ide", "scsi", "sd", "mtd",
"floppy", "pflash", and "virtio".
Optionally, you may specify the cache mode to be used by the drive. By default,
"unsafe" is used for vms launched in snapshot mode, and "writeback" is used
otherwise. Supported cache modes are "none", "writeback", "unsafe",
"directsync", and "writethrough".
Note: although disk snapshot image files are saved in the temporary vm instance
paths, they may not be usable if the "unsafe" cache mode is used, as all flush
commands from the guest are ignored in that cache mode. For example, even if
you shut down the guest cleanly, there may still be data not yet written to the
snapshot image file. If you wish to copy and use the snapshot image file
cleanly, you can flush the disk cache manually via the QMP command socket, or
specify a different cache mode such as "writeback".
The order is:
<path>,<interface>,<cache mode>
Examples:
To attach a disk with the default interface and cache mode:
vm config disk linux_disk.qcow2
To attach 2 disks using the "ide" interface for the first disk and default
interface for the second disk:
vm config disk linux_disk.qcow2,ide storage_disk.qcow2
To attach a disk using the "ide" interface with the "unsafe" cache mode:
vm config disk linux_disk.qcow2,ide,unsafe
Disk images launched in snapshot mode may safely be used for multiple VMs.
Calling vm config disks with no arguments prints the current configuration.
Note: this configuration only applies to KVM-based VMs.`,
Patterns: []string{
"vm config disks [diskspec]...",
},
Call: wrapSimpleCLI(cliVMConfigDisk),
},
{ // vm config net
HelpShort: "specify network interfaces for VM",
HelpLong: `
Specify the network(s) that the VM is a member of by VLAN. A corresponding VLAN
will be created for each network. Optionally, you may specify the bridge the
interface will be connected on. If the bridge name is omitted, minimega will
use the default "mega_bridge".
You can also optionally specify the MAC address of the interface to connect to
that network. If not specifed, the MAC address will be randomly generated.
Finally, you can also optionally specify a network device for qemu to use
(which is ignored by containers). By default, "e1000" is used. To see a list of
valid network devices, from run "qemu-kvm -device help".
The order is:
<bridge>,<VLAN>,<MAC>,<driver>
Examples:
To connect a VM to VLANs 1 and 5:
vm config net 1 5
To connect a VM to VLANs 100, 101, and 102 with specific mac addresses:
vm config net 100,00:00:00:00:00:00 101,00:00:00:00:01:00 102,00:00:00:00:02:00
To connect a VM to VLAN 1 on bridge0 and VLAN 2 on bridge1:
vm config net bridge0,1 bridge1,2
To connect a VM to VLAN 100 on bridge0 with a specific mac:
vm config net bridge0,100,00:11:22:33:44:55
To specify a specific driver, such as i82559c:
vm config net 100,i82559c
If you prefer, you can also use aliases for VLANs:
vm config net DMZ CORE
These aliases will be allocated from the pool of available VLANs and is
namespace-aware (i.e. 'DMZ' in namespace 'foo' will be a different VLAN than
'DMZ' in namespace 'bar'). Internally, this is implemented by concatenating the
namespace name with the VLAN alias (e.g. 'DMZ' in namespace 'foo' becomes
'foo//DMZ'). If you wish to connect VLANs in different namespaces, you may
use/abuse this implementation detail:
namespace bar
vm config net foo//DMZ
Calling vm config net with no arguments prints the current configuration.`,
Patterns: []string{
"vm config networks [netspec]...",
},
Call: wrapSimpleCLI(cliVMConfigNet),
},
{ // vm config qemu-override
HelpShort: "override parts of the QEMU launch string",
HelpLong: `
Override parts of the QEMU launch string by supplying a string to match, and a
replacement string. Overrides are applied in the order that they are defined
and do not replace earlier overrides -- if more than override share the same
"match" will later overrides will be applied to the overridden launch string.
Note: this configuration only applies to KVM-based VMs.`,
Patterns: []string{
"vm config qemu-override",
"vm config qemu-override <match> <replacement>",
},
Call: wrapSimpleCLI(cliVMConfigQemuOverride),
},
{ // clear vm config tag
HelpShort: "remove tags for newly launched VMs",
HelpLong: `
Remove tags in the same manner as "clear vm tag".`,
Patterns: []string{
"clear vm config tag <key>",
},
Call: wrapSimpleCLI(cliClearVMConfigTag),
},
}
func cliVMConfig(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
if c.BoolArgs["save"] {
// Save the current config
ns.savedVMConfig[c.StringArgs["name"]] = ns.vmConfig.Copy()
return nil
} else if c.BoolArgs["restore"] {
if name, ok := c.StringArgs["name"]; ok {
// Try to restore an existing config
if _, ok := ns.savedVMConfig[name]; !ok {
return fmt.Errorf("config %v does not exist", name)
}
ns.vmConfig = ns.savedVMConfig[name].Copy()
return nil
} else if len(ns.savedVMConfig) == 0 {
return errors.New("no vm configs saved")
}
// List the save configs
for k := range ns.savedVMConfig {
resp.Response += fmt.Sprintln(k)
}
return nil
} else if c.BoolArgs["clone"] {
// Clone the config of an existing vm, search across the namespace
name := c.StringArgs["vm"]
id, err := strconv.Atoi(name)
var found VM
for _, vm := range globalVMs(ns) {
if err == nil && id == vm.GetID() {
found = vm
} else if name == vm.GetName() {
found = vm
}
}
if found == nil {
return vmNotFound(name)
}
switch vm := found.(type) {
case *KvmVM:
ns.vmConfig.BaseConfig = vm.BaseConfig.Copy()
ns.vmConfig.KVMConfig = vm.KVMConfig.Copy()
// Clear SnapshotPaths since we can't launch VMs with the same SnapshotPath
for i, _ := range ns.vmConfig.KVMConfig.Disks {
ns.vmConfig.KVMConfig.Disks[i].SnapshotPath = ""
}
case *ContainerVM:
ns.vmConfig.BaseConfig = vm.BaseConfig.Copy()
ns.vmConfig.ContainerConfig = vm.ContainerConfig.Copy()
}
// clear UUID since we can't launch VMs with the same UUID
ns.vmConfig.UUID = ""
// reprocess the network configs from their original input
vals := []string{}
for _, nic := range ns.vmConfig.Networks {
vals = append(vals, nic.Raw)
}
return ns.processVMNets(vals)
}
// Print the config
resp.Response = ns.vmConfig.String(ns.Name)
return nil
}
func cliVMConfigDisk(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
if len(c.ListArgs) == 0 {
resp.Response = ns.vmConfig.DiskString(ns.Name)
return nil
}
return ns.processVMDisks(c.ListArgs["diskspec"])
}
func cliVMConfigNet(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
if len(c.ListArgs) == 0 {
resp.Response = ns.vmConfig.NetworkString(ns.Name)
return nil
}
return ns.processVMNets(c.ListArgs["netspec"])
}
func cliVMConfigQemuOverride(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
if len(c.StringArgs) == 0 {
resp.Response = ns.vmConfig.qemuOverrideString()
return nil
}
ns.vmConfig.QemuOverride = append(ns.vmConfig.QemuOverride, qemuOverride{
Match: c.StringArgs["match"],
Repl: c.StringArgs["replacement"],
})
return nil
}
func cliClearVMConfigTag(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
if k := c.StringArgs["key"]; k == Wildcard {
ns.vmConfig.Tags = nil
} else {
delete(ns.vmConfig.Tags, k)
}
return nil
}