Skip to content

Commit d846d68

Browse files
Merge pull request #3136 from Nordix/nuhakala/vbmctl-setup-network
✨ Add libvirt network creation and deletion to vbmctl
2 parents 88dd929 + a673ef4 commit d846d68

9 files changed

Lines changed: 388 additions & 27 deletions

File tree

hack/ci-e2e.sh

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,15 @@ sudo sysctl fs.inotify.max_user_instances=8192
6969
# Build the container image with e2e tag (used in tests)
7070
IMG=quay.io/metal3-io/baremetal-operator IMG_TAG=e2e make docker
7171

72-
if ! sudo virsh net-list --all | grep baremetal-e2e; then
73-
virsh -c qemu:///system net-define "${REPO_ROOT}/hack/e2e/net.xml"
74-
virsh -c qemu:///system net-start baremetal-e2e
75-
fi
72+
# Build vbmctl
73+
make build-vbmctl
74+
# Create VMs to act as BMHs in the tests and the libvirt network
75+
./bin/vbmctl -c "${REPO_ROOT}/test/e2e/config/vbmctl.yaml" create bml
7676

77-
# We need to create veth pair to connect metal3 net (defined above) and kind
78-
# docker subnet. Let us start by creating a docker network with pre-defined
79-
# name for bridge, so that we can configure the veth pair correctly.
80-
# Also assume that if kind net exists, it is created by us.
77+
# We need to create veth pair to connect metal3 net (defined above with vbmctl)
78+
# and kind docker subnet. Let us start by creating a docker network with
79+
# pre-defined name for bridge, so that we can configure the veth pair
80+
# correctly. Also assume that if kind net exists, it is created by us.
8181
if ! docker network list | grep kind; then
8282
# These options are used by kind itself. It uses docker default mtu and
8383
# generates ipv6 subnet ULA, but we can fix the ULA. Only addition to kind
@@ -112,11 +112,6 @@ sudo iptables -L FORWARD -n -v
112112
# server endpoint, not ironic.
113113
IP_ADDRESS="192.168.222.1"
114114

115-
# Build vbmctl
116-
make build-vbmctl
117-
# Create VMs to act as BMHs in the tests.
118-
./bin/vbmctl -c "${REPO_ROOT}/test/e2e/config/vbmctl.yaml" create bml
119-
120115
if [[ "${BMO_E2E_EMULATOR}" == "vbmc" ]]; then
121116
# Start VBMC
122117
docker start vbmc || docker run --name vbmc --network host -d \

test/e2e/config/vbmctl.yaml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ spec:
1515
size: 20
1616
- name: "2"
1717
size: 20
18-
networks:
18+
networkAttachments:
1919
- network: baremetal-e2e
2020
macAddress: "00:60:2f:31:81:01"
2121
- name: bmo-e2e-1
@@ -26,6 +26,11 @@ spec:
2626
size: 20
2727
- name: "2"
2828
size: 20
29-
networks:
29+
networkAttachments:
3030
- network: baremetal-e2e
3131
macAddress: "00:60:2f:31:81:02"
32+
networks:
33+
- name: baremetal-e2e
34+
bridge: metal3
35+
address: 192.168.222.1
36+
netmask: 255.255.255.0

test/vbmctl/README.md

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ This tool is under active development.
1717
| `vbmctl create bml` / `vbmctl delete bml` | ✅ Implemented |
1818
| `vbmctl status` | ✅ Implemented (basic) |
1919
| Configurable volumes | ❌ TODO (hard-coded to two per VM) |
20-
| Network management | ❌ TODO (uses existing libvirt networks) |
20+
| Network management | ⚠️ Partially implemented (only libvirt networks) |
2121
| BMC emulator support | ❌ TODO |
2222
| Image server | ❌ TODO |
2323
| State management (persistent state) | ❌ TODO |
@@ -29,6 +29,7 @@ This tool is under active development.
2929
- **DHCP Reservation**: Reserve IP addresses for VMs via DHCP on existing
3030
libvirt networks
3131
- **Library Support**: Can be imported as a Go module for programmatic use
32+
- **Libvirt network management**: create and delete libvirt networks
3233

3334
## Build Tags
3435

@@ -60,6 +61,9 @@ vbmctl config view
6061
# Create a single virtual machine
6162
vbmctl create vm --name test-vm --memory 4096 --vcpus 2
6263

64+
# Create network with default values (default name: baremetal-e2e)
65+
vbmctl create network
66+
6367
# Create VM with custom options
6468
vbmctl create vm \
6569
--name bmo-e2e-0 \
@@ -70,7 +74,7 @@ vbmctl create vm \
7074
--mac-address 00:60:2f:31:81:01 \
7175
--ip-address 192.168.222.100
7276

73-
# Create a bare metal lab (all VMs defined in spec.vms of the config file)
77+
# Create a bare metal lab (all VMs and networks defined in spec.vms of the config file)
7478
vbmctl create bml
7579

7680
# Check status
@@ -82,6 +86,9 @@ vbmctl delete vm test-vm
8286
# Delete the bare metal lab (all VMs defined in spec.vms of the config file)
8387
vbmctl delete bml
8488

89+
# Create network with default values
90+
vbmctl delete network
91+
8592
# Show help
8693
vbmctl --help
8794
```
@@ -141,10 +148,15 @@ spec:
141148
size: 20
142149
- name: "data"
143150
size: 10
144-
networks:
151+
networkAttachments:
145152
- network: "baremetal-e2e"
146153
macAddress: "00:60:2f:31:81:02"
147154
ipAddress: "192.168.222.101"
155+
networks:
156+
- name: baremetal-e2e
157+
bridge: metal3
158+
address: 192.168.222.1
159+
netmask: 255.255.255.0
148160
```
149161
150162
The `spec.vms` section defines the VMs that will be created when you run `vbmctl
@@ -176,6 +188,23 @@ func main() {
176188
}
177189
defer conn.Close()
178190
191+
// Create network manager
192+
networkManager, err := libvirt.NewNetworkManager(conn)
193+
if err != nil {
194+
return fmt.Errorf("failed to create Network manager: %w", err)
195+
}
196+
197+
// Create a network
198+
network, err := networkManager.CreateNetwork(ctx, vbmctlapi.NetworkConfig{
199+
Name: "baremetal-e2e",
200+
Bridge: "metal3",
201+
Address: "192.168.222.1",
202+
Netmask: "255.255.255.0",
203+
})
204+
if err != nil {
205+
log.Fatal(err)
206+
}
207+
179208
// Create VM manager
180209
vmManager, err := libvirt.NewVMManager(conn, libvirt.VMManagerOptions{
181210
PoolName: "baremetal-e2e",
@@ -195,7 +224,7 @@ func main() {
195224
},
196225
Networks: []api.NetworkAttachment{
197226
{
198-
Network: "baremetal-e2e",
227+
Network: "baremetal-e2e", // refers to network created above
199228
MACAddress: "00:60:2f:31:81:01",
200229
},
201230
},

test/vbmctl/cmd/vbmctl/main.go

Lines changed: 150 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ func newRootCmd() *cobra.Command {
4444
for testing and development purposes. It currently provides functionality for:
4545
4646
- Creating and managing virtual machines using libvirt
47+
- Creating and managing libvirt networks
4748
- Reserving IP addresses for VMs via DHCP on existing libvirt networks
4849
4950
Planned features (not yet implemented):
50-
- Network management (create/delete libvirt networks)
5151
- BMC emulator support (sushy-tools, vbmc)
5252
- Image server for provisioning
5353
@@ -88,6 +88,7 @@ func newCreateCmd() *cobra.Command {
8888

8989
cmd.AddCommand(newCreateVMCmd())
9090
cmd.AddCommand(newCreateBMLCmd())
91+
cmd.AddCommand(newCreateNetworkCmd())
9192
return cmd
9293
}
9394

@@ -185,8 +186,9 @@ func newCreateBMLCmd() *cobra.Command {
185186
cmd := &cobra.Command{
186187
Use: "bml",
187188
Short: "Create a bare metal lab from configuration file",
188-
Long: `Create a bare metal lab (bml) with all VMs defined in the spec.vms section
189-
of the configuration file.
189+
Long: `Create a bare metal lab (bml) with all VMs and networks defined in the spec.vms
190+
and spec.networks sections of the configuration file. Note that network block can
191+
be omitted and VMs can be connected to existing networks as well.
190192
191193
Example configuration:
192194
spec:
@@ -197,9 +199,12 @@ Example configuration:
197199
volumes:
198200
- name: "root"
199201
size: 20
200-
networks:
202+
networkAttachments:
201203
- network: "baremetal-e2e"
202-
macAddress: "00:60:2f:31:81:01"`,
204+
macAddress: "00:60:2f:31:81:01"
205+
networks:
206+
- name: "baremetal-e2e"
207+
- bridge: "metal3"`,
203208
RunE: func(_ *cobra.Command, _ []string) error {
204209
ctx, cancel := contextWithSignal()
205210
defer cancel()
@@ -219,6 +224,22 @@ Example configuration:
219224
}
220225
defer func() { _, _ = conn.Close() }()
221226

227+
// Create networks before VMs
228+
networkManager, err := libvirt.NewNetworkManager(conn)
229+
if err != nil {
230+
return fmt.Errorf("failed to create Network manager: %w", err)
231+
}
232+
networks, err := networkManager.CreateNetworks(ctx, cfg.Spec.Networks)
233+
if err != nil {
234+
return err
235+
}
236+
//nolint:forbidigo // CLI output is intentional
237+
fmt.Println("\nCreated networks:")
238+
for _, network := range networks {
239+
//nolint:forbidigo // CLI output is intentional
240+
fmt.Printf(" - %s (UUID: %s)\n", network.Name, network.UUID)
241+
}
242+
222243
vmManager, err := libvirt.NewVMManager(conn, libvirt.VMManagerOptions{
223244
PoolName: cfg.Spec.Pool.Name,
224245
PoolPath: cfg.Spec.Pool.Path,
@@ -249,6 +270,63 @@ Example configuration:
249270
return cmd
250271
}
251272

273+
func newCreateNetworkCmd() *cobra.Command {
274+
var (
275+
name string
276+
bridge string
277+
address string
278+
netmask string
279+
)
280+
281+
cmd := &cobra.Command{
282+
Use: "network",
283+
Short: "Create a network",
284+
Long: "Create a new network with the specified configuration.",
285+
RunE: func(_ *cobra.Command, _ []string) error {
286+
ctx, cancel := contextWithSignal()
287+
defer cancel()
288+
289+
cfg, err := loadConfig()
290+
if err != nil {
291+
return err
292+
}
293+
294+
conn, err := libvirtgo.NewConnect(cfg.Spec.Libvirt.URI)
295+
if err != nil {
296+
return fmt.Errorf("failed to connect to libvirt: %w", err)
297+
}
298+
defer func() { _, _ = conn.Close() }()
299+
300+
networkManager, err := libvirt.NewNetworkManager(conn)
301+
if err != nil {
302+
return fmt.Errorf("failed to create Network manager: %w", err)
303+
}
304+
305+
networkCfg := vbmctlapi.NetworkConfig{
306+
Name: name,
307+
Bridge: bridge,
308+
Address: address,
309+
Netmask: netmask,
310+
}
311+
312+
network, err := networkManager.CreateNetwork(ctx, networkCfg)
313+
if err != nil {
314+
return err
315+
}
316+
//nolint:forbidigo // CLI output is intentional
317+
fmt.Printf("Created network: %s (UUID: %s)\n", network.Name, network.UUID)
318+
return nil
319+
},
320+
}
321+
322+
cmd.Flags().StringVar(&name, "name", config.DefaultNetworkName, "name of the network")
323+
cmd.Flags().StringVar(&bridge, "bridge", config.DefaultNetworkBridge, "name of the bridge interface")
324+
cmd.Flags().StringVar(&address, "address", config.DefaultNetworkAddress, "address of bridge")
325+
cmd.Flags().StringVar(&netmask, "netmask", config.DefaultNetworkNetmask, "netmask for network")
326+
327+
return cmd
328+
}
329+
252330
func newDeleteCmd() *cobra.Command {
253331
cmd := &cobra.Command{
254332
Use: "delete",
@@ -258,6 +336,7 @@ func newDeleteCmd() *cobra.Command {
258336

259337
cmd.AddCommand(newDeleteVMCmd())
260338
cmd.AddCommand(newDeleteBMLCmd())
339+
cmd.AddCommand(newDeleteNetworkCmd())
261340
return cmd
262341
}
263342

@@ -344,8 +423,8 @@ func newDeleteBMLCmd() *cobra.Command {
344423

345424
//nolint:forbidigo // CLI output is intentional
346425
fmt.Printf("Deleting bare metal lab (%d VMs)...\n", len(names))
347-
348-
if err := vmManager.DeleteAll(ctx, names, true); err != nil {
426+
err = vmManager.DeleteAll(ctx, names, true)
427+
if err != nil {
349428
return err
350429
}
351430

@@ -356,6 +435,70 @@ func newDeleteBMLCmd() *cobra.Command {
356435
fmt.Printf(" - %s\n", name)
357436
}
358437

438+
networkManager, err := libvirt.NewNetworkManager(conn)
439+
if err != nil {
440+
return fmt.Errorf("failed to create Network manager: %w", err)
441+
}
442+
443+
networks := make([]string, len(cfg.Spec.Networks))
444+
for i, network := range cfg.Spec.Networks {
445+
networks[i] = network.Name
446+
}
447+
448+
//nolint:forbidigo // CLI output is intentional
449+
fmt.Printf("Deleting networks (%d networks)...\n", len(networks))
450+
451+
if err := networkManager.DeleteNetworks(ctx, networks); err != nil {
452+
return err
453+
}
454+
455+
//nolint:forbidigo // CLI output is intentional
456+
fmt.Println("Deleted networks:")
457+
for _, name := range networks {
458+
//nolint:forbidigo // CLI output is intentional
459+
fmt.Printf(" - %s\n", name)
460+
}
461+
462+
return nil
463+
},
464+
}
465+
466+
return cmd
467+
}
468+
469+
func newDeleteNetworkCmd() *cobra.Command {
470+
cmd := &cobra.Command{
471+
Use: "network [name]",
472+
Short: "Delete a network",
473+
Args: cobra.ExactArgs(1),
474+
RunE: func(_ *cobra.Command, args []string) error {
475+
ctx, cancel := contextWithSignal()
476+
defer cancel()
477+
478+
name := args[0]
479+
480+
cfg, err := loadConfig()
481+
if err != nil {
482+
return err
483+
}
484+
485+
conn, err := libvirtgo.NewConnect(cfg.Spec.Libvirt.URI)
486+
if err != nil {
487+
return fmt.Errorf("failed to connect to libvirt: %w", err)
488+
}
489+
defer func() { _, _ = conn.Close() }()
490+
491+
networkManager, err := libvirt.NewNetworkManager(conn)
492+
if err != nil {
493+
return fmt.Errorf("failed to create Network manager: %w", err)
494+
}
495+
496+
if err := networkManager.DeleteNetwork(ctx, name); err != nil {
497+
return fmt.Errorf("failed to delete network: %w", err)
498+
}
499+
500+
//nolint:forbidigo // CLI output is intentional
501+
fmt.Printf("Deleted network %s\n", name)
359502
return nil
360503
},
361504
}

0 commit comments

Comments
 (0)