Skip to content

Commit 323411e

Browse files
committed
Core libraries for two node disruptive tests.
1 parent af977aa commit 323411e

File tree

10 files changed

+1613
-6
lines changed

10 files changed

+1613
-6
lines changed

pkg/clioptions/clusterdiscovery/cluster.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ import (
3030
"github.com/openshift/origin/test/extended/util/azure"
3131
)
3232

33+
// HypervisorConfig contains configuration for hypervisor-based recovery operations
34+
type HypervisorConfig struct {
35+
HypervisorIP string `json:"hypervisorIP"`
36+
SSHUser string `json:"sshUser"`
37+
PrivateKeyPath string `json:"privateKeyPath"`
38+
}
39+
3340
type ClusterConfiguration struct {
3441
ProviderName string `json:"type"`
3542

@@ -76,6 +83,9 @@ type ClusterConfiguration struct {
7683
// IsNoOptionalCapabilities indicates the cluster has no optional capabilities enabled
7784
HasNoOptionalCapabilities bool
7885

86+
// HypervisorConfig contains SSH configuration for hypervisor-based recovery operations
87+
HypervisorConfig *HypervisorConfig
88+
7989
// APIGroups contains the set of API groups available in the cluster
8090
APIGroups sets.Set[string] `json:"-"`
8191
// EnabledFeatureGates contains the set of enabled feature gates in the cluster

pkg/cmd/openshift-tests/run/flags.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package run
22

33
import (
4+
"encoding/json"
5+
"fmt"
46
"os"
57

68
"github.com/openshift-eng/openshift-tests-extension/pkg/extension"
@@ -28,9 +30,6 @@ type RunSuiteFlags struct {
2830
ToImage string
2931
TestOptions []string
3032

31-
// Shared by initialization code
32-
config *clusterdiscovery.ClusterConfiguration
33-
3433
genericclioptions.IOStreams
3534
}
3635

@@ -84,7 +83,7 @@ func (f *RunSuiteFlags) ToOptions(args []string, availableSuites []*testginkgo.T
8483
// shallow copy to mutate
8584
ginkgoOptions := f.GinkgoRunSuiteOptions
8685

87-
providerConfig, err := f.SuiteWithKubeTestInitializationPreSuite()
86+
clusterConfig, err := f.SuiteWithKubeTestInitializationPreSuite()
8887
if err != nil {
8988
return nil, err
9089
}
@@ -95,13 +94,39 @@ func (f *RunSuiteFlags) ToOptions(args []string, availableSuites []*testginkgo.T
9594
return nil, err
9695
}
9796

97+
// Parse hypervisor configuration if provided and set it in environment for test context
98+
if f.GinkgoRunSuiteOptions.WithHypervisorConfigJSON != "" {
99+
// Validate the JSON format
100+
var hypervisorConfig clusterdiscovery.HypervisorConfig
101+
if err := json.Unmarshal([]byte(f.GinkgoRunSuiteOptions.WithHypervisorConfigJSON), &hypervisorConfig); err != nil {
102+
return nil, fmt.Errorf("failed to parse hypervisor configuration JSON: %v", err)
103+
}
104+
105+
// Validate required fields
106+
if hypervisorConfig.HypervisorIP == "" {
107+
return nil, fmt.Errorf("hypervisorIP is required in hypervisor configuration")
108+
}
109+
if hypervisorConfig.SSHUser == "" {
110+
return nil, fmt.Errorf("sshUser is required in hypervisor configuration")
111+
}
112+
if hypervisorConfig.PrivateKeyPath == "" {
113+
return nil, fmt.Errorf("privateKey is required in hypervisor configuration")
114+
}
115+
116+
// Set the hypervisor configuration in the cluster config
117+
clusterConfig.HypervisorConfig = &hypervisorConfig
118+
119+
// Also set it in environment for test context access
120+
os.Setenv("HYPERVISOR_CONFIG", f.GinkgoRunSuiteOptions.WithHypervisorConfigJSON)
121+
}
122+
98123
o := &RunSuiteOptions{
99124
GinkgoRunSuiteOptions: ginkgoOptions,
100125
Suite: suite,
101126
Extension: internalExtension,
102-
ClusterConfig: providerConfig,
127+
ClusterConfig: clusterConfig,
103128
FromRepository: f.FromRepository,
104-
CloudProviderJSON: providerConfig.ToJSONString(),
129+
CloudProviderJSON: clusterConfig.ToJSONString(),
105130
CloseFn: closeFn,
106131
IOStreams: f.IOStreams,
107132
}

pkg/cmd/openshift-tests/run/options.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ type RunSuiteOptions struct {
3131
CloseFn iooptions.CloseFunc
3232
genericclioptions.IOStreams
3333

34+
// HypervisorConfig contains SSH configuration for hypervisor-based recovery operations
35+
// If set, will run recovery tests that require the hypervisor-based recovery, such as
36+
// the node replacement test in the two_node recovery suite.
37+
HypervisorConfig *clusterdiscovery.HypervisorConfig
38+
3439
// ClusterConfig contains cluster-specific configuration for filtering tests
3540
ClusterConfig *clusterdiscovery.ClusterConfiguration
3641

pkg/test/filters/cluster_state.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ func NewClusterStateFilter(config *clusterdiscovery.ClusterConfiguration) *Clust
6767
skips = append(skips, "[Skipped:NoOptionalCapabilities]")
6868
}
6969

70+
if config.HypervisorConfig == nil {
71+
skips = append(skips, "[Requires:HypervisorSSHConfig]")
72+
}
73+
7074
logrus.WithField("skips", skips).Info("Generated skips for cluster state")
7175

7276
return &ClusterStateFilter{

pkg/test/ginkgo/cmd_runsuite.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ type GinkgoRunSuiteOptions struct {
9696

9797
// RetryStrategy controls retry behavior and final outcome decisions
9898
RetryStrategy RetryStrategy
99+
100+
// WithHypervisorConfigJSON contains JSON configuration for hypervisor-based recovery operations
101+
WithHypervisorConfigJSON string
99102
}
100103

101104
func NewGinkgoRunSuiteOptions(streams genericclioptions.IOStreams) *GinkgoRunSuiteOptions {
@@ -133,6 +136,7 @@ func (o *GinkgoRunSuiteOptions) BindFlags(flags *pflag.FlagSet) {
133136
flags.StringVar(&o.ShardStrategy, "shard-strategy", o.ShardStrategy, "Which strategy to use for sharding (hash)")
134137
availableStrategies := getAvailableRetryStrategies()
135138
flags.Var(newRetryStrategyFlag(&o.RetryStrategy), "retry-strategy", fmt.Sprintf("Test retry strategy (available: %s, default: %s)", strings.Join(availableStrategies, ", "), defaultRetryStrategy))
139+
flags.StringVar(&o.WithHypervisorConfigJSON, "with-hypervisor-json", os.Getenv("HYPERVISOR_CONFIG"), "JSON configuration for hypervisor-based recovery operations. Must contain hypervisorIP, sshUser, and privateKeyPath fields.")
136140
}
137141

138142
func (o *GinkgoRunSuiteOptions) Validate() error {
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Package utils provides hypervisor configuration and validation utilities for two-node cluster testing.
2+
//
3+
// Tests requiring hypervisor access should include the [Requires:HypervisorSSHConfig] annotation.
4+
//
5+
// Configuration can be provided via command-line flag or environment variable:
6+
//
7+
// openshift-tests run openshift/two-node --with-hypervisor-json='{
8+
// "IP": "192.168.111.1",
9+
// "User": "root",
10+
// "privateKeyPath": "/path/to/private/key"
11+
// }'
12+
//
13+
// Or:
14+
//
15+
// export HYPERVISOR_CONFIG='{"IP":"192.168.111.1","User":"root","privateKeyPath":"/path/to/key"}'
16+
// openshift-tests run openshift/two-node
17+
//
18+
// Usage example:
19+
//
20+
// if !exutil.HasHypervisorConfig() {
21+
// utils.PrintHypervisorConfigUsage()
22+
// return
23+
// }
24+
// config := exutil.GetHypervisorConfig()
25+
// utils.VerifyHypervisorConnectivity(&config, knownHostsPath)
26+
package utils
27+
28+
import (
29+
"fmt"
30+
"strings"
31+
32+
g "github.com/onsi/ginkgo/v2"
33+
"k8s.io/klog/v2"
34+
)
35+
36+
// PrintHypervisorConfigUsage prints usage instructions for configuring hypervisor SSH access.
37+
// Call this when HasHypervisorConfig() returns false to provide configuration guidance.
38+
func PrintHypervisorConfigUsage() {
39+
usageMessage := `
40+
================================================================================
41+
Two-Node Test Suite - Hypervisor Configuration Required
42+
================================================================================
43+
44+
This test requires hypervisor SSH configuration to manage virtual machines
45+
and perform node operations. The [Requires:HypervisorSSHConfig] annotation
46+
indicates this requirement.
47+
48+
CONFIGURATION METHODS:
49+
50+
1. Command-Line Flag (recommended for interactive testing):
51+
52+
openshift-tests run openshift/two-node --with-hypervisor-json='{
53+
"IP": "192.168.111.1",
54+
"User": "root",
55+
"privateKeyPath": "/path/to/private/key"
56+
}'
57+
58+
2. Environment Variable (recommended for CI/CD):
59+
60+
export HYPERVISOR_CONFIG='{"IP":"192.168.111.1","User":"root","privateKeyPath":"/path/to/key"}'
61+
openshift-tests run openshift/two-node
62+
63+
CONFIGURATION FIELDS:
64+
65+
- IP: IP address or hostname of the hypervisor
66+
- User: SSH username (typically "root")
67+
- privateKeyPath: Absolute path to SSH private key file
68+
69+
TROUBLESHOOTING:
70+
71+
If configuration fails:
72+
1. Verify JSON syntax is valid
73+
2. Check that the private key file exists
74+
3. Test SSH connectivity: ssh -i <privateKeyPath> <User>@<IP>
75+
4. Verify virsh is available: ssh <User>@<IP> 'virsh version'
76+
77+
================================================================================
78+
`
79+
g.GinkgoT().Logf(usageMessage)
80+
}
81+
82+
// VerifyHypervisorAvailability verifies SSH connectivity to the hypervisor and checks
83+
// that virsh and libvirt are available.
84+
func VerifyHypervisorAvailability(sshConfig *SSHConfig, knownHostsPath string) error {
85+
klog.V(2).Infof("Verifying hypervisor connectivity to %s@%s", sshConfig.User, sshConfig.IP)
86+
87+
// Test basic SSH connectivity
88+
output, _, err := VerifyConnectivity(sshConfig, knownHostsPath)
89+
if err != nil {
90+
klog.ErrorS(err, "Failed to establish SSH connection to hypervisor",
91+
"user", sshConfig.User,
92+
"host", sshConfig.IP,
93+
"output", output)
94+
klog.ErrorS(nil, "Ensure the hypervisor is accessible and SSH key is correct")
95+
return fmt.Errorf("failed to establish SSH connection to hypervisor %s@%s: %w", sshConfig.User, sshConfig.IP, err)
96+
}
97+
klog.V(2).Infof("SSH connectivity verified: %s", strings.TrimSpace(output))
98+
99+
// Test virsh availability and basic functionality
100+
output, err = VerifyVirsh(sshConfig, knownHostsPath)
101+
if err != nil {
102+
klog.ErrorS(err, "virsh is not available or not working on hypervisor",
103+
"user", sshConfig.User,
104+
"host", sshConfig.IP,
105+
"output", output)
106+
klog.ErrorS(nil, "Ensure libvirt and virsh are installed on the hypervisor")
107+
return fmt.Errorf("virsh is not available or not working on hypervisor %s@%s: %w", sshConfig.User, sshConfig.IP, err)
108+
}
109+
klog.V(2).Infof("virsh availability verified: %s", strings.TrimSpace(output))
110+
111+
// Test libvirt connection by listing VMs
112+
output, err = VirshListAllVMs(sshConfig, knownHostsPath)
113+
if err != nil {
114+
klog.ErrorS(err, "Failed to connect to libvirt on hypervisor",
115+
"user", sshConfig.User,
116+
"host", sshConfig.IP,
117+
"output", output)
118+
klog.ErrorS(nil, "Ensure libvirtd service is running and user has access")
119+
return fmt.Errorf("failed to connect to libvirt on hypervisor %s@%s: %w", sshConfig.User, sshConfig.IP, err)
120+
}
121+
klog.V(2).Infof("libvirt connection verified, found VMs: %s", strings.TrimSpace(output))
122+
123+
klog.V(2).Infof("Hypervisor connectivity verification completed successfully")
124+
return nil
125+
}

0 commit comments

Comments
 (0)