diff --git a/api/core/v1alpha2/virtual_machine.go b/api/core/v1alpha2/virtual_machine.go index d1aff42062..554eea221b 100644 --- a/api/core/v1alpha2/virtual_machine.go +++ b/api/core/v1alpha2/virtual_machine.go @@ -266,6 +266,7 @@ const ( ) type NetworksSpec struct { + ID int `json:"id,omitempty"` Type string `json:"type"` Name string `json:"name,omitempty"` VirtualMachineMACAddressName string `json:"virtualMachineMACAddressName,omitempty"` @@ -429,6 +430,7 @@ type Versions struct { } type NetworksStatus struct { + ID int `json:"id,omitempty"` Type string `json:"type"` Name string `json:"name,omitempty"` MAC string `json:"macAddress,omitempty"` diff --git a/crds/doc-ru-virtualmachines.yaml b/crds/doc-ru-virtualmachines.yaml index 281876d766..3f0c1e28a6 100644 --- a/crds/doc-ru-virtualmachines.yaml +++ b/crds/doc-ru-virtualmachines.yaml @@ -552,6 +552,9 @@ spec: Модуль `sdn` требуется, если в `.spec.networks` указаны дополнительные сети (`Network` или `ClusterNetwork`). Если указана только основная сеть (`Main`), модуль `sdn` не требуется. items: properties: + id: + description: | + ID сетевого интерфейса. type: description: | Тип сетевого интерфейса. @@ -781,6 +784,9 @@ spec: Список сетевых интерфейсов, подключенных к ВМ. items: properties: + id: + description: | + ID сетевого интерфейса. type: description: | Тип сетевого интерфейса. diff --git a/crds/virtualmachines.yaml b/crds/virtualmachines.yaml index a55a3c7f20..d09aeadfd7 100644 --- a/crds/virtualmachines.yaml +++ b/crds/virtualmachines.yaml @@ -983,6 +983,10 @@ spec: required: - type properties: + id: + type: integer + description: | + The ID of the network interface. type: type: string description: | @@ -1391,6 +1395,10 @@ spec: required: - type properties: + id: + type: integer + description: | + The ID of the network interface. type: type: string description: | diff --git a/images/virtualization-artifact/pkg/common/network/network.go b/images/virtualization-artifact/pkg/common/network/network.go index ba92f1e812..0d3cae925a 100644 --- a/images/virtualization-artifact/pkg/common/network/network.go +++ b/images/virtualization-artifact/pkg/common/network/network.go @@ -50,6 +50,7 @@ func HasMainNetworkSpec(networks []v1alpha2.NetworksSpec) bool { } type InterfaceSpec struct { + ID int `json:"id"` Type string `json:"type"` Name string `json:"name"` InterfaceName string `json:"ifName"` @@ -66,65 +67,87 @@ type InterfaceStatus struct { type InterfaceSpecList []InterfaceSpec -func CreateNetworkSpec(vm *v1alpha2.VirtualMachine, vmmacs []*v1alpha2.VirtualMachineMACAddress) InterfaceSpecList { - var ( - all []string - status []struct{ Name, MAC string } - taken = make(map[string]bool) - free []string - res InterfaceSpecList - freeIdx int - ) +type MacAddressPool struct { + reservedByName map[string]string + available []string +} + +func NewMacAddressPool(vm *v1alpha2.VirtualMachine, vmmacs []*v1alpha2.VirtualMachineMACAddress) *MacAddressPool { + reservedByName := make(map[string]string) + takenMacs := make(map[string]bool) - for _, v := range vmmacs { - if mac := v.Status.Address; mac != "" { - all = append(all, mac) - } - } for _, n := range vm.Status.Networks { - if n.Type == v1alpha2.NetworksTypeMain { - continue + if n.Type != v1alpha2.NetworksTypeMain && n.MAC != "" { + reservedByName[n.Name] = n.MAC + takenMacs[n.MAC] = true } - status = append(status, struct{ Name, MAC string }{n.Name, n.MAC}) - taken[n.MAC] = true } - for _, mac := range all { - if !taken[mac] { - free = append(free, mac) + + var available []string + for _, v := range vmmacs { + mac := v.Status.Address + if mac != "" && !takenMacs[mac] { + available = append(available, mac) } } - for _, n := range vm.Spec.Networks { - if n.Type == v1alpha2.NetworksTypeMain { - res = append(res, InterfaceSpec{ - Type: n.Type, - Name: n.Name, - InterfaceName: NameDefaultInterface, - MAC: "", - }) + + return &MacAddressPool{ + reservedByName: reservedByName, + available: available, + } +} + +func (p *MacAddressPool) Assign(networkName string) string { + if mac, exists := p.reservedByName[networkName]; exists { + return mac + } + + if len(p.available) > 0 { + mac := p.available[0] + p.available = p.available[1:] + return mac + } + + return "" +} + +func CreateNetworkSpec(vm *v1alpha2.VirtualMachine, vmmacs []*v1alpha2.VirtualMachineMACAddress) InterfaceSpecList { + macPool := NewMacAddressPool(vm, vmmacs) + var specs InterfaceSpecList + + for _, net := range vm.Spec.Networks { + if net.Type == v1alpha2.NetworksTypeMain { + specs = append(specs, createMainInterfaceSpec(net)) continue } - var mac string - for i, s := range status { - if s.Name == n.Name { - mac = s.MAC - status = append(status[:i], status[i+1:]...) - break - } - } - if mac == "" && freeIdx < len(free) { - mac = free[freeIdx] - freeIdx++ - } + + mac := macPool.Assign(net.Name) if mac != "" { - res = append(res, InterfaceSpec{ - Type: n.Type, - Name: n.Name, - InterfaceName: generateInterfaceName(mac, n.Type), - MAC: mac, - }) + specs = append(specs, createAdditionalInterfaceSpec(net, mac)) } } - return res + + return specs +} + +func createMainInterfaceSpec(net v1alpha2.NetworksSpec) InterfaceSpec { + return InterfaceSpec{ + ID: net.ID, + Type: net.Type, + Name: net.Name, + InterfaceName: NameDefaultInterface, + MAC: "", + } +} + +func createAdditionalInterfaceSpec(net v1alpha2.NetworksSpec, mac string) InterfaceSpec { + return InterfaceSpec{ + ID: net.ID, + Type: net.Type, + Name: net.Name, + InterfaceName: generateInterfaceName(mac, net.Type), + MAC: mac, + } } func (s InterfaceSpecList) ToString() (string, error) { @@ -157,3 +180,43 @@ func generateInterfaceName(macAddress, networkType string) string { } return name } + +const ( + ReservedMainID = 1 + StartGenericID = 2 +) + +type InterfaceIDAllocator struct { + used map[int]bool + cursor int +} + +func NewInterfaceIDAllocator() *InterfaceIDAllocator { + return &InterfaceIDAllocator{ + used: make(map[int]bool), + cursor: StartGenericID, + } +} + +func (a *InterfaceIDAllocator) Reserve(id int) { + if id > 0 { + a.used[id] = true + } +} + +func (a *InterfaceIDAllocator) NextAvailable() int { + for { + if a.cursor == ReservedMainID { + a.cursor++ + continue + } + + if !a.used[a.cursor] { + id := a.cursor + a.used[id] = true + a.cursor++ + return id + } + a.cursor++ + } +} diff --git a/images/virtualization-artifact/pkg/common/network/network_test.go b/images/virtualization-artifact/pkg/common/network/network_test.go index 7643aa0ed3..7405a340c2 100644 --- a/images/virtualization-artifact/pkg/common/network/network_test.go +++ b/images/virtualization-artifact/pkg/common/network/network_test.go @@ -78,6 +78,7 @@ var _ = Describe("Network Config Generation", func() { Expect(configs[0].Name).To(Equal("")) Expect(configs[0].InterfaceName).To(HavePrefix("default")) Expect(configs[0].MAC).To(HavePrefix("")) + Expect(configs[0].ID).To(Equal(0)) }) It("should generate correct interface name for Network type", func() { @@ -97,10 +98,12 @@ var _ = Describe("Network Config Generation", func() { Expect(configs[0].Name).To(Equal("")) Expect(configs[0].InterfaceName).To(HavePrefix("default")) Expect(configs[0].MAC).To(HavePrefix("")) + Expect(configs[0].ID).To(Equal(0)) Expect(configs[1].Type).To(Equal(v1alpha2.NetworksTypeNetwork)) Expect(configs[1].Name).To(Equal("mynet")) Expect(configs[1].InterfaceName).To(HavePrefix("veth_n")) + Expect(configs[1].ID).To(Equal(0)) }) It("should generate correct interface name for ClusterNetwork type", func() { @@ -125,30 +128,6 @@ var _ = Describe("Network Config Generation", func() { Expect(configs[1].InterfaceName).To(HavePrefix("veth_cn")) }) - It("should generate unique names for different networks", func() { - vm.Spec.Networks = []v1alpha2.NetworksSpec{ - { - Type: v1alpha2.NetworksTypeMain, - }, - { - Type: v1alpha2.NetworksTypeNetwork, - Name: "net1", - }, - { - Type: v1alpha2.NetworksTypeNetwork, - Name: "net1", - }, - } - - configs := CreateNetworkSpec(vm, vmmacs) - - Expect(configs).To(HaveLen(3)) - Expect(configs[0].Name).To(Equal("")) - Expect(configs[0].InterfaceName).To(HavePrefix("default")) - Expect(configs[0].MAC).To(HavePrefix("")) - Expect(configs[1].InterfaceName).NotTo(Equal(configs[2].InterfaceName)) - }) - It("should preserve MAC order for existing networks and assign free MAC to new network", func() { vm.Status.Networks = []v1alpha2.NetworksStatus{ { @@ -161,12 +140,12 @@ var _ = Describe("Network Config Generation", func() { }, { Type: v1alpha2.NetworksTypeNetwork, - Name: "name1", + Name: "name2", MAC: "00:1A:2B:3C:4D:5F", }, { Type: v1alpha2.NetworksTypeNetwork, - Name: "name1", + Name: "name3", MAC: "00:1A:2B:3C:4D:6A", }, } @@ -187,15 +166,15 @@ var _ = Describe("Network Config Generation", func() { }, { Type: v1alpha2.NetworksTypeNetwork, - Name: "name1", + Name: "name2", }, { Type: v1alpha2.NetworksTypeNetwork, - Name: "name2", + Name: "name4", }, { Type: v1alpha2.NetworksTypeNetwork, - Name: "name1", + Name: "name3", }, } @@ -209,13 +188,13 @@ var _ = Describe("Network Config Generation", func() { Expect(configs[1].Name).To(Equal("name1")) Expect(configs[1].MAC).To(Equal("00:1A:2B:3C:4D:5E")) - Expect(configs[2].Name).To(Equal("name1")) + Expect(configs[2].Name).To(Equal("name2")) Expect(configs[2].MAC).To(Equal("00:1A:2B:3C:4D:5F")) - Expect(configs[3].Name).To(Equal("name2")) + Expect(configs[3].Name).To(Equal("name4")) Expect(configs[3].MAC).To(Equal("00:1A:2B:3C:4D:7F")) - Expect(configs[4].Name).To(Equal("name1")) + Expect(configs[4].Name).To(Equal("name3")) Expect(configs[4].MAC).To(Equal("00:1A:2B:3C:4D:6A")) }) @@ -230,16 +209,16 @@ var _ = Describe("Network Config Generation", func() { MAC: "00:1A:2B:3C:4D:5E", }, { - Name: "name1", + Name: "name2", MAC: "00:1A:2B:3C:4D:5F", }, { Type: v1alpha2.NetworksTypeNetwork, - Name: "name2", + Name: "name3", MAC: "00:1A:2B:3C:4D:7F", }, { - Name: "name1", + Name: "name4", MAC: "00:1A:2B:3C:4D:6A", }, } @@ -260,11 +239,11 @@ var _ = Describe("Network Config Generation", func() { }, { Type: v1alpha2.NetworksTypeNetwork, - Name: "name1", + Name: "name2", }, { Type: v1alpha2.NetworksTypeNetwork, - Name: "name1", + Name: "name4", }, } @@ -275,10 +254,168 @@ var _ = Describe("Network Config Generation", func() { Expect(configs[1].Name).To(Equal("name1")) Expect(configs[1].MAC).To(Equal("00:1A:2B:3C:4D:5E")) - Expect(configs[2].Name).To(Equal("name1")) + Expect(configs[2].Name).To(Equal("name2")) Expect(configs[2].MAC).To(Equal("00:1A:2B:3C:4D:5F")) - Expect(configs[3].Name).To(Equal("name1")) + Expect(configs[3].Name).To(Equal("name4")) Expect(configs[3].MAC).To(Equal("00:1A:2B:3C:4D:6A")) }) + + It("should preserve id from spec for Main network", func() { + vm.Spec.Networks = []v1alpha2.NetworksSpec{ + { + Type: v1alpha2.NetworksTypeMain, + ID: 1, + }, + } + + configs := CreateNetworkSpec(vm, vmmacs) + + Expect(configs).To(HaveLen(1)) + Expect(configs[0].ID).To(Equal(1)) + }) + + It("should preserve id from spec for Network type with MAC", func() { + vm.Status.Networks = []v1alpha2.NetworksStatus{ + { + ID: 1, + Type: v1alpha2.NetworksTypeMain, + }, + { + ID: 5, + Type: v1alpha2.NetworksTypeNetwork, + Name: "mynet", + MAC: "00:1A:2B:3C:4D:5E", + }, + } + vmmac1 := newMACAddress("mac1", "00:1A:2B:3C:4D:5E", v1alpha2.VirtualMachineMACAddressPhaseAttached, "vm1") + vmmacs = []*v1alpha2.VirtualMachineMACAddress{vmmac1} + + vm.Spec.Networks = []v1alpha2.NetworksSpec{ + { + Type: v1alpha2.NetworksTypeMain, + ID: 1, + }, + { + Type: v1alpha2.NetworksTypeNetwork, + Name: "mynet", + ID: 5, + }, + } + + configs := CreateNetworkSpec(vm, vmmacs) + + Expect(configs).To(HaveLen(2)) + Expect(configs[0].ID).To(Equal(1)) + Expect(configs[1].ID).To(Equal(5)) + }) + + It("should preserve id from spec for ClusterNetwork type with MAC", func() { + vm.Status.Networks = []v1alpha2.NetworksStatus{ + { + Type: v1alpha2.NetworksTypeMain, + ID: 1, + }, + { + ID: 20, + Type: v1alpha2.NetworksTypeClusterNetwork, + Name: "clusternet", + MAC: "00:1A:2B:3C:4D:5E", + }, + } + vmmac1 := newMACAddress("mac1", "00:1A:2B:3C:4D:5E", v1alpha2.VirtualMachineMACAddressPhaseAttached, "vm1") + vmmacs = []*v1alpha2.VirtualMachineMACAddress{vmmac1} + + vm.Spec.Networks = []v1alpha2.NetworksSpec{ + { + Type: v1alpha2.NetworksTypeMain, + ID: 1, + }, + { + Type: v1alpha2.NetworksTypeClusterNetwork, + Name: "clusternet", + ID: 20, + }, + } + + configs := CreateNetworkSpec(vm, vmmacs) + + Expect(configs).To(HaveLen(2)) + Expect(configs[0].ID).To(Equal(1)) + Expect(configs[1].ID).To(Equal(20)) + }) + + It("should preserve different ids for multiple networks with MACs", func() { + vm.Status.Networks = []v1alpha2.NetworksStatus{ + { + Type: v1alpha2.NetworksTypeMain, + ID: 1, + }, + { + ID: 2, + Type: v1alpha2.NetworksTypeNetwork, + Name: "net1", + MAC: "00:1A:2B:3C:4D:5E", + }, + { + ID: 3, + Type: v1alpha2.NetworksTypeNetwork, + Name: "net2", + MAC: "00:1A:2B:3C:4D:5F", + }, + { + ID: 4, + Type: v1alpha2.NetworksTypeClusterNetwork, + Name: "cluster1", + MAC: "00:1A:2B:3C:4D:6A", + }, + } + vmmac1 := newMACAddress("mac1", "00:1A:2B:3C:4D:5E", v1alpha2.VirtualMachineMACAddressPhaseAttached, "vm1") + vmmac2 := newMACAddress("mac2", "00:1A:2B:3C:4D:5F", v1alpha2.VirtualMachineMACAddressPhaseAttached, "vm1") + vmmac3 := newMACAddress("mac3", "00:1A:2B:3C:4D:6A", v1alpha2.VirtualMachineMACAddressPhaseAttached, "vm1") + vmmacs = []*v1alpha2.VirtualMachineMACAddress{vmmac1, vmmac2, vmmac3} + + vm.Spec.Networks = []v1alpha2.NetworksSpec{ + { + Type: v1alpha2.NetworksTypeMain, + ID: 1, + }, + { + Type: v1alpha2.NetworksTypeNetwork, + Name: "net1", + ID: 2, + }, + { + Type: v1alpha2.NetworksTypeNetwork, + Name: "net2", + ID: 3, + }, + { + Type: v1alpha2.NetworksTypeClusterNetwork, + Name: "cluster1", + ID: 4, + }, + } + + configs := CreateNetworkSpec(vm, vmmacs) + + Expect(configs).To(HaveLen(4)) + Expect(configs[0].ID).To(Equal(1)) + Expect(configs[1].ID).To(Equal(2)) + Expect(configs[2].ID).To(Equal(3)) + Expect(configs[3].ID).To(Equal(4)) + }) + + It("should set id to zero when not specified", func() { + vm.Spec.Networks = []v1alpha2.NetworksSpec{ + { + Type: v1alpha2.NetworksTypeMain, + }, + } + + configs := CreateNetworkSpec(vm, vmmacs) + + Expect(configs).To(HaveLen(1)) + Expect(configs[0].ID).To(Equal(0)) + }) }) diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go index 606fae973b..7084be8e20 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm.go @@ -554,7 +554,7 @@ func (b *KVVM) ClearNetworkInterfaces() { b.Resource.Spec.Template.Spec.Domain.Devices.Interfaces = nil } -func (b *KVVM) SetNetworkInterface(name, macAddress string) { +func (b *KVVM) SetNetworkInterface(name, macAddress string, acpiIndex int) { net := virtv1.Network{ Name: name, NetworkSource: virtv1.NetworkSource{ @@ -571,8 +571,9 @@ func (b *KVVM) SetNetworkInterface(name, macAddress string) { devPreset := DeviceOptionsPresets.Find(b.opts.EnableParavirtualization) iface := virtv1.Interface{ - Name: name, - Model: devPreset.InterfaceModel, + Name: name, + Model: devPreset.InterfaceModel, + ACPIIndex: acpiIndex, } iface.InterfaceBindingMethod.Bridge = &virtv1.InterfaceBridge{} if macAddress != "" { diff --git a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go index 284c21eaa1..6d963e0fea 100644 --- a/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go +++ b/images/virtualization-artifact/pkg/controller/kvbuilder/kvvm_utils.go @@ -93,7 +93,6 @@ func ApplyVirtualMachineSpec( vdByName map[string]*v1alpha2.VirtualDisk, viByName map[string]*v1alpha2.VirtualImage, cviByName map[string]*v1alpha2.ClusterVirtualImage, - vmbdas map[v1alpha2.VMBDAObjectRef][]*v1alpha2.VirtualMachineBlockDeviceAttachment, class *v1alpha2.VirtualMachineClass, ipAddress string, networkSpec network.InterfaceSpecList, @@ -356,7 +355,7 @@ func ApplyMigrationVolumes(kvvm *KVVM, vm *v1alpha2.VirtualMachine, vdsByName ma func setNetwork(kvvm *KVVM, networkSpec network.InterfaceSpecList) { kvvm.ClearNetworkInterfaces() for _, n := range networkSpec { - kvvm.SetNetworkInterface(n.InterfaceName, n.MAC) + kvvm.SetNetworkInterface(n.InterfaceName, n.MAC, n.ID) } } diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/networks.go b/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/networks.go new file mode 100644 index 0000000000..e4f0a46ecb --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/networks.go @@ -0,0 +1,61 @@ +/* +Copyright 2026 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package defaulter + +import ( + "context" + + "github.com/deckhouse/virtualization-controller/pkg/common/network" + "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +type NetworksDefaulter struct{} + +func NewNetworksDefaulter() *NetworksDefaulter { + return &NetworksDefaulter{} +} + +func (d *NetworksDefaulter) Default(_ context.Context, vm *v1alpha2.VirtualMachine) error { + networks := vm.Spec.Networks + allocator := network.NewInterfaceIDAllocator() + + ensureMainNetworkID(networks) + + for _, net := range networks { + allocator.Reserve(net.ID) + } + + assignMissingIDs(networks, allocator) + return nil +} + +func ensureMainNetworkID(networks []v1alpha2.NetworksSpec) { + for i := range networks { + if networks[i].Type == v1alpha2.NetworksTypeMain && networks[i].ID == 0 { + networks[i].ID = network.ReservedMainID + return + } + } +} + +func assignMissingIDs(networks []v1alpha2.NetworksSpec, allocator *network.InterfaceIDAllocator) { + for i := range networks { + if networks[i].ID == 0 { + networks[i].ID = allocator.NextAvailable() + } + } +} diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/networks_test.go b/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/networks_test.go new file mode 100644 index 0000000000..9fb89d8607 --- /dev/null +++ b/images/virtualization-artifact/pkg/controller/vm/internal/defaulter/networks_test.go @@ -0,0 +1,216 @@ +/* +Copyright 2026 Flant JSC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package defaulter_test + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/deckhouse/virtualization-controller/pkg/common/testutil" + "github.com/deckhouse/virtualization-controller/pkg/controller/vm/internal/defaulter" + "github.com/deckhouse/virtualization/api/core/v1alpha2" +) + +var _ = Describe("NetworksDefaulter", func() { + var ( + ctx = testutil.ContextBackgroundWithNoOpLogger() + networksDefaulter *defaulter.NetworksDefaulter + ) + + BeforeEach(func() { + networksDefaulter = defaulter.NewNetworksDefaulter() + }) + + Describe("Default network IDs", func() { + It("should assign id=1 to Main network when id=0", func() { + vm := &v1alpha2.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{Name: "vm", Namespace: "default"}, + Spec: v1alpha2.VirtualMachineSpec{ + Networks: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 0}, + }, + }, + } + err := networksDefaulter.Default(ctx, vm) + Expect(err).NotTo(HaveOccurred()) + Expect(vm.Spec.Networks).To(HaveLen(1)) + Expect(vm.Spec.Networks[0].Type).To(Equal(v1alpha2.NetworksTypeMain)) + Expect(vm.Spec.Networks[0].ID).To(Equal(1)) + }) + + It("should not change Main network id when it is already set to 1", func() { + vm := &v1alpha2.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{Name: "vm", Namespace: "default"}, + Spec: v1alpha2.VirtualMachineSpec{ + Networks: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 1}, + }, + }, + } + err := networksDefaulter.Default(ctx, vm) + Expect(err).NotTo(HaveOccurred()) + Expect(vm.Spec.Networks).To(HaveLen(1)) + Expect(vm.Spec.Networks[0].Type).To(Equal(v1alpha2.NetworksTypeMain)) + Expect(vm.Spec.Networks[0].ID).To(Equal(1)) + }) + + It("should assign sequential ids starting from 2 to networks with id=0", func() { + vm := &v1alpha2.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{Name: "vm", Namespace: "default"}, + Spec: v1alpha2.VirtualMachineSpec{ + Networks: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 0}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test-network-1", ID: 0}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test-network-2", ID: 0}, + }, + }, + } + err := networksDefaulter.Default(ctx, vm) + Expect(err).NotTo(HaveOccurred()) + Expect(vm.Spec.Networks).To(HaveLen(3)) + Expect(vm.Spec.Networks[0].Type).To(Equal(v1alpha2.NetworksTypeMain)) + Expect(vm.Spec.Networks[0].ID).To(Equal(1)) + Expect(vm.Spec.Networks[1].Type).To(Equal(v1alpha2.NetworksTypeNetwork)) + Expect(vm.Spec.Networks[1].Name).To(Equal("test-network-1")) + Expect(vm.Spec.Networks[1].ID).To(Equal(2)) + Expect(vm.Spec.Networks[2].Type).To(Equal(v1alpha2.NetworksTypeNetwork)) + Expect(vm.Spec.Networks[2].Name).To(Equal("test-network-2")) + Expect(vm.Spec.Networks[2].ID).To(Equal(3)) + }) + + It("should not change network id when it is already set", func() { + vm := &v1alpha2.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{Name: "vm", Namespace: "default"}, + Spec: v1alpha2.VirtualMachineSpec{ + Networks: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 1}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test-network", ID: 5}, + }, + }, + } + err := networksDefaulter.Default(ctx, vm) + Expect(err).NotTo(HaveOccurred()) + Expect(vm.Spec.Networks).To(HaveLen(2)) + Expect(vm.Spec.Networks[0].Type).To(Equal(v1alpha2.NetworksTypeMain)) + Expect(vm.Spec.Networks[0].ID).To(Equal(1)) + Expect(vm.Spec.Networks[1].Type).To(Equal(v1alpha2.NetworksTypeNetwork)) + Expect(vm.Spec.Networks[1].ID).To(Equal(5)) + }) + + It("should assign sequential ids considering already set ids", func() { + vm := &v1alpha2.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{Name: "vm", Namespace: "default"}, + Spec: v1alpha2.VirtualMachineSpec{ + Networks: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 0}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test-network-1", ID: 5}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test-network-2", ID: 0}, + }, + }, + } + err := networksDefaulter.Default(ctx, vm) + Expect(err).NotTo(HaveOccurred()) + Expect(vm.Spec.Networks).To(HaveLen(3)) + Expect(vm.Spec.Networks[0].Type).To(Equal(v1alpha2.NetworksTypeMain)) + Expect(vm.Spec.Networks[0].ID).To(Equal(1)) + Expect(vm.Spec.Networks[1].Type).To(Equal(v1alpha2.NetworksTypeNetwork)) + Expect(vm.Spec.Networks[1].Name).To(Equal("test-network-1")) + Expect(vm.Spec.Networks[1].ID).To(Equal(5)) + Expect(vm.Spec.Networks[2].Type).To(Equal(v1alpha2.NetworksTypeNetwork)) + Expect(vm.Spec.Networks[2].Name).To(Equal("test-network-2")) + Expect(vm.Spec.Networks[2].ID).To(Equal(2)) + }) + + It("should handle ClusterNetwork type correctly", func() { + vm := &v1alpha2.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{Name: "vm", Namespace: "default"}, + Spec: v1alpha2.VirtualMachineSpec{ + Networks: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 0}, + {Type: v1alpha2.NetworksTypeClusterNetwork, Name: "test-cluster-network", ID: 0}, + }, + }, + } + err := networksDefaulter.Default(ctx, vm) + Expect(err).NotTo(HaveOccurred()) + Expect(vm.Spec.Networks).To(HaveLen(2)) + Expect(vm.Spec.Networks[0].Type).To(Equal(v1alpha2.NetworksTypeMain)) + Expect(vm.Spec.Networks[0].ID).To(Equal(1)) + Expect(vm.Spec.Networks[1].Type).To(Equal(v1alpha2.NetworksTypeClusterNetwork)) + Expect(vm.Spec.Networks[1].Name).To(Equal("test-cluster-network")) + Expect(vm.Spec.Networks[1].ID).To(Equal(2)) + }) + + It("should skip id=1 when assigning to non-Main networks", func() { + vm := &v1alpha2.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{Name: "vm", Namespace: "default"}, + Spec: v1alpha2.VirtualMachineSpec{ + Networks: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 1}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test-network", ID: 0}, + }, + }, + } + err := networksDefaulter.Default(ctx, vm) + Expect(err).NotTo(HaveOccurred()) + Expect(vm.Spec.Networks).To(HaveLen(2)) + Expect(vm.Spec.Networks[0].Type).To(Equal(v1alpha2.NetworksTypeMain)) + Expect(vm.Spec.Networks[0].ID).To(Equal(1)) + Expect(vm.Spec.Networks[1].Type).To(Equal(v1alpha2.NetworksTypeNetwork)) + Expect(vm.Spec.Networks[1].ID).To(Equal(2)) + }) + + It("should assign sequential ids starting from 2 when there is no Main network", func() { + vm := &v1alpha2.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{Name: "vm", Namespace: "default"}, + Spec: v1alpha2.VirtualMachineSpec{ + Networks: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeNetwork, Name: "test-network-1", ID: 0}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test-network-2", ID: 0}, + }, + }, + } + err := networksDefaulter.Default(ctx, vm) + Expect(err).NotTo(HaveOccurred()) + Expect(vm.Spec.Networks).To(HaveLen(2)) + Expect(vm.Spec.Networks[0].Type).To(Equal(v1alpha2.NetworksTypeNetwork)) + Expect(vm.Spec.Networks[0].Name).To(Equal("test-network-1")) + Expect(vm.Spec.Networks[0].ID).To(Equal(2)) + Expect(vm.Spec.Networks[1].Type).To(Equal(v1alpha2.NetworksTypeNetwork)) + Expect(vm.Spec.Networks[1].Name).To(Equal("test-network-2")) + Expect(vm.Spec.Networks[1].ID).To(Equal(3)) + }) + + It("should handle only ClusterNetwork without Main network", func() { + vm := &v1alpha2.VirtualMachine{ + ObjectMeta: metav1.ObjectMeta{Name: "vm", Namespace: "default"}, + Spec: v1alpha2.VirtualMachineSpec{ + Networks: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeClusterNetwork, Name: "test-cluster-network", ID: 0}, + }, + }, + } + err := networksDefaulter.Default(ctx, vm) + Expect(err).NotTo(HaveOccurred()) + Expect(vm.Spec.Networks).To(HaveLen(1)) + Expect(vm.Spec.Networks[0].Type).To(Equal(v1alpha2.NetworksTypeClusterNetwork)) + Expect(vm.Spec.Networks[0].Name).To(Equal("test-cluster-network")) + Expect(vm.Spec.Networks[0].ID).To(Equal(2)) + }) + }) +}) diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/network.go b/images/virtualization-artifact/pkg/controller/vm/internal/network.go index 51da70ea0c..d2d3c3ae9c 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/network.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/network.go @@ -118,6 +118,7 @@ func (h *NetworkInterfaceHandler) UpdateNetworkStatus(ctx context.Context, s sta if hasOnlyDefaultNetwork(vm) { vm.Status.Networks = []v1alpha2.NetworksStatus{ { + ID: network.ReservedMainID, Type: v1alpha2.NetworksTypeMain, Name: network.NameDefaultInterface, }, @@ -153,6 +154,7 @@ func (h *NetworkInterfaceHandler) UpdateNetworkStatus(ctx context.Context, s sta for _, interfaceSpec := range network.CreateNetworkSpec(vm, vmmacs) { if interfaceSpec.Type == v1alpha2.NetworksTypeMain { networksStatus = append(networksStatus, v1alpha2.NetworksStatus{ + ID: interfaceSpec.ID, Type: v1alpha2.NetworksTypeMain, Name: network.NameDefaultInterface, }) @@ -160,6 +162,7 @@ func (h *NetworkInterfaceHandler) UpdateNetworkStatus(ctx context.Context, s sta } networksStatus = append(networksStatus, v1alpha2.NetworksStatus{ + ID: interfaceSpec.ID, Type: interfaceSpec.Type, Name: interfaceSpec.Name, MAC: macAddressesByInterfaceName[interfaceSpec.InterfaceName], diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go index 370ee737a6..530551a938 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/sync_kvvm.go @@ -434,13 +434,8 @@ func MakeKVVMFromVMSpec(ctx context.Context, s state.VirtualMachineState) (*virt networkSpec := network.CreateNetworkSpec(current, vmmacs) - vmbdas, err := s.VirtualMachineBlockDeviceAttachments(ctx) - if err != nil { - return nil, fmt.Errorf("get vmbdas: %w", err) - } - // Create kubevirt VirtualMachine resource from d8 VirtualMachine spec. - err = kvbuilder.ApplyVirtualMachineSpec(kvvmBuilder, current, bdState.VDByName, bdState.VIByName, bdState.CVIByName, vmbdas, class, ipAddress, networkSpec) + err = kvbuilder.ApplyVirtualMachineSpec(kvvmBuilder, current, bdState.VDByName, bdState.VIByName, bdState.CVIByName, class, ipAddress, networkSpec) if err != nil { return nil, err } diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/validators/networks_validator.go b/images/virtualization-artifact/pkg/controller/vm/internal/validators/networks_validator.go index 8733c5fafa..3f3cb9e54e 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/validators/networks_validator.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/validators/networks_validator.go @@ -28,6 +28,10 @@ import ( "github.com/deckhouse/virtualization/api/core/v1alpha2" ) +const ( + maxNetworkID = 16*1024 - 1 // 16383 +) + type NetworksValidator struct { featureGate featuregate.FeatureGate } @@ -61,6 +65,10 @@ func (v *NetworksValidator) ValidateUpdate(_ context.Context, oldVM, newVM *v1al return nil, fmt.Errorf("network configuration requires SDN to be enabled") } + if err := v.validateNetworkIDsUnchanged(oldVM.Spec.Networks, newNetworksSpec); err != nil { + return nil, err + } + isChanged := !equality.Semantic.DeepEqual(newNetworksSpec, oldVM.Spec.Networks) if isChanged { return v.validateNetworksSpec(newNetworksSpec) @@ -74,6 +82,7 @@ func isSingleMainNet(networks []v1alpha2.NetworksSpec) bool { func (v *NetworksValidator) validateNetworksSpec(networksSpec []v1alpha2.NetworksSpec) (admission.Warnings, error) { namesSet := make(map[string]struct{}) + idsSet := make(map[int]struct{}) for i, network := range networksSpec { typ := network.Type name := network.Name @@ -89,6 +98,14 @@ func (v *NetworksValidator) validateNetworksSpec(networksSpec []v1alpha2.Network if err := v.validateNetworkUniqueness(typ, name, namesSet); err != nil { return nil, err } + + if err := v.validateNetworkIDUniqueness(network, idsSet); err != nil { + return nil, err + } + + if err := v.validateNetworkID(network); err != nil { + return nil, err + } } return nil, nil @@ -120,3 +137,65 @@ func (v *NetworksValidator) validateNetworkUniqueness(networkType, networkName s namesSet[key] = struct{}{} return nil } + +func (v *NetworksValidator) validateNetworkIDUniqueness(network v1alpha2.NetworksSpec, idsSet map[int]struct{}) error { + if network.ID == 0 { + return nil + } + if _, exists := idsSet[network.ID]; exists { + networkIdentifier := v.getNetworkIdentifier(network) + return fmt.Errorf("network id %d is duplicated for network %s", network.ID, networkIdentifier) + } + idsSet[network.ID] = struct{}{} + return nil +} + +func (v *NetworksValidator) validateNetworkIDsUnchanged(oldNetworksSpec, newNetworksSpec []v1alpha2.NetworksSpec) error { + oldNetworksMap := v.buildNetworksMap(oldNetworksSpec) + newNetworksMap := v.buildNetworksMap(newNetworksSpec) + + for key, oldNetwork := range oldNetworksMap { + newNetwork, exists := newNetworksMap[key] + if !exists { + continue + } + + if oldNetwork.ID == newNetwork.ID { + continue + } + + networkIdentifier := v.getNetworkIdentifier(oldNetwork) + return fmt.Errorf("network id cannot be changed for network %s", networkIdentifier) + } + + return nil +} + +func (v *NetworksValidator) buildNetworksMap(networksSpec []v1alpha2.NetworksSpec) map[string]v1alpha2.NetworksSpec { + networksMap := make(map[string]v1alpha2.NetworksSpec) + for _, network := range networksSpec { + key := v.getNetworkIdentifier(network) + networksMap[key] = network + } + return networksMap +} + +func (v *NetworksValidator) validateNetworkID(network v1alpha2.NetworksSpec) error { + if network.ID == 0 { + return nil + } + + if network.ID < 1 || network.ID > maxNetworkID { + networkIdentifier := v.getNetworkIdentifier(network) + return fmt.Errorf("network id must be between 1 and %d for network %s, got %d", maxNetworkID, networkIdentifier, network.ID) + } + + return nil +} + +func (v *NetworksValidator) getNetworkIdentifier(network v1alpha2.NetworksSpec) string { + if network.Type == v1alpha2.NetworksTypeMain { + return network.Type + } + return fmt.Sprintf("%s/%s", network.Type, network.Name) +} diff --git a/images/virtualization-artifact/pkg/controller/vm/internal/validators/networks_validator_test.go b/images/virtualization-artifact/pkg/controller/vm/internal/validators/networks_validator_test.go index bd4db04d3c..f3d73ac896 100644 --- a/images/virtualization-artifact/pkg/controller/vm/internal/validators/networks_validator_test.go +++ b/images/virtualization-artifact/pkg/controller/vm/internal/validators/networks_validator_test.go @@ -47,6 +47,28 @@ func TestNetworksValidateCreate(t *testing.T) { {[]v1alpha2.NetworksSpec{mainNetwork, networkTest, networkTest}, true, false}, {[]v1alpha2.NetworksSpec{mainNetwork, {Type: v1alpha2.NetworksTypeNetwork}}, true, false}, {[]v1alpha2.NetworksSpec{mainNetwork}, false, true}, + {[]v1alpha2.NetworksSpec{{Type: v1alpha2.NetworksTypeMain, ID: 1}}, true, true}, + {[]v1alpha2.NetworksSpec{{Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 2}}, true, true}, + {[]v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 1}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test1", ID: 2}, + {Type: v1alpha2.NetworksTypeClusterNetwork, Name: "test2", ID: 3}, + }, true, true}, + {[]v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 1}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test1", ID: 1}, + {Type: v1alpha2.NetworksTypeClusterNetwork, Name: "test2", ID: 2}, + }, true, false}, + {[]v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeNetwork, Name: "a", ID: 2}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "b", ID: 2}, + }, true, false}, + {[]v1alpha2.NetworksSpec{{Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 16383}}, true, true}, + {[]v1alpha2.NetworksSpec{{Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 0}}, true, true}, + {[]v1alpha2.NetworksSpec{{Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 16384}}, true, false}, + {[]v1alpha2.NetworksSpec{{Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: -1}}, true, false}, + {[]v1alpha2.NetworksSpec{{Type: v1alpha2.NetworksTypeMain, ID: 16383}}, true, true}, + {[]v1alpha2.NetworksSpec{{Type: v1alpha2.NetworksTypeMain, ID: 16384}}, true, false}, } for i, test := range tests { @@ -62,10 +84,10 @@ func TestNetworksValidateCreate(t *testing.T) { _, err := networkValidator.ValidateCreate(t.Context(), vm) if test.valid && err != nil { - t.Errorf("Validation failed for spec %s: expected valid, but got an error: %v", test.networks, err) + t.Errorf("Validation failed for spec %v: expected valid, but got an error: %v", test.networks, err) } if !test.valid && err == nil { - t.Errorf("Validation succeeded for spec %s: expected error, but got none", test.networks) + t.Errorf("Validation succeeded for spec %v: expected error, but got none", test.networks) } }) } @@ -160,6 +182,104 @@ func TestNetworksValidateUpdate(t *testing.T) { sdnEnabled: true, valid: true, }, + { + oldNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 1}, + }, + newNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 2}, + }, + sdnEnabled: true, + valid: false, + }, + { + oldNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 1}, + }, + newNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 2}, + }, + sdnEnabled: true, + valid: false, + }, + { + oldNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeClusterNetwork, Name: "cluster", ID: 5}, + }, + newNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeClusterNetwork, Name: "cluster", ID: 10}, + }, + sdnEnabled: true, + valid: false, + }, + { + oldNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 1}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 2}, + }, + newNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 1}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 2}, + }, + sdnEnabled: true, + valid: true, + }, + { + oldNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 0}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test1", ID: 1}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test2", ID: 2}, + }, + newNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 0}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test1", ID: 1}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test2", ID: 3}, + }, + sdnEnabled: true, + valid: false, + }, + { + oldNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 0}, + }, + newNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 0}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "new", ID: 5}, + }, + sdnEnabled: true, + valid: true, + }, + { + oldNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 0}, + {Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 1}, + }, + newNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeMain, ID: 0}, + }, + sdnEnabled: true, + valid: true, + }, + { + oldNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 0}, + }, + newNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 1}, + }, + sdnEnabled: true, + valid: false, + }, + { + oldNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 1}, + }, + newNetworksSpec: []v1alpha2.NetworksSpec{ + {Type: v1alpha2.NetworksTypeNetwork, Name: "test", ID: 0}, + }, + sdnEnabled: true, + valid: false, + }, } for i, test := range tests { diff --git a/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go b/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go index cb56986437..40ee16dba8 100644 --- a/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go +++ b/images/virtualization-artifact/pkg/controller/vm/vm_webhook.go @@ -137,6 +137,7 @@ func NewDefaulter(client client.Client, vmClassService *service.VirtualMachineCl defaulters: []VirtualMachineDefaulter{ defaulter.NewVirtualMachineClassNameDefaulter(client, vmClassService), defaulter.NewCoreFractionDefaulter(client), + defaulter.NewNetworksDefaulter(), }, log: log.With("webhook", "mutating"), } diff --git a/test/e2e/internal/util/sdn.go b/test/e2e/internal/util/sdn.go index 74dbd39f24..66b149dbf2 100644 --- a/test/e2e/internal/util/sdn.go +++ b/test/e2e/internal/util/sdn.go @@ -18,6 +18,7 @@ package util import ( "context" + "fmt" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -28,14 +29,18 @@ import ( "github.com/deckhouse/virtualization/test/e2e/internal/framework" ) -const ( - ClusterNetworkName = "cn-1003-for-e2e-test" - ClusterNetworkVLANID = 1003 - ClusterNetworkCreateCommand = `kubectl apply -f - <