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

🌱 [WIP] Allow vbmctl to create VM with multiple network interfaces #2207

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
2 changes: 1 addition & 1 deletion hack/clean-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ virsh -c qemu:///system net-destroy baremetal-e2e
virsh -c qemu:///system net-undefine baremetal-e2e

# Clean volume pool directory
rm -rf /tmp/pool_oo/*
rm -rf /tmp/pool_oo

# Clean volume pool
virsh pool-destroy default || true
Expand Down
13 changes: 13 additions & 0 deletions test/e2e/bmc.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ import (
"gopkg.in/yaml.v2"
)

type Network struct {
// Name of the network
NetworkName string `yaml:"name,omitempty"`
// MacAddress of the network
MacAddress string `yaml:"macAddress,omitempty"`
// IPAddress of the network
IPAddress string `yaml:"ipAddress,omitempty"`
}

type Networks []Network

// BMC defines a BMH to use in the tests.
type BMC struct {
// BMC initial username
Expand All @@ -28,6 +39,8 @@ type BMC struct {
// Optional. Only needed if e2eConfig variable
// SSH_CHECK_PROVISIONED is true
SSHPort string `yaml:"sshPort,omitempty"`
// Optional. Not needed for E2E tests
Networks Networks
}

func LoadBMCConfig(configPath string) (*[]BMC, error) {
Expand Down
8 changes: 8 additions & 0 deletions test/e2e/config/bmcs-fixture.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,18 @@
hostName: ""
ipAddress: "192.168.222.122"
sshPort: "22"
networks:
- name: "baremetal-e2e"
macAddress: "00:60:2f:31:81:01"
ipAddress: "192.168.222.122"
- user: admin
password: password
address: "redfish+http://192.168.222.1:8000/redfish/v1/Systems/bmo-e2e-1"
bootMacAddress: "00:60:2f:31:81:02"
hostName: ""
ipAddress: "192.168.222.123"
sshPort: "22"
networks:
- name: "baremetal-e2e"
macAddress: "00:60:2f:31:81:02"
ipAddress: "192.168.222.123"
8 changes: 8 additions & 0 deletions test/e2e/config/bmcs-ipmi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@
hostName: "bmo-e2e-0"
ipAddress: "192.168.222.122"
sshPort: "22"
networks:
- name: "baremetal-e2e"
macAddress: "00:60:2f:31:81:01"
ipAddress: "192.168.222.122"
- user: admin
password: password
address: "ipmi://192.168.222.1:16231"
bootMacAddress: "00:60:2f:31:81:02"
hostName: "bmo-e2e-1"
ipAddress: "192.168.222.123"
sshPort: "22"
networks:
- name: "baremetal-e2e"
macAddress: "00:60:2f:31:81:02"
ipAddress: "192.168.222.123"
8 changes: 8 additions & 0 deletions test/e2e/config/bmcs-redfish-virtualmedia.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@
hostName: "bmo-e2e-0"
ipAddress: "192.168.222.122"
sshPort: "22"
networks:
- name: "baremetal-e2e"
macAddress: "00:60:2f:31:81:01"
ipAddress: "192.168.222.122"
- user: admin
password: password
address: "redfish-virtualmedia+http://192.168.222.1:8000/redfish/v1/Systems/bmo-e2e-1"
bootMacAddress: "00:60:2f:31:81:02"
hostName: "bmo-e2e-1"
ipAddress: "192.168.222.123"
sshPort: "22"
networks:
- name: "baremetal-e2e"
macAddress: "00:60:2f:31:81:02"
ipAddress: "192.168.222.123"
8 changes: 8 additions & 0 deletions test/e2e/config/bmcs-redfish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@
hostName: "bmo-e2e-0"
ipAddress: "192.168.222.122"
sshPort: "22"
networks:
- name: "baremetal-e2e"
macAddress: "00:60:2f:31:81:01"
ipAddress: "192.168.222.122"
- user: admin
password: password
address: "redfish+http://192.168.222.1:8000/redfish/v1/Systems/bmo-e2e-1"
bootMacAddress: "00:60:2f:31:81:02"
hostName: "bmo-e2e-1"
ipAddress: "192.168.222.123"
sshPort: "22"
networks:
- name: "baremetal-e2e"
macAddress: "00:60:2f:31:81:02"
ipAddress: "192.168.222.123"
174 changes: 95 additions & 79 deletions test/vbmctl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ var (
templateFiles embed.FS
)

const (
poolName = "default"
poolPath = "/tmp/pool_oo"
)

type Host struct {
HostName string
Networks bmoe2e.Networks
PoolName string
PoolPath string
}

func RenderTemplate(inputFile string, data interface{}) (string, error) {
tmpl, err := template.ParseFS(templateFiles, inputFile)
if err != nil {
Expand All @@ -36,9 +48,36 @@ func RenderTemplate(inputFile string, data interface{}) (string, error) {
return buf.String(), nil
}

func startVolumePool(pool *libvirt.StoragePool) error {
if err := pool.SetAutostart(true); err != nil {
fmt.Println("Failed to Set the pool autostart")
fmt.Printf("Error occurred: %v\n", err)
return err
}

active, err := pool.IsActive()
if err != nil {
return err
}

if active {
return nil
}

if err := pool.Create(0); err != nil {
fmt.Println("Failed to Start the pool")
fmt.Printf("Error occurred: %v\n", err)
return err
}
return nil
}

// CreateVolumePool creates a volume pool with specified name if a pool with
// that name does not exist yet.
func CreateVolumePool(poolName, poolPath string) (*libvirt.StoragePool, error) {
if err := os.MkdirAll(poolPath, 0777); err != nil && !os.IsExist(err) {
return nil, err
}
// Connect to libvirt daemon
conn, err := libvirt.NewConnect("qemu:///system")
if err != nil {
Expand All @@ -51,6 +90,9 @@ func CreateVolumePool(poolName, poolPath string) (*libvirt.StoragePool, error) {

if err == nil {
fmt.Println("Pool already exists")
if err := startVolumePool(pool); err != nil {
return nil, err
}
return pool, nil
}

Expand Down Expand Up @@ -84,15 +126,7 @@ func CreateVolumePool(poolName, poolPath string) (*libvirt.StoragePool, error) {
return nil, err
}

if err = pool.SetAutostart(true); err != nil {
fmt.Println("Failed to Set the pool autostart")
fmt.Printf("Error occurred: %v\n", err)
return nil, err
}

if err = pool.Create(0); err != nil {
fmt.Println("Failed to Start the pool")
fmt.Printf("Error occurred: %v\n", err)
if err := startVolumePool(pool); err != nil {
return nil, err
}

Expand Down Expand Up @@ -153,9 +187,7 @@ func CreateVolume(volumeName, poolName, poolPath string, capacityInGB int) error
// If the domain is successfully defined and created, the virtual machine is
// started. Errors during qcow2 file creation, volume creation, libvirt connection,
// template rendering, or domain creation are returned.
func CreateLibvirtVM(hostName, networkName, macAddress string) error {
poolName := "default"
poolPath := "/tmp/pool_oo"
func CreateLibvirtVM(hostName string, networks *bmoe2e.Networks) error {
opts := make(map[string]any)
opts[qcow2.OPT_SIZE] = 3 * (1 << 30) // qcow2 file's size is 3g
opts[qcow2.OPT_FMT] = "qcow2" // qcow2 format
Expand All @@ -180,16 +212,11 @@ func CreateLibvirtVM(hostName, networkName, macAddress string) error {
}
defer conn.Close()

data := struct {
HostName string
Network string
MacAddress string
PoolPath string
}{
HostName: hostName,
Network: networkName,
MacAddress: macAddress,
PoolPath: poolPath,
data := Host{
HostName: hostName,
Networks: *networks,
PoolName: poolName,
PoolPath: poolPath,
}

vmCfg, err := RenderTemplate("templates/VM.xml.tpl", data)
Expand Down Expand Up @@ -221,90 +248,79 @@ func CreateLibvirtVM(hostName, networkName, macAddress string) error {
//
// It will return an error if the network does not exist, or if creating the VM
// or adding the DHCP host entry fails.
func CreateLibvirtBMC(macAddress, hostName, ipAddress, networkName string) error {
func CreateLibvirtBMC(hostName string, networks *bmoe2e.Networks) error {
var err error
conn, err := libvirt.NewConnect("qemu:///system")
if err != nil {
return err
}
defer conn.Close()

network, err := conn.LookupNetworkByName(networkName)
if err != nil {
return err
}

xmlTpl, err := template.New("xml").Parse("<host mac='{{ .MacAddress }}' name='{{ .HostName }}' ip='{{ .IPAddress }}' />")
for _, net := range *networks {
network, err := conn.LookupNetworkByName(net.NetworkName)
if err != nil {
return err
}
xmlTpl, err := template.New("xml").Parse("<host mac='{{ .MacAddress }}' name='{{ .HostName }}' ip='{{ .IPAddress }}' />")

if err != nil {
return err
}
if err != nil {
return err
}

data := struct {
MacAddress string
HostName string
IPAddress string
}{
MacAddress: macAddress,
HostName: hostName,
IPAddress: ipAddress,
}
data := struct {
MacAddress string
HostName string
IPAddress string
}{
MacAddress: net.MacAddress,
HostName: hostName,
IPAddress: net.IPAddress,
}

var buf bytes.Buffer
var buf bytes.Buffer

err = xmlTpl.Execute(&buf, data)
err = xmlTpl.Execute(&buf, data)
if err != nil {
fmt.Println("Failed to create BMC")
fmt.Printf("Error occurred: %v\n", err)
return err
}

if err != nil {
fmt.Println("Failed to create BMC")
fmt.Printf("Error occurred: %v\n", err)
return err
if err = network.Update(
libvirt.NETWORK_UPDATE_COMMAND_ADD_LAST,
libvirt.NETWORK_SECTION_IP_DHCP_HOST,
-1,
buf.String(),
libvirt.NETWORK_UPDATE_AFFECT_LIVE|libvirt.NETWORK_UPDATE_AFFECT_CONFIG,
); err != nil {
fmt.Printf("Error occurred: %v\n", err)
return err
}
}

if err = network.Update(
libvirt.NETWORK_UPDATE_COMMAND_ADD_LAST,
libvirt.NETWORK_SECTION_IP_DHCP_HOST,
-1,
buf.String(),
libvirt.NETWORK_UPDATE_AFFECT_LIVE|libvirt.NETWORK_UPDATE_AFFECT_CONFIG,
); err != nil {
fmt.Printf("Error occurred: %v\n", err)
return err
}
if err = CreateLibvirtVM(hostName, networkName, macAddress); err != nil {
if err = CreateLibvirtVM(hostName, networks); err != nil {
fmt.Printf("Error occurred: %v\n", err)
return err
}
return nil
}

func main() {
var vmName = flag.String(
"vm-name", "VM-1", "The name of the VM to create")
var networkName = flag.String(
"network-name", "baremetal-e2e", "The name of the network that the new VM should be attached to")
var macAddress = flag.String(
"mac-address", "00:60:2f:31:81:01", "Mac address of the VM on the network")
var ipAddress = flag.String(
"ip-address", "192.168.222.122", "IP address of the VM on the network")
var configFile = flag.String(
"yaml-source-file", "", "yaml file where BMCS are defined. If this is set, ignore all other options")
flag.Parse()
var err error
if *configFile == "" {
if err = CreateLibvirtBMC(*macAddress, *vmName, *ipAddress, *networkName); err != nil {
fmt.Fprintln(os.Stderr, "Error: YAML source file path is required")
os.Exit(1)
}
bmcs, err := bmoe2e.LoadBMCConfig(*configFile)
if err != nil {
os.Exit(1)
}
for _, bmc := range *bmcs {
if err = CreateLibvirtBMC(bmc.HostName, &bmc.Networks); err != nil {
fmt.Printf("Error occurred: %v\n", err)
os.Exit(1)
}
} else {
bmcs, err := bmoe2e.LoadBMCConfig(*configFile)
if err != nil {
os.Exit(1)
}
for _, bmc := range *bmcs {
if err = CreateLibvirtBMC(bmc.BootMacAddress, bmc.HostName, bmc.IPAddress, "baremetal-e2e"); err != nil {
fmt.Printf("Error occurred: %v\n", err)
os.Exit(1)
}
}
}
}
9 changes: 5 additions & 4 deletions test/vbmctl/templates/VM.xml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='{{ .PoolPath }}/{{ .HostName }}.qcow2'/>
<target dev='vda' bus='virtio'/>
<target dev='sda' bus='scsi'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>
<controller type='scsi' index='0' model='virtio-scsi'>
<address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
Expand Down Expand Up @@ -72,19 +73,19 @@
<target chassis='6' port='0x15'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
</controller>
{{ range .Networks }}
<interface type='network'>
<mac address='{{ .MacAddress }}'/>
<source network='{{ .Network }}'/>
<source network='{{ .NetworkName }}'/>
<model type='virtio' />
</interface>
{{ end }}
<serial type='pty'>
<log file='/var/log/libvirt/qemu/{{ .HostName }}-serial0.log' append='on'/>
<target type='isa-serial' port='0'>
<model name='isa-serial'/>
</target>
</serial>
<console type='pty'>
<log file='/var/log/libvirt/qemu/{{ .HostName }}-serial0.log' append='on'/>
<target type='serial' port='0'/>
</console>
<input type='mouse' bus='ps2'/>
Expand Down
Loading