Skip to content

Add multiple nics support to nodes #448

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/v1beta1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 31 additions & 2 deletions api/v1beta2/cloudstackmachine_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,45 @@ limitations under the License.
package v1beta2

import (
machineryconversion "k8s.io/apimachinery/pkg/conversion"
"sigs.k8s.io/cluster-api-provider-cloudstack/api/v1beta3"
utilconversion "sigs.k8s.io/cluster-api/util/conversion"
"sigs.k8s.io/controller-runtime/pkg/conversion"
)

func (src *CloudStackMachine) ConvertTo(dstRaw conversion.Hub) error { // nolint
dst := dstRaw.(*v1beta3.CloudStackMachine)
return Convert_v1beta2_CloudStackMachine_To_v1beta3_CloudStackMachine(src, dst, nil)
if err := Convert_v1beta2_CloudStackMachine_To_v1beta3_CloudStackMachine(src, dst, nil); err != nil {
return err
}

// Manually restore data
restored := &v1beta3.CloudStackMachine{}
if ok, err := utilconversion.UnmarshalData(src, restored); err != nil || !ok {
return err
}

if len(restored.Spec.Networks) > 0 {
dst.Spec.Networks = restored.Spec.Networks
}

return nil
}

func (dst *CloudStackMachine) ConvertFrom(srcRaw conversion.Hub) error { // nolint
src := srcRaw.(*v1beta3.CloudStackMachine)
return Convert_v1beta3_CloudStackMachine_To_v1beta2_CloudStackMachine(src, dst, nil)
if err := Convert_v1beta3_CloudStackMachine_To_v1beta2_CloudStackMachine(src, dst, nil); err != nil {
return err
}

// Preserve Hub data on down-conversion, including Networks field
err := utilconversion.MarshalData(src, dst)
return err
}

// Convert_v1beta3_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec handles the conversion from v1beta3 to v1beta2,
// ignoring the Networks field that doesn't exist in v1beta2
func Convert_v1beta3_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(in *v1beta3.CloudStackMachineSpec, out *CloudStackMachineSpec, s machineryconversion.Scope) error { // nolint
// Use the auto-generated conversion function, which will handle all fields except Networks
return autoConvert_v1beta3_CloudStackMachineSpec_To_v1beta2_CloudStackMachineSpec(in, out, s)
}
40 changes: 28 additions & 12 deletions api/v1beta2/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions api/v1beta3/cloudstackmachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ const (
NoAffinity = "no"
)

type NetworkSpec struct {
// CloudStack Network Name (required to resolve ID)
Name string `json:"name"`

// Optional IP in the network
IP string `json:"ip,omitempty"`

// Optional Network ID (overrides Name if set)
ID string `json:"id,omitempty"`
}

// CloudStackMachineSpec defines the desired state of CloudStackMachine
type CloudStackMachineSpec struct {
// Name.
Expand All @@ -55,6 +66,11 @@ type CloudStackMachineSpec struct {
// +optional
DiskOffering CloudStackResourceDiskOffering `json:"diskOffering,omitempty"`

// The list of networks (overrides zone.network)
// +optional
// In CloudStackMachineSpec
Networks []NetworkSpec `json:"networks,omitempty"`

// CloudStack ssh key to use.
// +optional
SSHKey string `json:"sshKey"`
Expand Down
20 changes: 20 additions & 0 deletions api/v1beta3/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,25 @@ spec:
name:
description: Name.
type: string
networks:
description: |-
The list of networks (overrides zone.network)
In CloudStackMachineSpec
items:
properties:
id:
description: Optional Network ID (overrides Name if set)
type: string
ip:
description: Optional IP in the network
type: string
name:
description: CloudStack Network Name (required to resolve ID)
type: string
required:
- name
type: object
type: array
offering:
description: CloudStack compute offering.
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,27 @@ spec:
name:
description: Name.
type: string
networks:
description: |-
The list of networks (overrides zone.network)
In CloudStackMachineSpec
items:
properties:
id:
description: Optional Network ID (overrides Name if
set)
type: string
ip:
description: Optional IP in the network
type: string
name:
description: CloudStack Network Name (required to resolve
ID)
type: string
required:
- name
type: object
type: array
offering:
description: CloudStack compute offering.
properties:
Expand Down
44 changes: 43 additions & 1 deletion pkg/cloud/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,38 @@ func (c *client) CheckLimits(
return nil
}

func (c *client) resolveNetworkIDByName(name string) (string, error) {
net, count, err := c.cs.Network.GetNetworkByName(name, cloudstack.WithProject(c.user.Project.ID))
if err != nil {
return "", errors.Wrapf(err, "failed to look up network %q", name)
}
if count != 1 {
return "", errors.Errorf("expected 1 network named %q, but got %d", name, count)
}
return net.Id, nil
}

func (c *client) buildIPToNetworkList(csMachine *infrav1.CloudStackMachine) ([]map[string]string, error) {
ipToNetworkList := []map[string]string{}

for _, net := range csMachine.Spec.Networks {
networkID := net.ID
if networkID == "" {
var err error
networkID, err = c.resolveNetworkIDByName(net.Name)
if err != nil {
return nil, err
}
}
entry := map[string]string{"networkid": networkID}
if net.IP != "" {
entry["ip"] = net.IP
}
ipToNetworkList = append(ipToNetworkList, entry)
}
return ipToNetworkList, nil
}

// DeployVM will create a VM instance,
// and sets the infrastructure machine spec and status accordingly.
func (c *client) DeployVM(
Expand All @@ -322,7 +354,17 @@ func (c *client) DeployVM(
}

p := c.cs.VirtualMachine.NewDeployVirtualMachineParams(offering.Id, templateID, fd.Spec.Zone.ID)
p.SetNetworkids([]string{fd.Spec.Zone.Network.ID})

if len(csMachine.Spec.Networks) == 0 && fd.Spec.Zone.Network.ID != "" {
p.SetNetworkids([]string{fd.Spec.Zone.Network.ID})
} else {
ipToNetworkList, err := c.buildIPToNetworkList(csMachine)
if err != nil {
return err
}
p.SetIptonetworklist(ipToNetworkList)
}

setIfNotEmpty(csMachine.Name, p.SetName)
setIfNotEmpty(capiMachine.Name, p.SetDisplayname)
setIfNotEmpty(diskOfferingID, p.SetDiskofferingid)
Expand Down