Skip to content

Commit 8cf4c3c

Browse files
authored
Merge pull request #3120 from larainema/fix/port-trusted-vif-extension
🐛 Use port_trusted_vif extension for trusted VF when available
2 parents dd3bc79 + 89ad437 commit 8cf4c3c

2 files changed

Lines changed: 91 additions & 3 deletions

File tree

pkg/cloud/services/networking/port.go

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/gophercloud/gophercloud/v2"
2828
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsbinding"
2929
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsecurity"
30+
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portstrustedvif"
3031
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/ports"
3132
"k8s.io/apimachinery/pkg/runtime"
3233
"k8s.io/apimachinery/pkg/util/wait"
@@ -240,14 +241,33 @@ func (s *Service) EnsurePort(eventObject runtime.Object, portSpec *infrav1.Resol
240241
builder = portSecurityOpts
241242
}
242243

244+
// Determine if port_trusted_vif extension is available when TrustedVF is requested.
245+
// If available, we use the dedicated port attribute instead of binding:profile.
246+
var usePortTrustedVIF bool
247+
if portSpec.Profile != nil && ptr.Deref(portSpec.Profile.TrustedVF, false) {
248+
usePortTrustedVIF, err = s.HasPortTrustedVIFExtension()
249+
if err != nil {
250+
return nil, err
251+
}
252+
}
253+
243254
portsBindingOpts := portsbinding.CreateOptsExt{
244255
CreateOptsBuilder: builder,
245256
HostID: ptr.Deref(portSpec.HostID, ""),
246257
VNICType: ptr.Deref(portSpec.VNICType, ""),
247-
Profile: getPortProfile(portSpec.Profile),
258+
Profile: getPortProfile(portSpec.Profile, usePortTrustedVIF),
248259
}
249260
builder = portsBindingOpts
250261

262+
// If the port_trusted_vif extension is available, set trusted mode via the
263+
// dedicated port attribute rather than through binding:profile.
264+
if usePortTrustedVIF {
265+
builder = portstrustedvif.PortCreateOptsExt{
266+
CreateOptsBuilder: builder,
267+
PortTrustedVIF: portSpec.Profile.TrustedVF,
268+
}
269+
}
270+
251271
port, err := s.client.CreatePort(builder)
252272
if err != nil {
253273
record.Warnf(eventObject, "FailedCreatePort", "Failed to create port %s: %v", portSpec.Name, err)
@@ -262,7 +282,7 @@ func (s *Service) EnsurePort(eventObject runtime.Object, portSpec *infrav1.Resol
262282
return port, nil
263283
}
264284

265-
func getPortProfile(p *infrav1.BindingProfile) map[string]interface{} {
285+
func getPortProfile(p *infrav1.BindingProfile, usePortTrustedVIF bool) map[string]interface{} {
266286
if p == nil {
267287
return nil
268288
}
@@ -274,7 +294,10 @@ func getPortProfile(p *infrav1.BindingProfile) map[string]interface{} {
274294
if ptr.Deref(p.OVSHWOffload, false) {
275295
portProfile["capabilities"] = []string{"switchdev"}
276296
}
277-
if ptr.Deref(p.TrustedVF, false) {
297+
// Only set trusted in binding:profile if the port_trusted_vif extension
298+
// is not available. When the extension is available, trusted mode is set
299+
// via the dedicated port attribute instead.
300+
if !usePortTrustedVIF && ptr.Deref(p.TrustedVF, false) {
278301
portProfile["trusted"] = true
279302
}
280303

@@ -607,6 +630,24 @@ func (s *Service) IsTrunkExtSupported() (trunknSupported bool, err error) {
607630
return true, nil
608631
}
609632

633+
// HasPortTrustedVIFExtension checks whether the Neutron port_trusted_vif
634+
// extension is available. When this extension is present, trusted VF mode
635+
// should be set via the dedicated port attribute rather than through
636+
// binding:profile.
637+
func (s *Service) HasPortTrustedVIFExtension() (bool, error) {
638+
allExts, err := s.client.ListExtensions()
639+
if err != nil {
640+
return false, err
641+
}
642+
643+
for _, ext := range allExts {
644+
if ext.Alias == "port-trusted-vif" {
645+
return true, nil
646+
}
647+
}
648+
return false, nil
649+
}
650+
610651
// AdoptPortsServer looks for ports in desiredPorts which were previously created, and adds them to resources.Ports.
611652
// A port matches if it has the same name and network ID as the desired port.
612653
// TODO(emilien): remove this function: https://github.com/kubernetes-sigs/cluster-api-provider-openstack/pull/2071

pkg/cloud/services/networking/port_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/attributestags"
2626
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsbinding"
2727
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portsecurity"
28+
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/portstrustedvif"
2829
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/security/groups"
2930
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/trunks"
3031
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/networks"
@@ -161,6 +162,8 @@ func Test_EnsurePort(t *testing.T) {
161162
Name: "foo-port-1",
162163
NetworkID: netID,
163164
}).Return(nil, nil)
165+
// Extension not available, so trusted goes into binding:profile
166+
m.ListExtensions().Return([]extensions.Extension{}, nil)
164167
// The following allows us to use gomega to
165168
// compare the argument instead of gomock.
166169
// Gomock's output in the case of a mismatch is
@@ -173,6 +176,50 @@ func Test_EnsurePort(t *testing.T) {
173176
},
174177
want: &ports.Port{ID: portID},
175178
},
179+
{
180+
name: "uses port_trusted_vif extension when available instead of binding:profile",
181+
port: infrav1.ResolvedPortSpec{
182+
Name: "test-port",
183+
NetworkID: netID,
184+
ResolvedPortSpecFields: infrav1.ResolvedPortSpecFields{
185+
VNICType: ptr.To("direct"),
186+
Profile: &infrav1.BindingProfile{
187+
TrustedVF: ptr.To(true),
188+
},
189+
},
190+
},
191+
expect: func(m *mock.MockNetworkClientMockRecorder, g Gomega) {
192+
var expectedCreateOpts ports.CreateOptsBuilder
193+
expectedCreateOpts = ports.CreateOpts{
194+
Name: "test-port",
195+
NetworkID: netID,
196+
}
197+
// When extension is available, trusted is NOT in binding:profile
198+
expectedCreateOpts = portsbinding.CreateOptsExt{
199+
CreateOptsBuilder: expectedCreateOpts,
200+
VNICType: "direct",
201+
}
202+
expectedCreateOpts = portstrustedvif.PortCreateOptsExt{
203+
CreateOptsBuilder: expectedCreateOpts,
204+
PortTrustedVIF: ptr.To(true),
205+
}
206+
207+
m.ListPort(ports.ListOpts{
208+
Name: "test-port",
209+
NetworkID: netID,
210+
}).Return(nil, nil)
211+
// Extension is available
212+
trustedVIFExt := extensions.Extension{}
213+
trustedVIFExt.Alias = "port-trusted-vif"
214+
m.ListExtensions().Return([]extensions.Extension{trustedVIFExt}, nil)
215+
m.CreatePort(gomock.Any()).DoAndReturn(func(builder ports.CreateOptsBuilder) (*ports.Port, error) {
216+
gotCreateOpts := builder.(portstrustedvif.PortCreateOptsExt)
217+
g.Expect(gotCreateOpts).To(Equal(expectedCreateOpts), cmp.Diff(gotCreateOpts, expectedCreateOpts))
218+
return &ports.Port{ID: portID}, nil
219+
})
220+
},
221+
want: &ports.Port{ID: portID},
222+
},
176223
{
177224
name: "creates minimum port correctly",
178225
port: infrav1.ResolvedPortSpec{

0 commit comments

Comments
 (0)