Skip to content

Commit 81f7a7e

Browse files
committed
OCPSTRAT-915: Nutanix CAPI support
1 parent 4f77b17 commit 81f7a7e

File tree

16 files changed

+1268
-430
lines changed

16 files changed

+1268
-430
lines changed

data/data/cluster-api/nutanix-infrastructure-components.yaml

Lines changed: 626 additions & 401 deletions
Large diffs are not rendered by default.

pkg/asset/installconfig/nutanix/nutanix.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func getPrismElement(ctx context.Context, client *nutanixclientv3.Client) (*nuta
162162

163163
pe := &nutanixtypes.PrismElement{}
164164
emptyFilter := ""
165-
pesAll, err := client.V3.ListAllCluster(emptyFilter)
165+
pesAll, err := client.V3.ListAllCluster(ctx, emptyFilter)
166166
if err != nil {
167167
return nil, errors.Wrap(err, "unable to list prism element clusters")
168168
}
@@ -213,7 +213,7 @@ func getSubnet(ctx context.Context, client *nutanixclientv3.Client, peUUID strin
213213

214214
emptyFilter := ""
215215
emptyClientFilters := make([]*nutanixclient.AdditionalFilter, 0)
216-
subnetsAll, err := client.V3.ListAllSubnet(emptyFilter, emptyClientFilters)
216+
subnetsAll, err := client.V3.ListAllSubnet(ctx, emptyFilter, emptyClientFilters)
217217
if err != nil {
218218
return "", errors.Wrap(err, "unable to list subnets")
219219
}

pkg/asset/installconfig/nutanix/validation.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"strconv"
7+
"time"
78

89
"k8s.io/apimachinery/pkg/util/validation/field"
910

@@ -31,8 +32,10 @@ func ValidateForProvisioning(ic *types.InstallConfig) error {
3132
return field.Required(parentPath, "nutanix validation requires a nutanix platform configuration")
3233
}
3334

35+
ctx, cancel := context.WithTimeout(context.TODO(), 60*time.Second)
36+
defer cancel()
3437
p := ic.Platform.Nutanix
35-
nc, err := nutanixtypes.CreateNutanixClient(context.TODO(),
38+
nc, err := nutanixtypes.CreateNutanixClient(ctx,
3639
p.PrismCentral.Endpoint.Address,
3740
strconv.Itoa(int(p.PrismCentral.Endpoint.Port)),
3841
p.PrismCentral.Username,
@@ -43,15 +46,15 @@ func ValidateForProvisioning(ic *types.InstallConfig) error {
4346

4447
// validate whether a prism element with the UUID actually exists
4548
for _, pe := range p.PrismElements {
46-
_, err = nc.V3.GetCluster(pe.UUID)
49+
_, err = nc.V3.GetCluster(ctx, pe.UUID)
4750
if err != nil {
4851
errList = append(errList, field.Invalid(parentPath.Child("prismElements"), pe.UUID, fmt.Sprintf("the prism element %s's UUID does not correspond to a valid prism element in Prism: %v", pe.Name, err)))
4952
}
5053
}
5154

5255
// validate whether a subnet with the UUID actually exists
5356
for _, subnetUUID := range p.SubnetUUIDs {
54-
_, err = nc.V3.GetSubnet(subnetUUID)
57+
_, err = nc.V3.GetSubnet(ctx, subnetUUID)
5558
if err != nil {
5659
errList = append(errList, field.Invalid(parentPath.Child("subnetUUIDs"), subnetUUID, fmt.Sprintf("the subnet UUID does not correspond to a valid subnet in Prism: %v", err)))
5760
}
@@ -60,15 +63,15 @@ func ValidateForProvisioning(ic *types.InstallConfig) error {
6063
// validate each FailureDomain configuration
6164
for _, fd := range p.FailureDomains {
6265
// validate whether the prism element with the UUID exists
63-
_, err = nc.V3.GetCluster(fd.PrismElement.UUID)
66+
_, err = nc.V3.GetCluster(ctx, fd.PrismElement.UUID)
6467
if err != nil {
6568
errList = append(errList, field.Invalid(parentPath.Child("failureDomains", "prismElements"), fd.PrismElement.UUID,
6669
fmt.Sprintf("the failure domain %s configured prism element UUID does not correspond to a valid prism element in Prism: %v", fd.Name, err)))
6770
}
6871

6972
// validate whether a subnet with the UUID actually exists
7073
for _, subnetUUID := range fd.SubnetUUIDs {
71-
_, err = nc.V3.GetSubnet(subnetUUID)
74+
_, err = nc.V3.GetSubnet(ctx, subnetUUID)
7275
if err != nil {
7376
errList = append(errList, field.Invalid(parentPath.Child("failureDomains", "subnetUUIDs"), subnetUUID,
7477
fmt.Sprintf("the failure domain %s configured subnet UUID does not correspond to a valid subnet in Prism: %v", fd.Name, err)))

pkg/asset/machines/clusterapi.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/openshift/installer/pkg/asset/machines/aws"
2121
"github.com/openshift/installer/pkg/asset/machines/azure"
2222
"github.com/openshift/installer/pkg/asset/machines/gcp"
23+
nutanixcapi "github.com/openshift/installer/pkg/asset/machines/nutanix"
2324
"github.com/openshift/installer/pkg/asset/machines/openstack"
2425
"github.com/openshift/installer/pkg/asset/machines/powervs"
2526
vspherecapi "github.com/openshift/installer/pkg/asset/machines/vsphere"
@@ -33,6 +34,7 @@ import (
3334
azuretypes "github.com/openshift/installer/pkg/types/azure"
3435
azuredefaults "github.com/openshift/installer/pkg/types/azure/defaults"
3536
gcptypes "github.com/openshift/installer/pkg/types/gcp"
37+
nutanixtypes "github.com/openshift/installer/pkg/types/nutanix"
3638
openstacktypes "github.com/openshift/installer/pkg/types/openstack"
3739
powervstypes "github.com/openshift/installer/pkg/types/powervs"
3840
vspheretypes "github.com/openshift/installer/pkg/types/vsphere"
@@ -380,6 +382,21 @@ func (c *ClusterAPI) Generate(dependencies asset.Parents) error {
380382
}
381383

382384
c.FileList = append(c.FileList, powervsMachines...)
385+
case nutanixtypes.Name:
386+
mpool := defaultNutanixMachinePoolPlatform()
387+
mpool.NumCPUs = 8
388+
mpool.Set(ic.Platform.Nutanix.DefaultMachinePlatform)
389+
mpool.Set(pool.Platform.Nutanix)
390+
if err = mpool.ValidateConfig(ic.Platform.Nutanix); err != nil {
391+
return fmt.Errorf("failed to generate Cluster API machine manifests for control-plane: %w", err)
392+
}
393+
pool.Platform.Nutanix = &mpool
394+
templateName := nutanixtypes.RHCOSImageName(clusterID.InfraID)
395+
396+
c.FileList, err = nutanixcapi.GenerateMachines(clusterID.InfraID, ic, &pool, templateName, "master")
397+
if err != nil {
398+
return fmt.Errorf("unable to generate CAPI machines for Nutanix %w", err)
399+
}
383400
default:
384401
// TODO: support other platforms
385402
}
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// Package generates capi Machine objects for nutanix.
2+
package nutanix
3+
4+
import (
5+
"fmt"
6+
7+
capnv1 "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/api/v1beta1"
8+
v1 "k8s.io/api/core/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/utils/ptr"
11+
capv1 "sigs.k8s.io/cluster-api/api/v1beta1"
12+
13+
machinev1 "github.com/openshift/api/machine/v1"
14+
"github.com/openshift/installer/pkg/asset"
15+
"github.com/openshift/installer/pkg/asset/manifests/capiutils"
16+
"github.com/openshift/installer/pkg/types"
17+
nutanixtypes "github.com/openshift/installer/pkg/types/nutanix"
18+
)
19+
20+
const (
21+
masterRole = "master"
22+
)
23+
24+
// GenerateMachines returns manifests and runtime objects to provision the control plane (including bootstrap, if applicable) nodes using CAPI.
25+
func GenerateMachines(clusterID string, config *types.InstallConfig, pool *types.MachinePool, osImage string, role string) ([]*asset.RuntimeFile, error) {
26+
machines, _, err := Machines(clusterID, config, pool, osImage, role, "")
27+
if err != nil {
28+
return nil, fmt.Errorf("unable to retrieve machines: %w", err)
29+
}
30+
31+
categoryKey := nutanixtypes.CategoryKey(clusterID)
32+
categoryIdentifiers := []capnv1.NutanixCategoryIdentifier{
33+
{Key: categoryKey, Value: nutanixtypes.CategoryValueOwned},
34+
}
35+
ntxMachines := make([]*capnv1.NutanixMachine, 0, len(machines))
36+
result := make([]*asset.RuntimeFile, 0, 2*len(machines))
37+
38+
for _, machine := range machines {
39+
providerSpec, ok := machine.Spec.ProviderSpec.Value.Object.(*machinev1.NutanixMachineProviderConfig)
40+
if !ok {
41+
return nil, fmt.Errorf("unable to convert ProviderSpec to NutanixMachineProviderConfig")
42+
}
43+
44+
// create the NutanixMachine object.
45+
ntxMachine := generateNutanixMachine(machine.Name, providerSpec, categoryIdentifiers)
46+
ntxMachines = append(ntxMachines, ntxMachine)
47+
result = append(result, &asset.RuntimeFile{
48+
File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", ntxMachine.Name)},
49+
Object: ntxMachine,
50+
})
51+
52+
// create the capi Machine object.
53+
capiMachine := &capv1.Machine{
54+
ObjectMeta: metav1.ObjectMeta{
55+
Namespace: capiutils.Namespace,
56+
Name: ntxMachine.Name,
57+
Labels: map[string]string{
58+
"cluster.x-k8s.io/control-plane": "",
59+
},
60+
},
61+
Spec: capv1.MachineSpec{
62+
ClusterName: clusterID,
63+
Bootstrap: capv1.Bootstrap{
64+
DataSecretName: ptr.To(fmt.Sprintf("%s-%s", clusterID, role)),
65+
},
66+
InfrastructureRef: v1.ObjectReference{
67+
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
68+
Kind: "NutanixMachine",
69+
Name: ntxMachine.Name,
70+
},
71+
},
72+
}
73+
capiMachine.SetGroupVersionKind(capv1.GroupVersion.WithKind("Machine"))
74+
75+
result = append(result, &asset.RuntimeFile{
76+
File: asset.File{Filename: fmt.Sprintf("10_machine_%s.yaml", capiMachine.Name)},
77+
Object: capiMachine,
78+
})
79+
}
80+
81+
// as part of provisioning control plane nodes, we need to create a bootstrap node as well.
82+
if role == masterRole {
83+
bootstrapSpec := ntxMachines[0].Spec.DeepCopy()
84+
bootstrapSpec.VCPUsPerSocket = 4
85+
bootstrapSpec.VCPUSockets = 1
86+
bootstrapImgName := nutanixtypes.BootISOImageName(clusterID)
87+
bootstrapSpec.BootstrapRef = &v1.ObjectReference{
88+
Kind: capnv1.NutanixMachineBootstrapRefKindImage,
89+
Name: bootstrapImgName,
90+
}
91+
92+
bootstrapNtxMachine := &capnv1.NutanixMachine{
93+
ObjectMeta: metav1.ObjectMeta{
94+
Namespace: capiutils.Namespace,
95+
Name: capiutils.GenerateBoostrapMachineName(clusterID),
96+
Labels: map[string]string{
97+
"cluster.x-k8s.io/control-plane": "",
98+
"install.openshift.io/bootstrap": "",
99+
},
100+
},
101+
Spec: *bootstrapSpec,
102+
}
103+
bootstrapNtxMachine.SetGroupVersionKind(capnv1.GroupVersion.WithKind("NutanixMachine"))
104+
105+
result = append(result, &asset.RuntimeFile{
106+
File: asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", bootstrapNtxMachine.Name)},
107+
Object: bootstrapNtxMachine,
108+
})
109+
110+
bootstrapCapiMachine := &capv1.Machine{
111+
ObjectMeta: metav1.ObjectMeta{
112+
Namespace: capiutils.Namespace,
113+
Name: bootstrapNtxMachine.Name,
114+
Labels: map[string]string{
115+
"cluster.x-k8s.io/control-plane": "",
116+
},
117+
},
118+
Spec: capv1.MachineSpec{
119+
ClusterName: clusterID,
120+
Bootstrap: capv1.Bootstrap{
121+
DataSecretName: ptr.To(fmt.Sprintf("%s-bootstrap", clusterID)),
122+
},
123+
InfrastructureRef: v1.ObjectReference{
124+
APIVersion: "infrastructure.cluster.x-k8s.io/v1beta1",
125+
Kind: "NutanixMachine",
126+
Name: bootstrapNtxMachine.Name,
127+
},
128+
},
129+
}
130+
bootstrapCapiMachine.SetGroupVersionKind(capv1.GroupVersion.WithKind("Machine"))
131+
132+
result = append(result, &asset.RuntimeFile{
133+
File: asset.File{Filename: fmt.Sprintf("10_machine_%s.yaml", bootstrapCapiMachine.Name)},
134+
Object: bootstrapCapiMachine,
135+
})
136+
}
137+
138+
return result, nil
139+
}
140+
141+
func generateNutanixMachine(machineName string, providerSpec *machinev1.NutanixMachineProviderConfig, categoryIdentifiers []capnv1.NutanixCategoryIdentifier) *capnv1.NutanixMachine {
142+
ntxMachine := &capnv1.NutanixMachine{
143+
ObjectMeta: metav1.ObjectMeta{
144+
Namespace: capiutils.Namespace,
145+
Name: machineName,
146+
Labels: map[string]string{
147+
"cluster.x-k8s.io/control-plane": "",
148+
},
149+
},
150+
Spec: capnv1.NutanixMachineSpec{
151+
VCPUsPerSocket: providerSpec.VCPUsPerSocket,
152+
VCPUSockets: providerSpec.VCPUSockets,
153+
MemorySize: providerSpec.MemorySize,
154+
SystemDiskSize: providerSpec.SystemDiskSize,
155+
Image: capnv1.NutanixResourceIdentifier{
156+
Type: capnv1.NutanixIdentifierType(providerSpec.Image.Type),
157+
Name: providerSpec.Image.Name,
158+
UUID: providerSpec.Image.UUID,
159+
},
160+
Cluster: capnv1.NutanixResourceIdentifier{
161+
Type: capnv1.NutanixIdentifierType(providerSpec.Cluster.Type),
162+
Name: providerSpec.Cluster.Name,
163+
UUID: providerSpec.Cluster.UUID,
164+
},
165+
Subnets: []capnv1.NutanixResourceIdentifier{},
166+
AdditionalCategories: categoryIdentifiers,
167+
BootType: capnv1.NutanixBootType(providerSpec.BootType),
168+
},
169+
}
170+
ntxMachine.SetGroupVersionKind(capnv1.GroupVersion.WithKind("NutanixMachine"))
171+
172+
for _, subnet := range providerSpec.Subnets {
173+
ntxMachine.Spec.Subnets = append(ntxMachine.Spec.Subnets, capnv1.NutanixResourceIdentifier{
174+
Type: capnv1.NutanixIdentifierType(subnet.Type),
175+
Name: subnet.Name,
176+
UUID: subnet.UUID,
177+
})
178+
}
179+
180+
for _, category := range providerSpec.Categories {
181+
ntxMachine.Spec.AdditionalCategories = append(ntxMachine.Spec.AdditionalCategories,
182+
capnv1.NutanixCategoryIdentifier{
183+
Key: category.Key,
184+
Value: category.Value,
185+
})
186+
}
187+
188+
if providerSpec.BootType != "" {
189+
ntxMachine.Spec.BootType = capnv1.NutanixBootType(providerSpec.BootType)
190+
}
191+
192+
if providerSpec.Project.Type != "" {
193+
ntxMachine.Spec.Project = &capnv1.NutanixResourceIdentifier{
194+
Type: capnv1.NutanixIdentifierType(providerSpec.Project.Type),
195+
Name: providerSpec.Project.Name,
196+
UUID: providerSpec.Project.UUID,
197+
}
198+
}
199+
200+
return ntxMachine
201+
}

pkg/asset/manifests/clusterapi/cluster.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/openshift/installer/pkg/asset/manifests/azure"
2121
"github.com/openshift/installer/pkg/asset/manifests/capiutils"
2222
"github.com/openshift/installer/pkg/asset/manifests/gcp"
23+
"github.com/openshift/installer/pkg/asset/manifests/nutanix"
2324
"github.com/openshift/installer/pkg/asset/manifests/openstack"
2425
"github.com/openshift/installer/pkg/asset/manifests/powervs"
2526
"github.com/openshift/installer/pkg/asset/manifests/vsphere"
@@ -29,6 +30,7 @@ import (
2930
awstypes "github.com/openshift/installer/pkg/types/aws"
3031
azuretypes "github.com/openshift/installer/pkg/types/azure"
3132
gcptypes "github.com/openshift/installer/pkg/types/gcp"
33+
nutanixtypes "github.com/openshift/installer/pkg/types/nutanix"
3234
openstacktypes "github.com/openshift/installer/pkg/types/openstack"
3335
powervstypes "github.com/openshift/installer/pkg/types/powervs"
3436
vsphereplatform "github.com/openshift/installer/pkg/types/vsphere"
@@ -132,6 +134,12 @@ func (c *Cluster) Generate(dependencies asset.Parents) error {
132134
if err != nil {
133135
return fmt.Errorf("failed to generate PowerVS manifests %w", err)
134136
}
137+
case nutanixtypes.Name:
138+
var err error
139+
out, err = nutanix.GenerateClusterAssets(installConfig, clusterID)
140+
if err != nil {
141+
return errors.Wrap(err, "failed to generate Nutanix manifests")
142+
}
135143
default:
136144
return fmt.Errorf("unsupported platform %q", platform)
137145
}

0 commit comments

Comments
 (0)