From 13bbfff1a612d179fcf516c7afd125f0b3e5a4ac Mon Sep 17 00:00:00 2001 From: Huy Mai Date: Tue, 21 Jan 2025 12:25:14 +0200 Subject: [PATCH 1/2] Allow vbmctl to create VM with multiple network interfaces 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 --- test/e2e/bmc.go | 13 ++ test/e2e/config/bmcs-fixture.yaml | 8 ++ test/e2e/config/bmcs-ipmi.yaml | 8 ++ .../e2e/config/bmcs-redfish-virtualmedia.yaml | 8 ++ test/e2e/config/bmcs-redfish.yaml | 8 ++ test/vbmctl/main.go | 125 +++++++++--------- test/vbmctl/templates/VM.xml.tpl | 4 +- 7 files changed, 107 insertions(+), 67 deletions(-) diff --git a/test/e2e/bmc.go b/test/e2e/bmc.go index 319f06b06b..513ed9a9bf 100644 --- a/test/e2e/bmc.go +++ b/test/e2e/bmc.go @@ -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 @@ -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) { diff --git a/test/e2e/config/bmcs-fixture.yaml b/test/e2e/config/bmcs-fixture.yaml index c16c8a36eb..2c1a0aa051 100644 --- a/test/e2e/config/bmcs-fixture.yaml +++ b/test/e2e/config/bmcs-fixture.yaml @@ -7,6 +7,10 @@ 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" @@ -14,3 +18,7 @@ hostName: "" ipAddress: "192.168.222.123" sshPort: "22" + networks: + - name: "baremetal-e2e" + macAddress: "00:60:2f:31:81:02" + ipAddress: "192.168.222.123" diff --git a/test/e2e/config/bmcs-ipmi.yaml b/test/e2e/config/bmcs-ipmi.yaml index 758d93459b..1d908e666d 100644 --- a/test/e2e/config/bmcs-ipmi.yaml +++ b/test/e2e/config/bmcs-ipmi.yaml @@ -5,6 +5,10 @@ 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" @@ -12,3 +16,7 @@ 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" diff --git a/test/e2e/config/bmcs-redfish-virtualmedia.yaml b/test/e2e/config/bmcs-redfish-virtualmedia.yaml index f991a792d1..fcaaaf05a9 100644 --- a/test/e2e/config/bmcs-redfish-virtualmedia.yaml +++ b/test/e2e/config/bmcs-redfish-virtualmedia.yaml @@ -5,6 +5,10 @@ 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" @@ -12,3 +16,7 @@ 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" diff --git a/test/e2e/config/bmcs-redfish.yaml b/test/e2e/config/bmcs-redfish.yaml index 8ec2b33a71..bd161c6a13 100644 --- a/test/e2e/config/bmcs-redfish.yaml +++ b/test/e2e/config/bmcs-redfish.yaml @@ -5,6 +5,10 @@ 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" @@ -12,3 +16,7 @@ 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" diff --git a/test/vbmctl/main.go b/test/vbmctl/main.go index bc8cb1649b..1572cc62cd 100644 --- a/test/vbmctl/main.go +++ b/test/vbmctl/main.go @@ -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 { @@ -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) @@ -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) @@ -221,7 +225,7 @@ 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 { @@ -229,48 +233,49 @@ func CreateLibvirtBMC(macAddress, hostName, ipAddress, networkName string) error } defer conn.Close() - network, err := conn.LookupNetworkByName(networkName) - if err != nil { - return err - } - - xmlTpl, err := template.New("xml").Parse("") + for _, net := range *networks { + network, err := conn.LookupNetworkByName(net.NetworkName) + if err != nil { + return err + } + xmlTpl, err := template.New("xml").Parse("") - 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 } @@ -278,33 +283,21 @@ func CreateLibvirtBMC(macAddress, hostName, ipAddress, networkName string) error } 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) - } - } } } diff --git a/test/vbmctl/templates/VM.xml.tpl b/test/vbmctl/templates/VM.xml.tpl index bd3de83fb7..cace5e287e 100644 --- a/test/vbmctl/templates/VM.xml.tpl +++ b/test/vbmctl/templates/VM.xml.tpl @@ -72,11 +72,13 @@
+ {{ range .Networks }} - + + {{ end }} From 99413b7cabe7c794dcf135236bb2f8dad0313b34 Mon Sep 17 00:00:00 2001 From: Huy Mai Date: Fri, 24 Jan 2025 14:19:59 +0000 Subject: [PATCH 2/2] Change pool name and hard drive path Signed-off-by: Huy Mai --- hack/clean-e2e.sh | 2 +- test/vbmctl/main.go | 55 ++++++++++++++++++++++---------- test/vbmctl/templates/VM.xml.tpl | 5 ++- 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/hack/clean-e2e.sh b/hack/clean-e2e.sh index 40ab5d10fb..c8f733032d 100755 --- a/hack/clean-e2e.sh +++ b/hack/clean-e2e.sh @@ -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 diff --git a/test/vbmctl/main.go b/test/vbmctl/main.go index 1572cc62cd..d2799e96be 100644 --- a/test/vbmctl/main.go +++ b/test/vbmctl/main.go @@ -21,9 +21,15 @@ var ( templateFiles embed.FS ) +const ( + poolName = "default" + poolPath = "/tmp/pool_oo" +) + type Host struct { HostName string Networks bmoe2e.Networks + PoolName string PoolPath string } @@ -42,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 { @@ -57,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 } @@ -90,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 } @@ -160,8 +188,6 @@ func CreateVolume(volumeName, poolName, poolPath string, capacityInGB int) error // started. Errors during qcow2 file creation, volume creation, libvirt connection, // template rendering, or domain creation are returned. func CreateLibvirtVM(hostName string, networks *bmoe2e.Networks) error { - poolName := "default" - poolPath := "/tmp/pool_oo" 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 @@ -186,13 +212,10 @@ func CreateLibvirtVM(hostName string, networks *bmoe2e.Networks) error { } defer conn.Close() - data := struct { - HostName string - Networks bmoe2e.Networks - PoolPath string - }{ + data := Host{ HostName: hostName, Networks: *networks, + PoolName: poolName, PoolPath: poolPath, } diff --git a/test/vbmctl/templates/VM.xml.tpl b/test/vbmctl/templates/VM.xml.tpl index cace5e287e..d5d963d1e2 100644 --- a/test/vbmctl/templates/VM.xml.tpl +++ b/test/vbmctl/templates/VM.xml.tpl @@ -30,7 +30,8 @@ - + +
@@ -80,13 +81,11 @@ {{ end }} - -