Skip to content

Commit

Permalink
Allow vbmctl to create VM with multiple network interfaces
Browse files Browse the repository at this point in the history
vbmctl currently creates VM with 1 network interface. This is enough for
BMO E2E, but to be able to have VMs that work for other purposes, having
the flexibility to have multiple networks is required.

Signed-off-by: Huy Mai <[email protected]>
  • Loading branch information
mquhuy committed Jan 21, 2025
1 parent 23c54cf commit a3de0e6
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 67 deletions.
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"
125 changes: 59 additions & 66 deletions test/vbmctl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ var (
templateFiles embed.FS
)

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

func RenderTemplate(inputFile string, data interface{}) (string, error) {
tmpl, err := template.ParseFS(templateFiles, inputFile)
if err != nil {
Expand Down Expand Up @@ -153,7 +159,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 {
func CreateLibvirtVM(hostName string, networks *bmoe2e.Networks) error {
poolName := "default"
poolPath := "/tmp/pool_oo"
opts := make(map[string]any)
Expand Down Expand Up @@ -181,15 +187,13 @@ func CreateLibvirtVM(hostName, networkName, macAddress string) error {
defer conn.Close()

data := struct {
HostName string
Network string
MacAddress string
PoolPath string
HostName string
Networks bmoe2e.Networks
PoolPath string
}{
HostName: hostName,
Network: networkName,
MacAddress: macAddress,
PoolPath: poolPath,
HostName: hostName,
Networks: *networks,
PoolPath: poolPath,
}

vmCfg, err := RenderTemplate("templates/VM.xml.tpl", data)
Expand Down Expand Up @@ -221,90 +225,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)
}
}
}
}
4 changes: 3 additions & 1 deletion test/vbmctl/templates/VM.xml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,13 @@
<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'>
Expand Down

0 comments on commit a3de0e6

Please sign in to comment.