Skip to content

Commit

Permalink
Finalize network v1a2 naming convention
Browse files Browse the repository at this point in the history
This change checks for v1a1 network name'd interfaces before creating or updating the new ones.
  • Loading branch information
sreyasn committed Oct 18, 2023
1 parent 30c3741 commit a84a865
Show file tree
Hide file tree
Showing 2 changed files with 251 additions and 32 deletions.
83 changes: 54 additions & 29 deletions pkg/vmprovider/providers/vsphere2/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/vmware/govmomi/vim25"
vimtypes "github.com/vmware/govmomi/vim25/types"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
Expand Down Expand Up @@ -259,30 +260,42 @@ func createNetOPNetworkInterface(
return nil, fmt.Errorf("network kind %q is not supported for VDS", kind)
}

var err error
// If empty, NetOP will try to select a namespace default.
networkName := interfaceSpec.Network.Name
netIf := &netopv1alpha1.NetworkInterface{}
netIfKey := types.NamespacedName{
Namespace: vmCtx.VM.Namespace,
Name: NetOPCRName(vmCtx.VM.Name, networkName, interfaceSpec.Name, true),
}

netIf := &netopv1alpha1.NetworkInterface{
ObjectMeta: metav1.ObjectMeta{
Name: NetOPCRName(vmCtx.VM.Name, networkName, interfaceSpec.Name, false),
Namespace: vmCtx.VM.Namespace,
},
// check if a networkIf object exists with the older (v1a1) naming convention
if err = client.Get(vmCtx, netIfKey, netIf); ctrlruntime.IgnoreNotFound(err) != nil {
return nil, err
}

_, err := controllerutil.CreateOrUpdate(vmCtx, client, netIf, func() error {
if err := controllerutil.SetOwnerReference(vmCtx.VM, netIf, client.Scheme()); err != nil {
// If this fails we likely have an object name collision, and we're in a tough spot.
return err
// If object with the older (v1a1) name is not found, use the new naming convention to create/update one.
if apierrors.IsNotFound(err) {
netIf.ObjectMeta = metav1.ObjectMeta{
Name: NetOPCRName(vmCtx.VM.Name, networkName, interfaceSpec.Name, false),
Namespace: vmCtx.VM.Namespace,
}

netIf.Spec.NetworkName = networkName
// NetOP only defines a VMXNet3 type, but it doesn't really matter for our purposes.
netIf.Spec.Type = netopv1alpha1.NetworkInterfaceTypeVMXNet3
return nil
})
_, err := controllerutil.CreateOrUpdate(vmCtx, client, netIf, func() error {
if err := controllerutil.SetOwnerReference(vmCtx.VM, netIf, client.Scheme()); err != nil {
// If this fails we likely have an object name collision, and we're in a tough spot.
return err
}

if err != nil {
return nil, err
netIf.Spec.NetworkName = networkName
// NetOP only defines a VMXNet3 type, but it doesn't really matter for our purposes.
netIf.Spec.Type = netopv1alpha1.NetworkInterfaceTypeVMXNet3
return nil
})

if err != nil {
return nil, err
}
}

netIf, err = waitForReadyNetworkInterface(vmCtx, client, netIf.Name)
Expand Down Expand Up @@ -402,27 +415,39 @@ func createNCPNetworkInterface(
return nil, fmt.Errorf("network kind %q is not supported for NCP", kind)
}

var err error
// If empty, NCP will use the namespace default.
networkName := interfaceSpec.Network.Name
vnetIf := &ncpv1alpha1.VirtualNetworkInterface{}
vnetIfKey := types.NamespacedName{
Namespace: vmCtx.VM.Namespace,
Name: NCPCRName(vmCtx.VM.Name, networkName, interfaceSpec.Name, true),
}

vnetIf := &ncpv1alpha1.VirtualNetworkInterface{
ObjectMeta: metav1.ObjectMeta{
Name: NCPCRName(vmCtx.VM.Name, networkName, interfaceSpec.Name, false),
Namespace: vmCtx.VM.Namespace,
},
// check if a networkIf object exists with the older (v1a1) naming convention
if err = client.Get(vmCtx, vnetIfKey, vnetIf); ctrlruntime.IgnoreNotFound(err) != nil {
return nil, err
}

_, err := controllerutil.CreateOrUpdate(vmCtx, client, vnetIf, func() error {
if err := controllerutil.SetOwnerReference(vmCtx.VM, vnetIf, client.Scheme()); err != nil {
return err
// If object with the older (v1a1) name is not found, use the new naming convention to create/update one.
if apierrors.IsNotFound(err) {
vnetIf.ObjectMeta = metav1.ObjectMeta{
Name: NCPCRName(vmCtx.VM.Name, networkName, interfaceSpec.Name, false),
Namespace: vmCtx.VM.Namespace,
}

vnetIf.Spec.VirtualNetwork = networkName
return nil
})
_, err := controllerutil.CreateOrUpdate(vmCtx, client, vnetIf, func() error {
if err := controllerutil.SetOwnerReference(vmCtx.VM, vnetIf, client.Scheme()); err != nil {
return err
}

if err != nil {
return nil, err
vnetIf.Spec.VirtualNetwork = networkName
return nil
})

if err != nil {
return nil, err
}
}

vnetIf, err = waitForReadyNCPNetworkInterface(vmCtx, client, vnetIf.Name)
Expand Down
200 changes: 197 additions & 3 deletions pkg/vmprovider/providers/vsphere2/network/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ var _ = Describe("CreateAndWaitForNetworkInterfaces", func() {
vm *vmopv1.VirtualMachine
interfaceSpecs []vmopv1.VirtualMachineNetworkInterfaceSpec

results network.NetworkInterfaceResults
err error
results network.NetworkInterfaceResults
err error
initObjects []client.Object
)

BeforeEach(func() {
Expand All @@ -59,7 +60,7 @@ var _ = Describe("CreateAndWaitForNetworkInterfaces", func() {
})

JustBeforeEach(func() {
ctx = suite.NewTestContextForVCSim(testConfig)
ctx = suite.NewTestContextForVCSim(testConfig, initObjects...)

results, err = network.CreateAndWaitForNetworkInterfaces(
vmCtx,
Expand All @@ -73,6 +74,7 @@ var _ = Describe("CreateAndWaitForNetworkInterfaces", func() {
AfterEach(func() {
ctx.AfterEach()
ctx = nil
initObjects = nil
})

Context("Named Network", func() {
Expand Down Expand Up @@ -225,6 +227,95 @@ var _ = Describe("CreateAndWaitForNetworkInterfaces", func() {
Expect(ipConfig.IsIPv4).To(BeFalse())
Expect(ipConfig.Gateway).To(Equal("fd1a:6c85:79fe:7c98:0000:0000:0000:0001"))
})

When("v1a1 network interface exists", func() {
BeforeEach(func() {
netIf := &netopv1alpha1.NetworkInterface{
ObjectMeta: metav1.ObjectMeta{
Name: network.NetOPCRName(vm.Name, networkName, interfaceName, true),
Namespace: vm.Namespace,
},
Spec: netopv1alpha1.NetworkInterfaceSpec{
NetworkName: networkName,
Type: netopv1alpha1.NetworkInterfaceTypeVMXNet3,
},
}

initObjects = append(initObjects, netIf)
})

It("returns success", func() {
// Assert test env is what we expect.
Expect(ctx.NetworkRef.Reference().Type).To(Equal("DistributedVirtualPortgroup"))

Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("network interface is not ready yet"))
Expect(results.Results).To(BeEmpty())

By("simulate successful NetOP reconcile", func() {
netInterface := &netopv1alpha1.NetworkInterface{
ObjectMeta: metav1.ObjectMeta{
Name: network.NetOPCRName(vm.Name, networkName, interfaceName, true),
Namespace: vm.Namespace,
},
}
Expect(ctx.Client.Get(ctx, client.ObjectKeyFromObject(netInterface), netInterface)).To(Succeed())
Expect(netInterface.Spec.NetworkName).To(Equal(networkName))

netInterface.Status.NetworkID = ctx.NetworkRef.Reference().Value
netInterface.Status.MacAddress = "" // NetOP doesn't set this.
netInterface.Status.IPConfigs = []netopv1alpha1.IPConfig{
{
IP: "192.168.1.110",
IPFamily: netopv1alpha1.IPv4Protocol,
Gateway: "192.168.1.1",
SubnetMask: "255.255.255.0",
},
{
IP: "fd1a:6c85:79fe:7c98:0000:0000:0000:000f",
IPFamily: netopv1alpha1.IPv6Protocol,
Gateway: "fd1a:6c85:79fe:7c98:0000:0000:0000:0001",
SubnetMask: "ffff:ffff:ffff:ff00:0000:0000:0000:0000",
},
}
netInterface.Status.Conditions = []netopv1alpha1.NetworkInterfaceCondition{
{
Type: netopv1alpha1.NetworkInterfaceReady,
Status: corev1.ConditionTrue,
},
}
Expect(ctx.Client.Status().Update(ctx, netInterface)).To(Succeed())
})

results, err = network.CreateAndWaitForNetworkInterfaces(
vmCtx,
ctx.Client,
ctx.VCClient.Client,
ctx.Finder,
nil,
interfaceSpecs)
Expect(err).ToNot(HaveOccurred())

Expect(results.Results).To(HaveLen(1))
result := results.Results[0]
Expect(result.MacAddress).To(BeEmpty())
Expect(result.ExternalID).To(BeEmpty())
Expect(result.NetworkID).To(Equal(ctx.NetworkRef.Reference().Value))
Expect(result.Backing).ToNot(BeNil())
Expect(result.Backing.Reference()).To(Equal(ctx.NetworkRef.Reference()))
Expect(result.Name).To(Equal(interfaceName))

Expect(result.IPConfigs).To(HaveLen(2))
ipConfig := result.IPConfigs[0]
Expect(ipConfig.IPCIDR).To(Equal("192.168.1.110/24"))
Expect(ipConfig.IsIPv4).To(BeTrue())
Expect(ipConfig.Gateway).To(Equal("192.168.1.1"))
ipConfig = result.IPConfigs[1]
Expect(ipConfig.IPCIDR).To(Equal("fd1a:6c85:79fe:7c98::f/56"))
Expect(ipConfig.IsIPv4).To(BeFalse())
Expect(ipConfig.Gateway).To(Equal("fd1a:6c85:79fe:7c98:0000:0000:0000:0001"))
})
})
})
})

Expand Down Expand Up @@ -339,6 +430,109 @@ var _ = Describe("CreateAndWaitForNetworkInterfaces", func() {
Expect(results.Results[0].Backing).ToNot(BeNil())
Expect(results.Results[0].Backing.Reference()).To(Equal(ctx.NetworkRef.Reference()))
})

When("v1a1 NCP network interface exists", func() {
BeforeEach(func() {
vnetIf := &ncpv1alpha1.VirtualNetworkInterface{
ObjectMeta: metav1.ObjectMeta{
Name: network.NCPCRName(vm.Name, networkName, interfaceName, true),
Namespace: vm.Namespace,
},
Spec: ncpv1alpha1.VirtualNetworkInterfaceSpec{
VirtualNetwork: networkName,
},
}

initObjects = append(initObjects, vnetIf)
})

It("returns success", func() {
// Assert test env is what we expect.
Expect(ctx.NetworkRef.Reference().Type).To(Equal("DistributedVirtualPortgroup"))

Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("network interface is not ready yet"))
Expect(results.Results).To(BeEmpty())

By("simulate successful NCP reconcile", func() {
netInterface := &ncpv1alpha1.VirtualNetworkInterface{
ObjectMeta: metav1.ObjectMeta{
Name: network.NCPCRName(vm.Name, networkName, interfaceName, true),
Namespace: vm.Namespace,
},
}
Expect(ctx.Client.Get(ctx, client.ObjectKeyFromObject(netInterface), netInterface)).To(Succeed())
Expect(netInterface.Spec.VirtualNetwork).To(Equal(networkName))

netInterface.Status.InterfaceID = interfaceID
netInterface.Status.MacAddress = macAddress
netInterface.Status.ProviderStatus = &ncpv1alpha1.VirtualNetworkInterfaceProviderStatus{
NsxLogicalSwitchID: builder.NsxTLogicalSwitchUUID,
}
netInterface.Status.IPAddresses = []ncpv1alpha1.VirtualNetworkInterfaceIP{
{
IP: "192.168.1.110",
Gateway: "192.168.1.1",
SubnetMask: "255.255.255.0",
},
{
IP: "fd1a:6c85:79fe:7c98:0000:0000:0000:000f",
Gateway: "fd1a:6c85:79fe:7c98:0000:0000:0000:0001",
SubnetMask: "ffff:ffff:ffff:ff00:0000:0000:0000:0000",
},
}
netInterface.Status.Conditions = []ncpv1alpha1.VirtualNetworkCondition{
{
Type: "Ready",
Status: "True",
},
}
Expect(ctx.Client.Status().Update(ctx, netInterface)).To(Succeed())
})

results, err = network.CreateAndWaitForNetworkInterfaces(
vmCtx,
ctx.Client,
ctx.VCClient.Client,
ctx.Finder,
nil,
interfaceSpecs)
Expect(err).ToNot(HaveOccurred())

Expect(results.Results).To(HaveLen(1))
result := results.Results[0]
Expect(result.MacAddress).To(Equal(macAddress))
Expect(result.ExternalID).To(Equal(interfaceID))
Expect(result.NetworkID).To(Equal(builder.NsxTLogicalSwitchUUID))
Expect(result.Name).To(Equal(interfaceName))

Expect(result.IPConfigs).To(HaveLen(2))
ipConfig := result.IPConfigs[0]
Expect(ipConfig.IPCIDR).To(Equal("192.168.1.110/24"))
Expect(ipConfig.IsIPv4).To(BeTrue())
Expect(ipConfig.Gateway).To(Equal("192.168.1.1"))
ipConfig = result.IPConfigs[1]
Expect(ipConfig.IPCIDR).To(Equal("fd1a:6c85:79fe:7c98::f/56"))
Expect(ipConfig.IsIPv4).To(BeFalse())
Expect(ipConfig.Gateway).To(Equal("fd1a:6c85:79fe:7c98:0000:0000:0000:0001"))

// Without the ClusterMoRef on the first call this will be nil for NSXT.
Expect(result.Backing).To(BeNil())

clusterMoRef := ctx.GetSingleClusterCompute().Reference()
results, err = network.CreateAndWaitForNetworkInterfaces(
vmCtx,
ctx.Client,
ctx.VCClient.Client,
ctx.Finder,
&clusterMoRef,
interfaceSpecs)
Expect(err).ToNot(HaveOccurred())
Expect(results.Results).To(HaveLen(1))
Expect(results.Results[0].Backing).ToNot(BeNil())
Expect(results.Results[0].Backing.Reference()).To(Equal(ctx.NetworkRef.Reference()))
})
})
})
})
})

0 comments on commit a84a865

Please sign in to comment.