@@ -1533,7 +1533,8 @@ func verifyResourcePool(vmCtx pkgctx.VirtualMachineContext) error {
15331533 return nil
15341534}
15351535
1536- // vmCreateDoPlacement determines placement of the VM prior to creating the VM on VC.
1536+ // vmCreateDoPlacement determines placement of the VM prior to creating the VM
1537+ // on VC. If VM has a group name specified, placement is determined by group.
15371538func (vs * vSphereVMProvider ) vmCreateDoPlacement (
15381539 vmCtx pkgctx.VirtualMachineContext ,
15391540 vcClient * vcclient.Client ,
@@ -1546,9 +1547,23 @@ func (vs *vSphereVMProvider) vmCreateDoPlacement(
15461547 vmopv1 .VirtualMachineConditionPlacementReady ,
15471548 "NotReady" ,
15481549 retErr )
1550+ } else {
1551+ pkgcnd .MarkTrue (
1552+ vmCtx .VM ,
1553+ vmopv1 .VirtualMachineConditionPlacementReady )
15491554 }
15501555 }()
15511556
1557+ if pkgcfg .FromContext (vmCtx ).Features .VMGroups &&
1558+ vmCtx .VM .Spec .GroupName != "" {
1559+ vmCtx .Logger .Info (
1560+ "Getting VM placement result from its group" ,
1561+ "groupName" , vmCtx .VM .Spec .GroupName ,
1562+ )
1563+
1564+ return vs .vmCreateDoPlacementByGroup (vmCtx , vcClient , createArgs )
1565+ }
1566+
15521567 placementConfigSpec , err := virtualmachine .CreateConfigSpecForPlacement (
15531568 vmCtx ,
15541569 createArgs .ConfigSpec ,
@@ -1580,6 +1595,124 @@ func (vs *vSphereVMProvider) vmCreateDoPlacement(
15801595 return err
15811596 }
15821597
1598+ return processPlacementResult (vmCtx , vcClient , createArgs , * result )
1599+ }
1600+
1601+ // vmCreateDoPlacementByGroup places the VM from the group's placement result.
1602+ func (vs * vSphereVMProvider ) vmCreateDoPlacementByGroup (
1603+ vmCtx pkgctx.VirtualMachineContext ,
1604+ vcClient * vcclient.Client ,
1605+ createArgs * VMCreateArgs ) error {
1606+
1607+ // Should never happen when this function is called, check just in case.
1608+ if vmCtx .VM .Spec .GroupName == "" {
1609+ return fmt .Errorf ("VM.Spec.GroupName is empty" )
1610+ }
1611+
1612+ var vmg vmopv1.VirtualMachineGroup
1613+ if err := vs .k8sClient .Get (
1614+ vmCtx ,
1615+ ctrlclient.ObjectKey {
1616+ Namespace : vmCtx .VM .Namespace ,
1617+ Name : vmCtx .VM .Spec .GroupName ,
1618+ },
1619+ & vmg ,
1620+ ); err != nil {
1621+ return fmt .Errorf ("failed to get VM Group object: %w" , err )
1622+ }
1623+
1624+ var memberStatus vmopv1.VirtualMachineGroupMemberStatus
1625+ for _ , m := range vmg .Status .Members {
1626+ if m .Kind == vmCtx .VM .Kind && m .Name == vmCtx .VM .Name {
1627+ memberStatus = m
1628+ break
1629+ }
1630+ }
1631+
1632+ if ! pkgcnd .IsTrue (
1633+ & memberStatus ,
1634+ vmopv1 .VirtualMachineGroupMemberConditionGroupLinked ,
1635+ ) {
1636+ return fmt .Errorf ("VM is not linked to its group" )
1637+ }
1638+
1639+ if ! pkgcnd .IsTrue (
1640+ & memberStatus ,
1641+ vmopv1 .VirtualMachineGroupMemberConditionPlacementReady ,
1642+ ) {
1643+ return fmt .Errorf ("VM Group placement is not ready" )
1644+ }
1645+
1646+ placementStatus := memberStatus .Placement
1647+ if placementStatus == nil {
1648+ return fmt .Errorf ("VM Group placement is empty" )
1649+ }
1650+
1651+ vmCtx .Logger .V (6 ).Info (
1652+ "VM Group placement is ready" ,
1653+ "placement" , placementStatus ,
1654+ )
1655+
1656+ // Create a placement result from the group's placement status.
1657+ var result placement.Result
1658+
1659+ if placementStatus .Zone != "" {
1660+ result .ZonePlacement = true
1661+ result .ZoneName = placementStatus .Zone
1662+ }
1663+
1664+ if placementStatus .Node != "" {
1665+ result .HostMoRef = & vimtypes.ManagedObjectReference {
1666+ Type : string (vimtypes .ManagedObjectTypesHostSystem ),
1667+ Value : placementStatus .Node ,
1668+ }
1669+ }
1670+
1671+ if placementStatus .Pool != "" {
1672+ result .PoolMoRef = vimtypes.ManagedObjectReference {
1673+ Type : string (vimtypes .ManagedObjectTypesResourcePool ),
1674+ Value : placementStatus .Pool ,
1675+ }
1676+ }
1677+
1678+ result .Datastores = make ([]placement.DatastoreResult , len (placementStatus .Datastores ))
1679+ for i , ds := range placementStatus .Datastores {
1680+ if val := ds .DiskKey ; val != nil {
1681+ result .Datastores [i ].DiskKey = * val
1682+ result .Datastores [i ].ForDisk = true
1683+ }
1684+
1685+ result .Datastores [i ].MoRef = vimtypes.ManagedObjectReference {
1686+ Type : string (vimtypes .ManagedObjectTypesDatastore ),
1687+ Value : ds .ID ,
1688+ }
1689+
1690+ result .Datastores [i ].Name = ds .Name
1691+ result .Datastores [i ].URL = ds .URL
1692+ result .Datastores [i ].DiskFormats = ds .SupportedDiskFormats
1693+ }
1694+
1695+ // InstanceStoragePlacement flag is needed to update the VM's annotations
1696+ // with the selected host for VMs that have instance storage backed volumes.
1697+ // They're likely not supported for group placement. Leave it here for now
1698+ // to keep consistent with the single VM placement workflow.
1699+ if pkgcfg .FromContext (vmCtx ).Features .InstanceStorage {
1700+ if vmopv1util .IsInstanceStoragePresent (vmCtx .VM ) {
1701+ result .InstanceStoragePlacement = true
1702+ }
1703+ }
1704+
1705+ return processPlacementResult (vmCtx , vcClient , createArgs , result )
1706+ }
1707+
1708+ // processPlacementResult updates the createArgs and VM annotations/labels with
1709+ // the given placement result.
1710+ func processPlacementResult (
1711+ vmCtx pkgctx.VirtualMachineContext ,
1712+ vcClient * vcclient.Client ,
1713+ createArgs * VMCreateArgs ,
1714+ result placement.Result ) error {
1715+
15831716 if result .PoolMoRef .Value != "" {
15841717 createArgs .ResourcePoolMoID = result .PoolMoRef .Value
15851718 }
@@ -1633,8 +1766,6 @@ func (vs *vSphereVMProvider) vmCreateDoPlacement(
16331766 }
16341767 }
16351768
1636- pkgcnd .MarkTrue (vmCtx .VM , vmopv1 .VirtualMachineConditionPlacementReady )
1637-
16381769 return nil
16391770}
16401771
0 commit comments