@@ -8,9 +8,12 @@ import (
8
8
"fmt"
9
9
"net"
10
10
"net/netip"
11
+ "slices"
11
12
"strconv"
12
13
"strings"
13
14
15
+ "github.com/vmware/govmomi/task"
16
+
14
17
"github.com/openshift/machine-api-operator/pkg/util/ipam"
15
18
16
19
"github.com/google/uuid"
@@ -83,6 +86,10 @@ func (r *Reconciler) create() error {
83
86
return fmt .Errorf ("%v: failed validating machine provider spec: %w" , r .machine .GetName (), err )
84
87
}
85
88
89
+ if r .providerSpec .Workspace .VMGroup != "" && ! r .featureGates .Enabled (featuregate .Feature (apifeatures .FeatureGateVSphereHostVMGroupZonal )) {
90
+ return fmt .Errorf ("%v: vmGroup is only available with the VSphereHostVMGroupZonal feature gate" , r .machine .GetName ())
91
+ }
92
+
86
93
if ipam .HasStaticIPConfiguration (r .providerSpec ) {
87
94
if ! r .featureGates .Enabled (featuregate .Feature (apifeatures .FeatureGateVSphereStaticIPs )) {
88
95
return fmt .Errorf ("%v: static IP/IPAM configuration is only available with the VSphereStaticIPs feature gate" , r .machine .GetName ())
@@ -209,7 +216,21 @@ func (r *Reconciler) create() error {
209
216
}
210
217
211
218
// if clone task finished successfully, power on the vm
212
- if moTask .Info .DescriptionId == cloneVmTaskDescriptionId {
219
+ // The simulator task.Info.DescriptionId is different (VirtualMachine.cloneVM)
220
+ if strings .Contains (moTask .Info .DescriptionId , cloneVmTaskDescriptionId ) {
221
+ if r .machineScope .providerSpec .Workspace .VMGroup != "" {
222
+ klog .Infof ("Adding on cloned machine: %s to vm group: %s" , r .machine .Name , r .machineScope .providerSpec .Workspace .VMGroup )
223
+
224
+ if err := modifyVMGroup (r .machineScope , false ); err != nil {
225
+ var taskError task.Error
226
+ if errors .As (err , & taskError ) {
227
+ return fmt .Errorf ("could not update VM Group membership: %w" , taskError )
228
+ }
229
+
230
+ return fmt .Errorf ("could not update VM Group membership: %w" , err )
231
+ }
232
+ }
233
+
213
234
klog .Infof ("Powering on cloned machine: %v" , r .machine .Name )
214
235
task , err := powerOn (r .machineScope )
215
236
if err != nil {
@@ -494,6 +515,13 @@ func (r *Reconciler) delete() error {
494
515
return fmt .Errorf ("%v: failed to destroy vm: %w" , r .machine .GetName (), err )
495
516
}
496
517
518
+ if r .machineScope .providerSpec .Workspace .VMGroup != "" {
519
+ klog .Infof ("Removing machine: %v from vm group: %v" , r .machine .Name , r .machineScope .providerSpec .Workspace .VMGroup )
520
+ if err := modifyVMGroup (r .machineScope , true ); err != nil {
521
+ return fmt .Errorf ("failed to remove machine from vm group: %w" , err )
522
+ }
523
+ }
524
+
497
525
if err := setProviderStatus (task .Reference ().Value , conditionSuccess (), r .machineScope , vm ); err != nil {
498
526
return fmt .Errorf ("failed to set provider status: %w" , err )
499
527
}
@@ -1036,6 +1064,86 @@ func clone(s *machineScope) (string, error) {
1036
1064
return taskVal , nil
1037
1065
}
1038
1066
1067
+ func modifyVMGroup (s * machineScope , delete bool ) error {
1068
+ vmRef , err := findVM (s )
1069
+ if err != nil {
1070
+ if isNotFound (err ) {
1071
+ return fmt .Errorf ("virtual machine %s was not found: %w" , s .machine .Name , err )
1072
+ }
1073
+ return fmt .Errorf ("error finding virtual machine: %w" , err )
1074
+ }
1075
+
1076
+ rp , err := s .session .Finder .ResourcePool (s .Context , s .providerSpec .Workspace .ResourcePool )
1077
+ if err != nil {
1078
+ return fmt .Errorf ("error getting resource pool %s: %w" , s .providerSpec .Workspace .ResourcePool , err )
1079
+ }
1080
+
1081
+ ownerRef , err := rp .Owner (s .Context )
1082
+ if err != nil {
1083
+ return fmt .Errorf ("error getting cluster owner reference from resource pool %s: %w" , s .providerSpec .Workspace .ResourcePool , err )
1084
+ }
1085
+
1086
+ var ccr * object.ClusterComputeResource
1087
+ var ok bool
1088
+ if ccr , ok = ownerRef .(* object.ClusterComputeResource ); ! ok {
1089
+ return fmt .Errorf ("error getting cluster from resource pool %s: %w" , s .providerSpec .Workspace .ResourcePool , err )
1090
+ }
1091
+
1092
+ clusterConfig , err := ccr .Configuration (s .Context )
1093
+ if err != nil {
1094
+ return fmt .Errorf ("error getting cluster %s configuration: %w" , s .providerSpec .Workspace .ResourcePool , err )
1095
+ }
1096
+
1097
+ var clusterVmGroup * types.ClusterVmGroup
1098
+
1099
+ for _ , g := range clusterConfig .Group {
1100
+ if vmg , ok := g .(* types.ClusterVmGroup ); ok {
1101
+ if vmg .Name == s .providerSpec .Workspace .VMGroup {
1102
+ clusterVmGroup = vmg
1103
+ break
1104
+ }
1105
+ }
1106
+ }
1107
+
1108
+ switch {
1109
+ case clusterVmGroup == nil :
1110
+ clusterVmGroup = & types.ClusterVmGroup {
1111
+ Vm : []types.ManagedObjectReference {vmRef },
1112
+ }
1113
+ case slices .Contains (clusterVmGroup .Vm , vmRef ) && delete :
1114
+ clusterVmGroup .Vm = slices .DeleteFunc (clusterVmGroup .Vm , func (ref types.ManagedObjectReference ) bool {
1115
+ return vmRef .Value == ref .Value
1116
+ })
1117
+ case ! slices .Contains (clusterVmGroup .Vm , vmRef ):
1118
+ clusterVmGroup .Vm = append (clusterVmGroup .Vm , vmRef )
1119
+ default :
1120
+ return nil
1121
+ }
1122
+
1123
+ clusterConfigSpec := & types.ClusterConfigSpecEx {
1124
+ GroupSpec : []types.ClusterGroupSpec {
1125
+ {
1126
+ ArrayUpdateSpec : types.ArrayUpdateSpec {
1127
+ Operation : types .ArrayUpdateOperation ("edit" ),
1128
+ },
1129
+ Info : & types.ClusterVmGroup {
1130
+ ClusterGroupInfo : types.ClusterGroupInfo {
1131
+ Name : s .providerSpec .Workspace .VMGroup ,
1132
+ },
1133
+ Vm : clusterVmGroup .Vm ,
1134
+ },
1135
+ },
1136
+ },
1137
+ }
1138
+
1139
+ clusterTask , err := ccr .Reconfigure (s .Context , clusterConfigSpec , true )
1140
+ if err != nil {
1141
+ return fmt .Errorf ("error reconfiguring cluster %s for vm-host group %s: %w" , ccr .Name (), clusterVmGroup .Name , err )
1142
+ }
1143
+
1144
+ return clusterTask .Wait (s .Context )
1145
+ }
1146
+
1039
1147
func powerOn (s * machineScope ) (string , error ) {
1040
1148
vmRef , err := findVM (s )
1041
1149
if err != nil {
0 commit comments